From f53bb4cd7d9eca4bc2b770cb6e71ecc29cb8868b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 11 Jun 2025 18:12:04 +0200 Subject: [PATCH] Add "More" to the sidebar menu with links to mutes, blocks, and so on (#34987) --- .../components/account_header.tsx | 51 ++----- .../compose/components/action_bar.tsx | 81 ----------- .../compose/components/navigation_bar.tsx | 7 +- .../features/ui/components/more_link.tsx | 126 ++++++++++++++++++ .../ui/components/navigation_panel.tsx | 33 +---- app/javascript/mastodon/locales/en.json | 5 + .../styles/mastodon/components.scss | 1 + spec/system/log_out_spec.rb | 4 +- 8 files changed, 151 insertions(+), 157 deletions(-) delete mode 100644 app/javascript/mastodon/features/compose/components/action_bar.tsx create mode 100644 app/javascript/mastodon/features/ui/components/more_link.tsx diff --git a/app/javascript/mastodon/features/account_timeline/components/account_header.tsx b/app/javascript/mastodon/features/account_timeline/components/account_header.tsx index a156e0cc36..18807ecf85 100644 --- a/app/javascript/mastodon/features/account_timeline/components/account_header.tsx +++ b/app/javascript/mastodon/features/account_timeline/components/account_header.tsx @@ -418,7 +418,7 @@ export const AccountHeader: React.FC<{ return arr; } - if (signedIn && account.id !== me && !account.suspended) { + if (signedIn && !account.suspended) { arr.push({ text: intl.formatMessage(messages.mention, { name: account.username, @@ -442,37 +442,7 @@ export const AccountHeader: React.FC<{ arr.push(null); } - if (account.id === me) { - arr.push({ - text: intl.formatMessage(messages.edit_profile), - href: '/settings/profile', - }); - arr.push({ - text: intl.formatMessage(messages.preferences), - href: '/settings/preferences', - }); - arr.push(null); - arr.push({ - text: intl.formatMessage(messages.follow_requests), - to: '/follow_requests', - }); - arr.push({ - text: intl.formatMessage(messages.favourites), - to: '/favourites', - }); - arr.push({ text: intl.formatMessage(messages.lists), to: '/lists' }); - arr.push({ - text: intl.formatMessage(messages.followed_tags), - to: '/followed_tags', - }); - arr.push(null); - arr.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' }); - arr.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' }); - arr.push({ - text: intl.formatMessage(messages.domain_blocks), - to: '/domain_blocks', - }); - } else if (signedIn) { + if (signedIn) { if (relationship?.following) { if (!relationship.muting) { if (relationship.showing_reblogs) { @@ -611,8 +581,7 @@ export const AccountHeader: React.FC<{ } if ( - (account.id !== me && - (permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) || + (permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) @@ -880,12 +849,14 @@ export const AccountHeader: React.FC<{
{!hidden && bellBtn} {!hidden && shareBtn} - + {accountId !== me && ( + + )} {!hidden && actionBtn}
diff --git a/app/javascript/mastodon/features/compose/components/action_bar.tsx b/app/javascript/mastodon/features/compose/components/action_bar.tsx deleted file mode 100644 index 55e95fb5d8..0000000000 --- a/app/javascript/mastodon/features/compose/components/action_bar.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { useMemo } from 'react'; - -import { defineMessages, useIntl } from 'react-intl'; - -import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react'; -import { openModal } from 'mastodon/actions/modal'; -import { Dropdown } from 'mastodon/components/dropdown_menu'; -import { useAppDispatch } from 'mastodon/store'; - -const messages = defineMessages({ - edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' }, - preferences: { - id: 'navigation_bar.preferences', - defaultMessage: 'Preferences', - }, - follow_requests: { - id: 'navigation_bar.follow_requests', - defaultMessage: 'Follow requests', - }, - favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favorites' }, - lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' }, - followed_tags: { - id: 'navigation_bar.followed_tags', - defaultMessage: 'Followed hashtags', - }, - blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' }, - domain_blocks: { - id: 'navigation_bar.domain_blocks', - defaultMessage: 'Blocked domains', - }, - mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' }, - filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' }, - logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' }, - bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' }, -}); - -export const ActionBar: React.FC = () => { - const dispatch = useAppDispatch(); - const intl = useIntl(); - - const menu = useMemo(() => { - const handleLogoutClick = () => { - dispatch(openModal({ modalType: 'CONFIRM_LOG_OUT', modalProps: {} })); - }; - - return [ - { - text: intl.formatMessage(messages.edit_profile), - href: '/settings/profile', - }, - { - text: intl.formatMessage(messages.preferences), - href: '/settings/preferences', - }, - null, - { - text: intl.formatMessage(messages.follow_requests), - to: '/follow_requests', - }, - { text: intl.formatMessage(messages.favourites), to: '/favourites' }, - { text: intl.formatMessage(messages.bookmarks), to: '/bookmarks' }, - { text: intl.formatMessage(messages.lists), to: '/lists' }, - { - text: intl.formatMessage(messages.followed_tags), - to: '/followed_tags', - }, - null, - { text: intl.formatMessage(messages.mutes), to: '/mutes' }, - { text: intl.formatMessage(messages.blocks), to: '/blocks' }, - { - text: intl.formatMessage(messages.domain_blocks), - to: '/domain_blocks', - }, - { text: intl.formatMessage(messages.filters), href: '/filters' }, - null, - { text: intl.formatMessage(messages.logout), action: handleLogoutClick }, - ]; - }, [intl, dispatch]); - - return ; -}; diff --git a/app/javascript/mastodon/features/compose/components/navigation_bar.tsx b/app/javascript/mastodon/features/compose/components/navigation_bar.tsx index df1c0a129d..778c7cc3a6 100644 --- a/app/javascript/mastodon/features/compose/components/navigation_bar.tsx +++ b/app/javascript/mastodon/features/compose/components/navigation_bar.tsx @@ -9,8 +9,6 @@ import { IconButton } from 'mastodon/components/icon_button'; import { me } from 'mastodon/initial_state'; import { useAppDispatch, useAppSelector } from 'mastodon/store'; -import { ActionBar } from './action_bar'; - const messages = defineMessages({ cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' }, }); @@ -33,15 +31,14 @@ export const NavigationBar: React.FC = () => { return (
- {isReplying ? ( + + {isReplying && ( - ) : ( - )}
); diff --git a/app/javascript/mastodon/features/ui/components/more_link.tsx b/app/javascript/mastodon/features/ui/components/more_link.tsx new file mode 100644 index 0000000000..765b059df2 --- /dev/null +++ b/app/javascript/mastodon/features/ui/components/more_link.tsx @@ -0,0 +1,126 @@ +import { useMemo } from 'react'; + +import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; + +import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react'; +import { openModal } from 'mastodon/actions/modal'; +import { Dropdown } from 'mastodon/components/dropdown_menu'; +import { Icon } from 'mastodon/components/icon'; +import { useIdentity } from 'mastodon/identity_context'; +import type { MenuItem } from 'mastodon/models/dropdown_menu'; +import { canManageReports, canViewAdminDashboard } from 'mastodon/permissions'; +import { useAppDispatch } from 'mastodon/store'; + +const messages = defineMessages({ + followedTags: { + id: 'navigation_bar.followed_tags', + defaultMessage: 'Followed hashtags', + }, + blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' }, + domainBlocks: { + id: 'navigation_bar.domain_blocks', + defaultMessage: 'Blocked domains', + }, + mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' }, + filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' }, + administration: { + id: 'navigation_bar.administration', + defaultMessage: 'Administration', + }, + moderation: { id: 'navigation_bar.moderation', defaultMessage: 'Moderation' }, + logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' }, + automatedDeletion: { + id: 'navigation_bar.automated_deletion', + defaultMessage: 'Automated post deletion', + }, + accountSettings: { + id: 'navigation_bar.account_settings', + defaultMessage: 'Password and security', + }, + importExport: { + id: 'navigation_bar.import_export', + defaultMessage: 'Import and export', + }, + privacyAndReach: { + id: 'navigation_bar.privacy_and_reach', + defaultMessage: 'Privacy and reach', + }, +}); + +export const MoreLink: React.FC = () => { + const intl = useIntl(); + const { permissions } = useIdentity(); + const dispatch = useAppDispatch(); + + const menu = useMemo(() => { + const arr: MenuItem[] = [ + { + text: intl.formatMessage(messages.followedTags), + to: '/followed_tags', + }, + null, + { text: intl.formatMessage(messages.filters), href: '/filters' }, + { text: intl.formatMessage(messages.mutes), to: '/mutes' }, + { text: intl.formatMessage(messages.blocks), to: '/blocks' }, + { + text: intl.formatMessage(messages.domainBlocks), + to: '/domain_blocks', + }, + ]; + + arr.push( + null, + { + href: '/settings/privacy', + text: intl.formatMessage(messages.privacyAndReach), + }, + { + href: '/statuses_cleanup', + text: intl.formatMessage(messages.automatedDeletion), + }, + { + href: '/auth/edit', + text: intl.formatMessage(messages.accountSettings), + }, + { + href: '/settings/export', + text: intl.formatMessage(messages.importExport), + }, + ); + + if (canManageReports(permissions)) { + arr.push(null, { + href: '/admin/reports', + text: intl.formatMessage(messages.moderation), + }); + } + + if (canViewAdminDashboard(permissions)) { + arr.push({ + href: '/admin/dashboard', + text: intl.formatMessage(messages.administration), + }); + } + + const handleLogoutClick = () => { + dispatch(openModal({ modalType: 'CONFIRM_LOG_OUT', modalProps: {} })); + }; + + arr.push(null, { + text: intl.formatMessage(messages.logout), + action: handleLogoutClick, + }); + + return arr; + }, [intl, dispatch, permissions]); + + return ( + + + + ); +}; diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.tsx b/app/javascript/mastodon/features/ui/components/navigation_panel.tsx index 61e4f2c1b1..b38888779d 100644 --- a/app/javascript/mastodon/features/ui/components/navigation_panel.tsx +++ b/app/javascript/mastodon/features/ui/components/navigation_panel.tsx @@ -16,12 +16,10 @@ import BookmarksActiveIcon from '@/material-icons/400-24px/bookmarks-fill.svg?re import BookmarksIcon from '@/material-icons/400-24px/bookmarks.svg?react'; import ExploreActiveIcon from '@/material-icons/400-24px/explore-fill.svg?react'; import ExploreIcon from '@/material-icons/400-24px/explore.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 InfoIcon from '@/material-icons/400-24px/info.svg?react'; import LogoutIcon from '@/material-icons/400-24px/logout.svg?react'; -import AdministrationIcon from '@/material-icons/400-24px/manufacturing.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'; @@ -43,13 +41,13 @@ import { useBreakpoint } from 'mastodon/features/ui/hooks/useBreakpoint'; import { useIdentity } from 'mastodon/identity_context'; import { timelinePreview, trendsEnabled, me } from 'mastodon/initial_state'; import { transientSingleColumn } from 'mastodon/is_mobile'; -import { canManageReports, canViewAdminDashboard } from 'mastodon/permissions'; import { selectUnreadNotificationGroupsCount } from 'mastodon/selectors/notifications'; import { useAppSelector, useAppDispatch } from 'mastodon/store'; import { ColumnLink } from './column_link'; import DisabledAccountBanner from './disabled_account_banner'; import { ListPanel } from './list_panel'; +import { MoreLink } from './more_link'; import SignInBanner from './sign_in_banner'; const messages = defineMessages({ @@ -67,11 +65,6 @@ const messages = defineMessages({ 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', @@ -227,7 +220,7 @@ const MENU_WIDTH = 284; export const NavigationPanel: React.FC = () => { const intl = useIntl(); - const { signedIn, disabledAccountId, permissions } = useIdentity(); + const { signedIn, disabledAccountId } = useIdentity(); const open = useAppSelector((state) => state.navigation.open); const dispatch = useAppDispatch(); const openable = useBreakpoint('openable'); @@ -450,31 +443,13 @@ export const NavigationPanel: React.FC = () => { text={intl.formatMessage(messages.preferences)} /> - {canManageReports(permissions) && ( - - )} - {canViewAdminDashboard(permissions) && ( - - )} + )}

+