diff --git a/app/javascript/mastodon/components/column/back_button.tsx b/app/javascript/mastodon/components/column/back_button.tsx
index 72fc0443b5f..8050ce95110 100644
--- a/app/javascript/mastodon/components/column/back_button.tsx
+++ b/app/javascript/mastodon/components/column/back_button.tsx
@@ -5,7 +5,7 @@ import { FormattedMessage } from 'react-intl';
import ArrowBackIcon from '@/material-icons/400-24px/arrow_back.svg?react';
import { Icon } from 'mastodon/components/icon';
import { getColumnSkipLinkId } from 'mastodon/features/ui/components/skip_links';
-import { ButtonInTabsBar } from 'mastodon/features/ui/util/columns_context';
+import { RenderIntoTabsBarPortal } from 'mastodon/features/ui/util/columns_context';
import { useAppHistory } from '../router';
@@ -49,5 +49,5 @@ export const ColumnBackButton: React.FC<{ onClick?: OnClickCallback }> = ({
);
- return {component};
+ return {component};
};
diff --git a/app/javascript/mastodon/components/column/header.tsx b/app/javascript/mastodon/components/column/header.tsx
index dd8d8fca36a..ecf5008e94a 100644
--- a/app/javascript/mastodon/components/column/header.tsx
+++ b/app/javascript/mastodon/components/column/header.tsx
@@ -13,7 +13,7 @@ import UnfoldLessIcon from '@/material-icons/400-24px/unfold_less.svg?react';
import UnfoldMoreIcon from '@/material-icons/400-24px/unfold_more.svg?react';
import type { IconProp } from 'mastodon/components/icon';
import { Icon } from 'mastodon/components/icon';
-import { ButtonInTabsBar } from 'mastodon/features/ui/util/columns_context';
+import { RenderIntoTabsBarPortal } from 'mastodon/features/ui/util/columns_context';
import { useIdentity } from 'mastodon/identity_context';
import { getColumnSkipLinkId } from '../../features/ui/components/skip_links';
@@ -340,6 +340,6 @@ export const ColumnHeader: React.FC = ({
if (placeholder) {
return component;
} else {
- return {component};
+ return {component};
}
};
diff --git a/app/javascript/mastodon/components/column_header/index.tsx b/app/javascript/mastodon/components/column_header/index.tsx
index 616a1c512d5..ea4c6da49f8 100644
--- a/app/javascript/mastodon/components/column_header/index.tsx
+++ b/app/javascript/mastodon/components/column_header/index.tsx
@@ -9,6 +9,7 @@ import { ArrowLeftIcon, ListIcon } from '@phosphor-icons/react';
import { openNavigation } from '@/mastodon/actions/navigation';
import { getColumnSkipLinkId } from '@/mastodon/features/ui/components/skip_links';
import { useBreakpoint } from '@/mastodon/features/ui/hooks/useBreakpoint';
+import { RenderIntoTabsBarPortal } from '@/mastodon/features/ui/util/columns_context';
import { useAppDispatch } from '@/mastodon/store';
import { hasReactChildren } from '@/mastodon/utils/has_react_children';
@@ -38,21 +39,23 @@ export const ColumnHeader: React.FC = ({
const columnIndex = useColumnIndexContext();
return (
-
- {withBackButton ? : }
-
-
-
- {hasReactChildren(extraButtons) && (
- {extraButtons}
- )}
-
+
+
+ {withBackButton ? : }
+
+
+
+ {hasReactChildren(extraButtons) && (
+ {extraButtons}
+ )}
+
+
);
};
diff --git a/app/javascript/mastodon/components/column_header/styles.module.scss b/app/javascript/mastodon/components/column_header/styles.module.scss
index 54a601f0226..315e9c70111 100644
--- a/app/javascript/mastodon/components/column_header/styles.module.scss
+++ b/app/javascript/mastodon/components/column_header/styles.module.scss
@@ -18,7 +18,7 @@
color: var(--color-text-primary);
background: linear-gradient(
to bottom,
- var(--color-bg-primary) 50%,
+ var(--color-bg-primary) var(--column-header-gradient-start, 55%),
transparent
);
}
diff --git a/app/javascript/mastodon/features/bookmarked_statuses/index.tsx b/app/javascript/mastodon/features/bookmarked_statuses/index.tsx
index 33d93010bf4..3ac7830c8f6 100644
--- a/app/javascript/mastodon/features/bookmarked_statuses/index.tsx
+++ b/app/javascript/mastodon/features/bookmarked_statuses/index.tsx
@@ -5,7 +5,9 @@ import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
-import { ColumnHeader } from '@/mastodon/components/column/header';
+import { ColumnHeader as LegacyColumnHeader } from '@/mastodon/components/column/header';
+import { ColumnHeader } from '@/mastodon/components/column_header';
+import { isRedesignEnabled } from '@/mastodon/utils/environment';
import BookmarksIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react';
import {
fetchBookmarkedStatuses,
@@ -18,6 +20,7 @@ import { useAppDispatch, useAppSelector } from 'mastodon/store';
const messages = defineMessages({
heading: { id: 'column.bookmarks', defaultMessage: 'Bookmarks' },
+ headingRedesign: { id: 'column.saved_posts', defaultMessage: 'Saved Posts' },
});
const Bookmarks: React.FC<{
@@ -74,16 +77,20 @@ const Bookmarks: React.FC<{
bindToDocument={!multiColumn}
label={intl.formatMessage(messages.heading)}
>
-
+ {isRedesignEnabled() ? (
+
+ ) : (
+
+ )}
- {intl.formatMessage(messages.heading)}
+
+ {isRedesignEnabled()
+ ? intl.formatMessage(messages.headingRedesign)
+ : intl.formatMessage(messages.heading)}
+
diff --git a/app/javascript/mastodon/features/favourites/index.jsx b/app/javascript/mastodon/features/favourites/index.jsx
deleted file mode 100644
index 0a82ecfc3bb..00000000000
--- a/app/javascript/mastodon/features/favourites/index.jsx
+++ /dev/null
@@ -1,104 +0,0 @@
-import PropTypes from 'prop-types';
-
-import { defineMessages, FormattedMessage } from 'react-intl';
-
-import { Helmet } from '@unhead/react/helmet';
-
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { connect } from 'react-redux';
-
-import { debounce } from 'lodash';
-
-import RefreshIcon from '@/material-icons/400-24px/refresh.svg?react';
-import { fetchFavourites, expandFavourites } from 'mastodon/actions/interactions';
-import { Account } from 'mastodon/components/account';
-import { Column } from 'mastodon/components/column';
-import { ColumnHeader } from '@/mastodon/components/column/header';
-import { Icon } from 'mastodon/components/icon';
-import { injectIntl } from '@/mastodon/components/intl';
-import { LoadingIndicator } from 'mastodon/components/loading_indicator';
-import ScrollableList from 'mastodon/components/scrollable_list';
-
-const messages = defineMessages({
- refresh: { id: 'refresh', defaultMessage: 'Refresh' },
-});
-
-const mapStateToProps = (state, props) => ({
- accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId, 'items']),
- hasMore: !!state.getIn(['user_lists', 'favourited_by', props.params.statusId, 'next']),
- isLoading: state.getIn(['user_lists', 'favourited_by', props.params.statusId, 'isLoading'], true),
-});
-
-class Favourites extends ImmutablePureComponent {
-
- static propTypes = {
- params: PropTypes.object.isRequired,
- dispatch: PropTypes.func.isRequired,
- accountIds: ImmutablePropTypes.list,
- hasMore: PropTypes.bool,
- isLoading: PropTypes.bool,
- multiColumn: PropTypes.bool,
- intl: PropTypes.object.isRequired,
- };
-
- componentDidMount () {
- if (!this.props.accountIds) {
- this.props.dispatch(fetchFavourites(this.props.params.statusId));
- }
- }
-
- handleRefresh = () => {
- this.props.dispatch(fetchFavourites(this.props.params.statusId));
- };
-
- handleLoadMore = debounce(() => {
- this.props.dispatch(expandFavourites(this.props.params.statusId));
- }, 300, { leading: true });
-
- render () {
- const { intl, accountIds, hasMore, isLoading, multiColumn } = this.props;
-
- if (!accountIds) {
- return (
-
-
-
- );
- }
-
- const emptyMessage = ;
-
- return (
-
-
- )}
- />
-
-
- {accountIds.map(id =>
- ,
- )}
-
-
-
-
-
-
- );
- }
-
-}
-
-export default connect(mapStateToProps)(injectIntl(Favourites));
diff --git a/app/javascript/mastodon/features/favourites/index.tsx b/app/javascript/mastodon/features/favourites/index.tsx
new file mode 100644
index 00000000000..5937ab56e05
--- /dev/null
+++ b/app/javascript/mastodon/features/favourites/index.tsx
@@ -0,0 +1,142 @@
+import { useCallback, useEffect } from 'react';
+
+import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
+
+import { useParams } from 'react-router-dom';
+
+import type { List as ImmutableList } from 'immutable';
+
+import { ArrowClockwiseIcon } from '@phosphor-icons/react';
+import { Helmet } from '@unhead/react/helmet';
+import { useDebouncedCallback } from 'use-debounce';
+
+import { ColumnHeader as LegacyColumnHeader } from '@/mastodon/components/column/header';
+import {
+ ColumnHeader,
+ ColumnHeaderButton,
+} from '@/mastodon/components/column_header';
+import { useAppDispatch, useAppSelector } from '@/mastodon/store';
+import { isRedesignEnabled } from '@/mastodon/utils/environment';
+import RefreshIcon from '@/material-icons/400-24px/refresh.svg?react';
+import {
+ fetchFavourites,
+ expandFavourites,
+} from 'mastodon/actions/interactions';
+import { Account } from 'mastodon/components/account';
+import { Column } from 'mastodon/components/column';
+import { Icon } from 'mastodon/components/icon';
+import { LoadingIndicator } from 'mastodon/components/loading_indicator';
+import ScrollableList from 'mastodon/components/scrollable_list';
+
+const messages = defineMessages({
+ title: { id: 'status.likes_title', defaultMessage: 'Post Likes' },
+ refresh: { id: 'refresh', defaultMessage: 'Refresh' },
+});
+
+const Favourites: React.FC<{ multiColumn?: boolean }> = ({ multiColumn }) => {
+ const dispatch = useAppDispatch();
+ const intl = useIntl();
+
+ const { statusId } = useParams<{ statusId?: string }>();
+
+ const { accountIds, hasMore, isLoading } = useAppSelector((state) => ({
+ accountIds: state.user_lists.getIn(['favourited_by', statusId, 'items']) as
+ | ImmutableList
+ | undefined,
+ hasMore: !!state.user_lists.getIn(['favourited_by', statusId, 'next']),
+ isLoading: state.user_lists.getIn(
+ ['favourited_by', statusId, 'isLoading'],
+ true,
+ ) as boolean,
+ }));
+
+ useEffect(() => {
+ if (!accountIds) {
+ dispatch(fetchFavourites(statusId));
+ }
+ }, [accountIds, dispatch, statusId]);
+
+ const handleRefresh = useCallback(() => {
+ dispatch(fetchFavourites(statusId));
+ }, [dispatch, statusId]);
+
+ const handleLoadMore = useDebouncedCallback(
+ () => {
+ dispatch(expandFavourites(statusId));
+ },
+ 300,
+ {
+ leading: true,
+ },
+ );
+
+ if (!accountIds) {
+ return (
+
+
+
+ );
+ }
+
+ const emptyMessage = (
+
+ );
+
+ return (
+
+ {isRedesignEnabled() ? (
+
+ {intl.formatMessage(messages.refresh)}
+
+ }
+ />
+ ) : (
+
+
+
+ }
+ />
+ )}
+
+
+ {accountIds.map((id) => (
+
+ ))}
+
+
+
+
+
+
+ );
+};
+
+// eslint-disable-next-line import/no-default-export
+export default Favourites;
diff --git a/app/javascript/mastodon/features/ui/components/columns_area/redesign.module.scss b/app/javascript/mastodon/features/ui/components/columns_area/redesign.module.scss
index 108bb1010b9..d1f77e35130 100644
--- a/app/javascript/mastodon/features/ui/components/columns_area/redesign.module.scss
+++ b/app/javascript/mastodon/features/ui/components/columns_area/redesign.module.scss
@@ -51,20 +51,24 @@
container: column / inline-size;
color: var(--color-text-primary);
background-color: var(--color-bg-primary);
-
- @media ($mobile-menu-breakpoint < width < $no-gap-breakpoint) {
- border-inline: 1px solid var(--color-border-primary);
- }
}
.columnHeader {
- background: var(--color-bg-blend-solid);
+ // Make bottom half of column header transparent to make the
+ // overlay gradient of the nested `ColumnHeader` component work
+ --column-header-gradient-start: 55%;
+
+ background: linear-gradient(
+ to bottom,
+ var(--color-bg-blend-solid) var(--column-header-gradient-start),
+ transparent var(--column-header-gradient-start)
+ );
position: sticky;
top: 0;
z-index: 2;
border-top: 0;
- @media (width >= $no-gap-breakpoint) {
+ @media (width >= $mobile-menu-breakpoint) {
border-top: 10px solid var(--color-bg-blend-solid);
}
@@ -74,3 +78,22 @@
background: var(--color-bg-primary);
}
}
+
+.content {
+ width: 100%;
+ height: 100%;
+ margin: 0 auto;
+
+ :global(.column) {
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ }
+
+ :global(.scrollable) {
+ contain: content;
+ overflow: visible;
+ border: none;
+ border-radius: 0;
+ }
+}
diff --git a/app/javascript/mastodon/features/ui/components/columns_area/redesign.tsx b/app/javascript/mastodon/features/ui/components/columns_area/redesign.tsx
index d948e430e15..5f838295f96 100644
--- a/app/javascript/mastodon/features/ui/components/columns_area/redesign.tsx
+++ b/app/javascript/mastodon/features/ui/components/columns_area/redesign.tsx
@@ -15,7 +15,7 @@ import { useColumnsContext } from '../../util/columns_context';
import { MultiColumnContent } from './multi_column_content';
import classes from './redesign.module.scss';
-const TabsBarPortal = () => {
+const TabsBarPortal: React.FC> = (props) => {
const { setTabsBarElement } = useColumnsContext();
const setRef = useCallback(
@@ -27,7 +27,7 @@ const TabsBarPortal = () => {
[setTabsBarElement],
);
- return ;
+ return ;
};
export const ColumnsAreaRedesign: React.FC<{
@@ -47,11 +47,9 @@ export const ColumnsAreaRedesign: React.FC<{
-
-
-
+
-
{children}
+
{children}
@@ -68,11 +66,9 @@ export const ColumnsAreaRedesign: React.FC<{
{isMobile ? : }
-
-
-
+
- {children}
+ {children}
);
diff --git a/app/javascript/mastodon/features/ui/util/columns_context.tsx b/app/javascript/mastodon/features/ui/util/columns_context.tsx
index e02918deb00..7ea8794c48b 100644
--- a/app/javascript/mastodon/features/ui/util/columns_context.tsx
+++ b/app/javascript/mastodon/features/ui/util/columns_context.tsx
@@ -16,7 +16,7 @@ export function useColumnsContext() {
return useContext(ColumnsContext);
}
-export const ButtonInTabsBar: React.FC<{
+export const RenderIntoTabsBarPortal: React.FC<{
children: ReactElement | string | undefined;
}> = ({ children }) => {
const { multiColumn, tabsBarElement } = useColumnsContext();
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index 8f0f8664015..d81c0977a84 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -470,6 +470,7 @@
"column.other_collections": "Collections by {name}",
"column.pins": "Pinned posts",
"column.public": "Federated timeline",
+ "column.saved_posts": "Saved Posts",
"column.your_collections": "Your Collections",
"column_back_button.label": "Back",
"column_header.hide_settings": "Hide settings",
@@ -1335,6 +1336,7 @@
"status.filter": "Filter this post",
"status.history.created": "{name} created {date}",
"status.history.edited": "{name} edited {date}",
+ "status.likes_title": "Post Likes",
"status.load_more": "Load more",
"status.media.open": "Click to open",
"status.media.show": "Click to show",