From ee3cb553cd746e16f7f090d01651ee7ca0c525eb Mon Sep 17 00:00:00 2001 From: diondiondion Date: Fri, 4 Sep 2026 14:28:15 +0000 Subject: [PATCH] Redesign: Update column headers on pinned Fediverse Feed pages (#40383) --- .../features/community_timeline/index.jsx | 59 +++++++++---- .../components/feed_column_settings.tsx | 85 +++++++++++++++++++ .../features/public_timeline/index.jsx | 61 +++++++++---- app/javascript/mastodon/locales/en.json | 2 + 4 files changed, 176 insertions(+), 31 deletions(-) create mode 100644 app/javascript/mastodon/features/public_timeline/components/feed_column_settings.tsx diff --git a/app/javascript/mastodon/features/community_timeline/index.jsx b/app/javascript/mastodon/features/community_timeline/index.jsx index 28b6a8094c2..d85a3fdf835 100644 --- a/app/javascript/mastodon/features/community_timeline/index.jsx +++ b/app/javascript/mastodon/features/community_timeline/index.jsx @@ -9,7 +9,7 @@ import { connect } from 'react-redux'; import PeopleIcon from '@/material-icons/400-24px/group.svg?react'; import { Column } from '@/mastodon/components/column'; -import { ColumnHeader } from '@/mastodon/components/column/header'; +import { ColumnHeader as LegacyColumnHeader } from '@/mastodon/components/column/header'; import { injectIntl } from '@/mastodon/components/intl'; import { DismissableBanner } from 'mastodon/components/dismissable_banner'; import { identityContextPropShape, withIdentity } from 'mastodon/identity_context'; @@ -22,9 +22,14 @@ import { expandCommunityTimeline } from '../../actions/timelines'; import StatusListContainer from '../ui/containers/status_list_container'; import ColumnSettingsContainer from './containers/column_settings_container'; +import { ColumnHeader, ColumnSettingsMenu } from '@/mastodon/components/column_header'; +import { FeedColumnSettings } from '../public_timeline/components/feed_column_settings'; +import { MultiColumnMenuItems } from '@/mastodon/components/column_header/multicolumn_settings'; +import { isRedesignEnabled } from '@/mastodon/utils/environment'; const messages = defineMessages({ title: { id: 'column.community', defaultMessage: 'Local timeline' }, + title_redesign: { id: 'column.this_server', defaultMessage: 'This Server' }, }); const mapStateToProps = (state, { columnId }) => { @@ -129,21 +134,45 @@ class CommunityTimeline extends PureComponent { /> ); + const title = intl.formatMessage(isRedesignEnabled() ? messages.title_redesign : messages.title) + return ( - - - - + + {isRedesignEnabled() ? ( + + + {multiColumn && ( + + )} + + } + /> + ) : ( + + + + )} } diff --git a/app/javascript/mastodon/features/public_timeline/components/feed_column_settings.tsx b/app/javascript/mastodon/features/public_timeline/components/feed_column_settings.tsx new file mode 100644 index 00000000000..1447dc5cb19 --- /dev/null +++ b/app/javascript/mastodon/features/public_timeline/components/feed_column_settings.tsx @@ -0,0 +1,85 @@ +import { useCallback } from 'react'; + +import { FormattedMessage } from 'react-intl'; + +import type { + List as ImmutableList, + Record as ImmutableRecord, +} from 'immutable'; + +import { changeColumnParams } from '@/mastodon/actions/columns'; +import { MenuItemCheckbox } from '@/mastodon/components/menu'; +import type { MenuItemCheckboxChangeHandler } from '@/mastodon/components/menu/items'; +import { useAppSelector, useAppDispatch } from 'mastodon/store'; + +import { changeSetting } from '../../../actions/settings'; + +type Columns = ImmutableList< + ImmutableRecord<{ + uuid: string; + params?: string; + }> +>; + +export const FeedColumnSettings: React.FC<{ + columnId: string | undefined; + localOnly?: boolean; +}> = ({ columnId, localOnly }) => { + const dispatch = useAppDispatch(); + + const settings = useAppSelector((state) => { + const columns = state.settings.get('columns') as Columns; + const index = columns.findIndex((c) => c.get('uuid') === columnId); + + return columnId && index >= 0 + ? columns.get(index)?.get('params') + : state.settings.get('public'); + }); + + const onChange = useCallback( + ({ value, checked }) => { + if (columnId) { + dispatch(changeColumnParams(columnId, ['other', value], checked)); + } else { + dispatch(changeSetting(['public', 'other', value], checked)); + } + }, + [columnId, dispatch], + ); + + /* eslint-disable @typescript-eslint/no-unsafe-call */ + // @ts-expect-error settings isn't typed yet + const onlyMedia = settings.getIn(['other', 'onlyMedia']) as boolean; + // @ts-expect-error settings isn't typed yet + const onlyRemote = settings.getIn(['other', 'onlyRemote']) as boolean; + /* eslint-enable @typescript-eslint/no-unsafe-call */ + + return ( + <> + + + + {!localOnly && ( + + + + )} + + ); +}; diff --git a/app/javascript/mastodon/features/public_timeline/index.jsx b/app/javascript/mastodon/features/public_timeline/index.jsx index 56c488d9e21..1fc0e32463a 100644 --- a/app/javascript/mastodon/features/public_timeline/index.jsx +++ b/app/javascript/mastodon/features/public_timeline/index.jsx @@ -9,7 +9,7 @@ import { connect } from 'react-redux'; import PublicIcon from '@/material-icons/400-24px/public.svg?react'; import { Column } from '@/mastodon/components/column'; -import { ColumnHeader } from '@/mastodon/components/column/header'; +import { ColumnHeader as LegacyColumnHeader } from '@/mastodon/components/column/header'; import { DismissableBanner } from 'mastodon/components/dismissable_banner'; import { injectIntl } from '@/mastodon/components/intl'; import { identityContextPropShape, withIdentity } from 'mastodon/identity_context'; @@ -22,9 +22,14 @@ import { expandPublicTimeline } from '../../actions/timelines'; import StatusListContainer from '../ui/containers/status_list_container'; import ColumnSettingsContainer from './containers/column_settings_container'; +import { isRedesignEnabled } from '@/mastodon/utils/environment'; +import { ColumnHeader, ColumnSettingsMenu } from '@/mastodon/components/column_header'; +import { FeedColumnSettings } from './components/feed_column_settings'; +import { MultiColumnMenuItems } from '@/mastodon/components/column_header/multicolumn_settings'; const messages = defineMessages({ title: { id: 'column.public', defaultMessage: 'Federated timeline' }, + title_redesign: { id: 'column.other_servers', defaultMessage: 'Other Servers' }, }); const mapStateToProps = (state, { columnId }) => { @@ -132,21 +137,45 @@ class PublicTimeline extends PureComponent { /> ); + const title = intl.formatMessage(isRedesignEnabled() ? messages.title_redesign : messages.title) + return ( - - - - + + {isRedesignEnabled() ? ( + + + {multiColumn && ( + + )} + + } + /> + ) : ( + + + + )} } @@ -159,7 +188,7 @@ class PublicTimeline extends PureComponent { /> - {intl.formatMessage(messages.title)} + {title} diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 5153cac43b6..790fbcacef1 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -475,10 +475,12 @@ "column.mutes": "Muted users", "column.notifications": "Notifications", "column.other_collections": "Collections by {name}", + "column.other_servers": "Other Servers", "column.pins": "Pinned posts", "column.public": "Federated timeline", "column.saved_posts": "Saved Posts", "column.settings": "Column Settings", + "column.this_server": "This Server", "column.your_collections": "Your Collections", "column_back_button.label": "Back", "column_header.hide_settings": "Hide settings",