diff --git a/app/javascript/flavours/glitch/features/ui/components/columns_area.jsx b/app/javascript/flavours/glitch/features/ui/components/columns_area.jsx index e4c67ed65e..540a0d5f7b 100644 --- a/app/javascript/flavours/glitch/features/ui/components/columns_area.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/columns_area.jsx @@ -27,8 +27,10 @@ import BundleColumnError from './bundle_column_error'; import { ColumnLoading } from './column_loading'; import ComposePanel from './compose_panel'; import DrawerLoading from './drawer_loading'; +import HeaderNavigation from './header_navigation'; import NavigationPanel from './navigation_panel'; + const componentMap = { 'COMPOSE': Compose, 'HOME': HomeTimeline, @@ -151,21 +153,27 @@ export default class ColumnsArea extends ImmutablePureComponent { if (singleColumn) { return ( -
-
-
- {renderComposePanel && } +
+
+ +
+ +
+
+
+ {renderComposePanel && } +
-
-
-
-
{children}
-
+
+
+
{children}
+
-
-
- +
+
+ +
diff --git a/app/javascript/flavours/glitch/features/ui/components/header.jsx b/app/javascript/flavours/glitch/features/ui/components/header.jsx index f102912faa..01885a7847 100644 --- a/app/javascript/flavours/glitch/features/ui/components/header.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/header.jsx @@ -12,7 +12,6 @@ import { openModal } from 'flavours/glitch/actions/modal'; import { fetchServer } from 'flavours/glitch/actions/server'; import { Avatar } from 'flavours/glitch/components/avatar'; import { Icon } from 'flavours/glitch/components/icon'; -import { WordmarkLogo, SymbolLogo } from 'flavours/glitch/components/logo'; import { Permalink } from 'flavours/glitch/components/permalink'; import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { registrationsOpen, me, sso_redirect } from 'flavours/glitch/initial_state'; @@ -66,6 +65,7 @@ class Header extends PureComponent { if (signedIn) { content = ( <> + {location.pathname !== '/search' && } {location.pathname !== '/publish' && } @@ -105,11 +105,6 @@ class Header extends PureComponent { return (
- - - - -
{content}
diff --git a/app/javascript/flavours/glitch/features/ui/components/header_nav_link.jsx b/app/javascript/flavours/glitch/features/ui/components/header_nav_link.jsx new file mode 100644 index 0000000000..daa6ebdf9d --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/components/header_nav_link.jsx @@ -0,0 +1,60 @@ +import PropTypes from 'prop-types'; + +import classNames from 'classnames'; +import { useRouteMatch, NavLink } from 'react-router-dom'; + +import { Icon } from 'flavours/glitch/components/icon'; + +const HeaderNavLink = ({ icon, activeIcon, iconComponent, activeIconComponent, text, to, onClick, href, method, badge, transparent, ...other }) => { + const match = useRouteMatch(to); + const className = classNames('header-nav-link', { 'header-nav-link--transparent': transparent }); + const badgeElement = typeof badge !== 'undefined' ? {badge} : null; + const iconElement = (typeof icon === 'string' || iconComponent) ? : icon; + const activeIconElement = activeIcon ?? (activeIconComponent ? : iconElement); + const active = match?.isExact; + + if (href) { + return ( + + {active ? activeIconElement : iconElement} + {badgeElement} + + ); + } else if (to) { + return ( + + {active ? activeIconElement : iconElement} + {badgeElement} + + ); + } else { + const handleOnClick = (e) => { + e.preventDefault(); + e.stopPropagation(); + return onClick(e); + }; + return ( + // eslint-disable-next-line jsx-a11y/anchor-is-valid -- intentional to have the same look and feel as other menu items + + {iconElement} + {badgeElement} + + ); + } +}; + +HeaderNavLink.propTypes = { + icon: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired, + iconComponent: PropTypes.func, + activeIcon: PropTypes.node, + activeIconComponent: PropTypes.func, + text: PropTypes.string.isRequired, + to: PropTypes.string, + onClick: PropTypes.func, + href: PropTypes.string, + method: PropTypes.string, + badge: PropTypes.node, + transparent: PropTypes.bool, +}; + +export default HeaderNavLink; diff --git a/app/javascript/flavours/glitch/features/ui/components/header_navigation.jsx b/app/javascript/flavours/glitch/features/ui/components/header_navigation.jsx new file mode 100644 index 0000000000..e9d9c47cd7 --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/components/header_navigation.jsx @@ -0,0 +1,271 @@ +import PropTypes from 'prop-types'; +import { Component, useEffect } from 'react'; + +import { defineMessages, injectIntl, useIntl , FormattedMessage } from 'react-intl'; + +import { useSelector, useDispatch, connect } from 'react-redux'; + + +import BookmarksActiveIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react'; +import BookmarksIcon from '@/material-icons/400-24px/bookmarks.svg?react'; +import ModerationIcon from '@/material-icons/400-24px/gavel.svg?react'; +import HomeActiveIcon from '@/material-icons/400-24px/home-fill.svg?react'; +import HomeIcon from '@/material-icons/400-24px/home.svg?react'; +import ListAltActiveIcon from '@/material-icons/400-24px/list_alt-fill.svg?react'; +import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react'; +import MailActiveIcon from '@/material-icons/400-24px/mail-fill.svg?react'; +import MailIcon from '@/material-icons/400-24px/mail.svg?react'; +import AdministrationIcon from '@/material-icons/400-24px/manufacturing.svg?react'; +import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react'; +import NotificationsActiveIcon from '@/material-icons/400-24px/notifications-fill.svg?react'; +import NotificationsIcon from '@/material-icons/400-24px/notifications.svg?react'; +import PersonAddActiveIcon from '@/material-icons/400-24px/person_add-fill.svg?react'; +import PersonAddIcon from '@/material-icons/400-24px/person_add.svg?react'; +import PublicIcon from '@/material-icons/400-24px/public.svg?react'; +import SearchIcon from '@/material-icons/400-24px/search.svg?react'; +import SettingsIcon from '@/material-icons/400-24px/settings.svg?react'; +import StarActiveIcon from '@/material-icons/400-24px/star-fill.svg?react'; +import StarIcon from '@/material-icons/400-24px/star.svg?react'; +import { fetchFollowRequests } from 'flavours/glitch/actions/accounts'; +import { Avatar } from 'flavours/glitch/components/avatar'; +import { CollapseButton } from 'flavours/glitch/components/collapse_button'; +import { IconWithBadge } from 'flavours/glitch/components/icon_with_badge'; +import { Permalink } from 'flavours/glitch/components/permalink'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; +import { timelinePreview, registrationsOpen, me, sso_redirect } from 'flavours/glitch/initial_state'; +import { transientSingleColumn } from 'flavours/glitch/is_mobile'; +import { canManageReports, canViewAdminDashboard } from 'flavours/glitch/permissions'; +import { selectUnreadNotificationGroupsCount } from 'flavours/glitch/selectors/notifications'; +import { selectUseGroupedNotifications } from 'flavours/glitch/selectors/settings'; +import { preferencesLink } from 'flavours/glitch/utils/backend_links'; + +import DisabledAccountBanner from './disabled_account_banner'; +import HeaderNavLink from './header_nav_link'; +import { ListPanel } from './list_panel'; +import SignInBanner from './sign_in_banner'; + +const messages = defineMessages({ + home: { id: 'tabs_bar.home', defaultMessage: 'Home' }, + notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' }, + explore: { id: 'explore.title', defaultMessage: 'Explore' }, + firehose: { id: 'column.firehose', defaultMessage: 'Live feeds' }, + direct: { id: 'navigation_bar.direct', defaultMessage: 'Private mentions' }, + favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favorites' }, + bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' }, + lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' }, + preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' }, + administration: { id: 'navigation_bar.administration', defaultMessage: 'Administration' }, + moderation: { id: 'navigation_bar.moderation', defaultMessage: 'Moderation' }, + followsAndFollowers: { id: 'navigation_bar.follows_and_followers', defaultMessage: 'Follows and followers' }, + about: { id: 'navigation_bar.about', defaultMessage: 'About' }, + search: { id: 'navigation_bar.search', defaultMessage: 'Search' }, + advancedInterface: { id: 'navigation_bar.advanced_interface', defaultMessage: 'Open in advanced web interface' }, + openedInClassicInterface: { id: 'navigation_bar.opened_in_classic_interface', defaultMessage: 'Posts, accounts, and other specific pages are opened by default in the classic web interface.' }, + app_settings: { id: 'navigation_bar.app_settings', defaultMessage: 'App settings' }, + followRequests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' }, +}); + +const NotificationsLink = () => { + const optedInGroupedNotifications = useSelector(selectUseGroupedNotifications); + const count = useSelector(state => state.getIn(['local_settings', 'notifications', 'tab_badge']) ? state.getIn(['notifications', 'unread']) : 0); + const intl = useIntl(); + + const newCount = useSelector(selectUnreadNotificationGroupsCount); + + return ( + } + activeIcon={} + text={intl.formatMessage(messages.notifications)} + /> + ); +}; + +const FollowRequestsLink = () => { + const count = useSelector(state => state.getIn(['user_lists', 'follow_requests', 'items'])?.size ?? 0); + const intl = useIntl(); + const dispatch = useDispatch(); + + useEffect(() => { + dispatch(fetchFollowRequests()); + }, [dispatch]); + + if (count === 0) { + return null; + } + + return ( + } + activeIcon={} + text={intl.formatMessage(messages.followRequests)} + /> + ); +}; + +const Account = connect(state => ({ + account: state.getIn(['accounts', me]), +}))(({ account }) => ( + + + +)); + + +class HeaderNavigation extends Component { + static propTypes = { + identity: identityContextPropShape, + intl: PropTypes.object.isRequired, + onOpenSettings: PropTypes.func, + location: PropTypes.object, + openClosedRegistrationsModal: PropTypes.func, + signupUrl: PropTypes.string.isRequired, + }; + + isFirehoseActive = (match, location) => { + return match || location.pathname.startsWith('/public'); + }; + + state = { + collapsed: true, + animating: false + }; + + handleToggleClick = (e) => { + e.stopPropagation(); + this.setState(function(state) { + return { + collapsed: !state.collapsed, + animating: true + }; + }); + }; + + handleTransitionEnd = () => { + this.setState({ + animating: false + }); + }; + + render () { + const { intl, onOpenSettings, location, openClosedRegistrationsModal, signupUrl } = this.props; + const { signedIn, disabledAccountId, permissions } = this.props.identity; + + let banner = undefined; + + let content; + + if (signedIn) { + content = ( + + ); + } else { + + if (sso_redirect) { + content = ( + + ); + } else { + let signupButton; + + if (registrationsOpen) { + signupButton = ( + + + + ); + } else { + signupButton = ( + + ); + } + + content = ( + <> + {signupButton} + + + ); + } + } + + if(transientSingleColumn) + banner = (
+ {intl.formatMessage(messages.openedInClassicInterface)} + {" "} + + {intl.formatMessage(messages.advancedInterface)} + +
); + + return ( +
+ {banner && +
+ {banner} +
+ } + + {content} + + {signedIn && ( + <> + + + + + )} + + + + {(signedIn || timelinePreview) && ( + + )} + + {!signedIn && ( +
+ { disabledAccountId ? : } +
+ )} + + + +
    + {(!this.state.collapsed || this.state.animating) && ( + <> + {signedIn && ( + <> +
  • + +
  • +
  • +
  • + +
  • + + {canManageReports(permissions) &&
  • } + {canViewAdminDashboard(permissions) &&
  • } + + )} + {!!preferencesLink &&
  • } +
  • + + )} +
+ + {signedIn && ( + + )} +
+ ); + } +} + +export default injectIntl(withIdentity(HeaderNavigation)); diff --git a/app/javascript/flavours/glitch/styles/components.scss b/app/javascript/flavours/glitch/styles/components.scss index e0b72d4e2d..3175c0af9a 100644 --- a/app/javascript/flavours/glitch/styles/components.scss +++ b/app/javascript/flavours/glitch/styles/components.scss @@ -3147,11 +3147,23 @@ $ui-header-logo-wordmark-width: 99px; .layout-single-column { .ui__header { - display: flex; + // lol turn this shit off + display: none; background: var(--background-color); border-bottom: 1px solid var(--background-border-color); } + .header-dropdown { + display: none; + top: -100%; + transition: all 0.5s; + } + + .header-dropdown.visible { + display: block; + top: 0; + } + .column > .scrollable, .tabs-bar__wrapper .column-header, .tabs-bar__wrapper .column-back-button { diff --git a/reload.sh b/reload.sh new file mode 100644 index 0000000000..f687808185 --- /dev/null +++ b/reload.sh @@ -0,0 +1,3 @@ +#! /bin/bash +sudo su - mastodon -c 'cd live && RAILS_ENV=production bundle exec rails tmp:cache:clear && RAILS_ENV=production bundle exec rails assets:precompile' +sudo systemctl restart mastodon-* \ No newline at end of file