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' &&
@@ -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 = (
);
+
+ 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