From bcdcb04e85f5dd22578c5b8d67d9859cb6a47fc7 Mon Sep 17 00:00:00 2001 From: Echo Date: Thu, 17 Sep 2026 09:13:30 +0000 Subject: [PATCH] Status: Move old components (#40559) --- .../mastodon/components/status/action_bar.tsx | 2 +- .../components/status/attachments.tsx | 2 +- .../styles.module.scss => header.module.scss} | 0 .../mastodon/components/status/header.tsx | 188 ++++++++---------- .../mastodon/components/status/index.tsx | 24 +++ .../legacy/action_bar}/index.jsx | 12 +- .../action_bar}/remove_quote_hint.module.css | 0 .../legacy/action_bar}/remove_quote_hint.tsx | 7 +- .../legacy/banner.tsx} | 2 +- .../{ => legacy}/boost_button.stories.tsx | 0 .../status/{ => legacy}/boost_button.tsx | 16 +- .../status/legacy/container.js} | 35 ++-- .../legacy/content.jsx} | 16 +- .../{ => status/legacy}/content_warning.tsx | 6 +- .../{ => status/legacy}/filter_warning.tsx | 2 +- .../legacy/hashtag_bar.test.tsx} | 2 +- .../{ => status/legacy}/hashtag_bar.tsx | 4 +- .../components/status/legacy/header.tsx | 131 ++++++++++++ .../picture_in_picture_placeholder.tsx | 0 .../legacy/quoted.stories.tsx} | 4 +- .../legacy/quoted.tsx} | 18 +- .../components/{ => status/legacy}/status.jsx | 40 ++-- .../legacy/thread_label.tsx} | 2 +- .../components/status/redesign/header.tsx | 108 ---------- .../mastodon/components/status/status.tsx | 4 +- .../components/status/styles.module.scss | 9 + .../mastodon/components/status/types.ts | 7 +- .../mastodon/components/status_list.jsx | 6 +- .../components/pinned_statuses.tsx | 4 +- .../annual_report/highlighted_post.tsx | 4 +- .../compose/components/quoted_post.tsx | 2 +- .../components/conversation.tsx | 2 +- .../notifications/components/notification.jsx | 18 +- .../components/embedded_status.tsx | 2 +- .../components/notification_with_status.tsx | 4 +- .../picture_in_picture/components/footer.tsx | 2 +- .../report/components/status_check_box.jsx | 2 +- .../mastodon/features/search/index.tsx | 6 +- .../features/status/components/action_bar.jsx | 6 +- .../status/components/detailed_status.tsx | 14 +- .../mastodon/features/status/index.jsx | 30 +-- app/javascript/mastodon/features/ui/index.jsx | 2 +- 42 files changed, 391 insertions(+), 354 deletions(-) rename app/javascript/mastodon/components/status/{redesign/styles.module.scss => header.module.scss} (100%) create mode 100644 app/javascript/mastodon/components/status/index.tsx rename app/javascript/mastodon/components/{status_action_bar => status/legacy/action_bar}/index.jsx (98%) rename app/javascript/mastodon/components/{status_action_bar => status/legacy/action_bar}/remove_quote_hint.module.css (100%) rename app/javascript/mastodon/components/{status_action_bar => status/legacy/action_bar}/remove_quote_hint.tsx (95%) rename app/javascript/mastodon/components/{status_banner.tsx => status/legacy/banner.tsx} (96%) rename app/javascript/mastodon/components/status/{ => legacy}/boost_button.stories.tsx (100%) rename app/javascript/mastodon/components/status/{ => legacy}/boost_button.tsx (95%) rename app/javascript/mastodon/{containers/status_container.jsx => components/status/legacy/container.js} (85%) rename app/javascript/mastodon/components/{status_content.jsx => status/legacy/content.jsx} (93%) rename app/javascript/mastodon/components/{ => status/legacy}/content_warning.tsx (77%) rename app/javascript/mastodon/components/{ => status/legacy}/filter_warning.tsx (89%) rename app/javascript/mastodon/components/{__tests__/hashtag_bar.tsx => status/legacy/hashtag_bar.test.tsx} (99%) rename app/javascript/mastodon/components/{ => status/legacy}/hashtag_bar.tsx (98%) create mode 100644 app/javascript/mastodon/components/status/legacy/header.tsx rename app/javascript/mastodon/components/{ => status/legacy}/picture_in_picture_placeholder.tsx (100%) rename app/javascript/mastodon/components/{status_quoted.stories.tsx => status/legacy/quoted.stories.tsx} (95%) rename app/javascript/mastodon/components/{status_quoted.tsx => status/legacy/quoted.tsx} (95%) rename app/javascript/mastodon/components/{ => status/legacy}/status.jsx (94%) rename app/javascript/mastodon/components/{status_thread_label.tsx => status/legacy/thread_label.tsx} (95%) delete mode 100644 app/javascript/mastodon/components/status/redesign/header.tsx diff --git a/app/javascript/mastodon/components/status/action_bar.tsx b/app/javascript/mastodon/components/status/action_bar.tsx index 78e9ea08ae4..0a5cb9a1969 100644 --- a/app/javascript/mastodon/components/status/action_bar.tsx +++ b/app/javascript/mastodon/components/status/action_bar.tsx @@ -67,10 +67,10 @@ import { MenuList, MenuTrigger, } from '../menu'; -import { RemoveQuoteHint } from '../status_action_bar/remove_quote_hint'; import { boostItemState, quoteItemState } from './boost_button_utils'; import { useStatusContext } from './hooks'; +import { RemoveQuoteHint } from './legacy/action_bar/remove_quote_hint'; import classes from './styles.module.scss'; interface StatusActionBarProps { diff --git a/app/javascript/mastodon/components/status/attachments.tsx b/app/javascript/mastodon/components/status/attachments.tsx index eb32928ed3e..5b319256d17 100644 --- a/app/javascript/mastodon/components/status/attachments.tsx +++ b/app/javascript/mastodon/components/status/attachments.tsx @@ -21,10 +21,10 @@ import { useAppDispatch, useAppSelector } from '@/mastodon/store'; import { compareUrls } from '@/mastodon/utils/compare_urls'; import { Card, CardBody, CardTitle } from '../card'; -import { PictureInPicturePlaceholder } from '../picture_in_picture_placeholder'; import { RelativeTimestamp } from '../relative_timestamp'; import { useStatusContext } from './hooks'; +import { PictureInPicturePlaceholder } from './legacy/picture_in_picture_placeholder'; import { StatusQuote } from './quote'; export const StatusAttachments: React.FC<{ diff --git a/app/javascript/mastodon/components/status/redesign/styles.module.scss b/app/javascript/mastodon/components/status/header.module.scss similarity index 100% rename from app/javascript/mastodon/components/status/redesign/styles.module.scss rename to app/javascript/mastodon/components/status/header.module.scss diff --git a/app/javascript/mastodon/components/status/header.tsx b/app/javascript/mastodon/components/status/header.tsx index a88ffe1b354..ca534c3e33b 100644 --- a/app/javascript/mastodon/components/status/header.tsx +++ b/app/javascript/mastodon/components/status/header.tsx @@ -1,132 +1,108 @@ -import type { FC, HTMLAttributes, MouseEventHandler, ReactNode } from 'react'; +import { useId } from 'react'; -import { defineMessage, useIntl } from 'react-intl'; +import { FormattedMessage } from 'react-intl'; import classNames from 'classnames'; import { Link } from 'react-router-dom'; -import type { Account, AccountShapeFull } from '@/mastodon/models/account'; -import { selectAccountStatus } from '@/mastodon/selectors/statuses'; -import { useAppSelector } from '@/mastodon/store'; +import type { AccountStatusShape } from '@/mastodon/models/status'; import { Avatar } from '../avatar'; -import { AvatarOverlay } from '../avatar_overlay'; -import type { DisplayNameProps } from '../display_name'; -import { LinkedDisplayName } from '../display_name'; +import { DisplayName } from '../display_name'; +import { useAccountHandle } from '../display_name/default'; import { RelativeTimestamp } from '../relative_timestamp'; -import { VisibilityIcon } from '../visibility_icon'; +import { Skeleton } from '../skeleton'; -export interface StatusHeaderProps { - statusId: string; - account?: Account | AccountShapeFull; - avatarSize?: number; - contentBeforeDate?: ReactNode; - contentAfterDate?: ReactNode; - wrapperProps?: HTMLAttributes; - displayNameProps?: DisplayNameProps; - onHeaderClick?: MouseEventHandler; +import classes from './header.module.scss'; +import { statusLink } from './utils'; + +interface StatusRedesignHeaderProps { + status: Pick< + AccountStatusShape, + 'id' | 'account' | 'created_at' | 'visibility' + >; + children?: React.ReactNode; className?: string; - featured?: boolean; } -export type StatusHeaderRenderFn = (args: StatusHeaderProps) => ReactNode; - -export const StatusHeader: FC = ({ - statusId, - account, +export const StatusRedesignHeader: React.FC = ({ + status, + children, className, - avatarSize = 48, - wrapperProps, - contentBeforeDate, - contentAfterDate, - onHeaderClick, }) => { - const status = useAppSelector((state) => - selectAccountStatus(state, statusId), + const account = status.account; + const handle = useAccountHandle(account); + + const handleId = useId(); + const accountLinkProps = { + to: { + pathname: `/@${account.acct}`, + state: { reference: 'status' }, + }, + title: `@${account.acct}`, + 'data-id': account.id, + 'data-hover-card-account': account.id, + 'data-hover-card-reference': 'status', + }; + + let displayName = ( + + + ); - if (!status) { - return null; + if (status.visibility === 'private') { + displayName = ( + + ); } - const statusAccount = status.account; - const editedAt = status.edited_at; return ( - /* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */ -
- - - {contentBeforeDate} - +
- - - - - {editedAt && } + - {contentAfterDate} -
- ); -}; +
+

+ {displayName} + • + + + +

-const editMessage = defineMessage({ - id: 'status.edited', - defaultMessage: 'Edited {date}', -}); - -const StatusEditedAt: FC<{ editedAt: string }> = ({ editedAt }) => { - const intl = useIntl(); - return ( - - {' '} - * - - ); -}; - -const StatusDisplayName: FC<{ - statusAccount?: AccountShapeFull; - friendAccount?: Account | AccountShapeFull; - avatarSize: number; -}> = ({ statusAccount, friendAccount, avatarSize }) => { - const AccountComponent = friendAccount ? AvatarOverlay : Avatar; - return ( - -
- +

+ + {handle ?? } + +

-
+ + {children} + ); }; diff --git a/app/javascript/mastodon/components/status/index.tsx b/app/javascript/mastodon/components/status/index.tsx new file mode 100644 index 00000000000..5c125e27640 --- /dev/null +++ b/app/javascript/mastodon/components/status/index.tsx @@ -0,0 +1,24 @@ +import { lazy, Suspense } from 'react'; + +import { isRedesignStatusEnabled } from '@/mastodon/utils/environment'; + +import { LoadingIndicator } from '../loading_indicator'; + +import { StatusQuoteManager } from './legacy/quoted'; +import type { StatusContainerProps } from './types'; + +const LazyStatusRedesign = lazy(() => + import('./status').then(({ StatusRedesign }) => ({ + default: StatusRedesign, + })), +); + +const StatusRedesign = (props: StatusContainerProps) => ( + }> + + +); + +export const Status = isRedesignStatusEnabled() + ? StatusRedesign + : StatusQuoteManager; diff --git a/app/javascript/mastodon/components/status_action_bar/index.jsx b/app/javascript/mastodon/components/status/legacy/action_bar/index.jsx similarity index 98% rename from app/javascript/mastodon/components/status_action_bar/index.jsx rename to app/javascript/mastodon/components/status/legacy/action_bar/index.jsx index b386edaf3dc..bef18c8690d 100644 --- a/app/javascript/mastodon/components/status_action_bar/index.jsx +++ b/app/javascript/mastodon/components/status/legacy/action_bar/index.jsx @@ -19,14 +19,14 @@ import { identityContextPropShape, withIdentity } from 'mastodon/identity_contex import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions'; import { WithRouterPropTypes } from 'mastodon/utils/react_router'; -import { Dropdown } from 'mastodon/components/dropdown_menu'; -import { me, quickBoosting } from '../../initial_state'; +import { Dropdown } from '@/mastodon/components/dropdown_menu'; +import { me, quickBoosting } from '@/mastodon/initial_state'; -import { IconButton } from '../icon_button'; -import { injectIntl } from '../intl'; -import { BoostButton } from '../status/boost_button'; +import { IconButton } from '@/mastodon/components/icon_button'; +import { injectIntl } from '@/mastodon/components/intl'; +import { BoostButton } from '../boost_button'; import { RemoveQuoteHint } from './remove_quote_hint'; -import { quoteItemState } from '../status/boost_button_utils'; +import { quoteItemState } from '../../boost_button_utils'; import { selectStatusConditions } from '@/mastodon/selectors/statuses'; diff --git a/app/javascript/mastodon/components/status_action_bar/remove_quote_hint.module.css b/app/javascript/mastodon/components/status/legacy/action_bar/remove_quote_hint.module.css similarity index 100% rename from app/javascript/mastodon/components/status_action_bar/remove_quote_hint.module.css rename to app/javascript/mastodon/components/status/legacy/action_bar/remove_quote_hint.module.css diff --git a/app/javascript/mastodon/components/status_action_bar/remove_quote_hint.tsx b/app/javascript/mastodon/components/status/legacy/action_bar/remove_quote_hint.tsx similarity index 95% rename from app/javascript/mastodon/components/status_action_bar/remove_quote_hint.tsx rename to app/javascript/mastodon/components/status/legacy/action_bar/remove_quote_hint.tsx index 7411fb83ba3..a83e0e577dd 100644 --- a/app/javascript/mastodon/components/status_action_bar/remove_quote_hint.tsx +++ b/app/javascript/mastodon/components/status/legacy/action_bar/remove_quote_hint.tsx @@ -4,13 +4,12 @@ import { FormattedMessage, useIntl } from 'react-intl'; import classNames from 'classnames'; +import { Button } from '@/mastodon/components/button'; +import { Icon } from '@/mastodon/components/icon'; +import { Popover } from '@/mastodon/components/popover'; import { useDismissible } from '@/mastodon/hooks/useDismissible'; import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react'; -import { Button } from '../button'; -import { Icon } from '../icon'; -import { Popover } from '../popover'; - import classes from './remove_quote_hint.module.css'; const DISMISSIBLE_BANNER_ID = 'notifications/remove_quote_hint'; diff --git a/app/javascript/mastodon/components/status_banner.tsx b/app/javascript/mastodon/components/status/legacy/banner.tsx similarity index 96% rename from app/javascript/mastodon/components/status_banner.tsx rename to app/javascript/mastodon/components/status/legacy/banner.tsx index b7f7289652d..4c63db38256 100644 --- a/app/javascript/mastodon/components/status_banner.tsx +++ b/app/javascript/mastodon/components/status/legacy/banner.tsx @@ -3,7 +3,7 @@ import { useCallback, useRef, useId } from 'react'; import { FormattedMessage } from 'react-intl'; -import { AnimateEmojiProvider } from './emoji/context'; +import { AnimateEmojiProvider } from '../../emoji/context'; export enum BannerVariant { Warning = 'warning', diff --git a/app/javascript/mastodon/components/status/boost_button.stories.tsx b/app/javascript/mastodon/components/status/legacy/boost_button.stories.tsx similarity index 100% rename from app/javascript/mastodon/components/status/boost_button.stories.tsx rename to app/javascript/mastodon/components/status/legacy/boost_button.stories.tsx diff --git a/app/javascript/mastodon/components/status/boost_button.tsx b/app/javascript/mastodon/components/status/legacy/boost_button.tsx similarity index 95% rename from app/javascript/mastodon/components/status/boost_button.tsx rename to app/javascript/mastodon/components/status/legacy/boost_button.tsx index 27c7e95a62c..3103c4c39ad 100644 --- a/app/javascript/mastodon/components/status/boost_button.tsx +++ b/app/javascript/mastodon/components/status/legacy/boost_button.tsx @@ -11,17 +11,23 @@ import { quoteComposeById } from '@/mastodon/actions/compose_typed'; import { toggleReblog } from '@/mastodon/actions/interactions'; import { openModal } from '@/mastodon/actions/modal'; import { fetchStatus } from '@/mastodon/actions/statuses'; +import type { RenderItemFn } from '@/mastodon/components/dropdown_menu'; +import { + Dropdown, + DropdownMenuItemContent, +} from '@/mastodon/components/dropdown_menu'; +import { IconButton } from '@/mastodon/components/icon_button'; import { useStatus } from '@/mastodon/hooks/useStatus'; import { quickBoosting } from '@/mastodon/initial_state'; import type { ActionMenuItem } from '@/mastodon/models/dropdown_menu'; import { selectStatusConditions } from '@/mastodon/selectors/statuses'; import { useAppDispatch, useAppSelector } from '@/mastodon/store'; -import type { RenderItemFn } from '../dropdown_menu'; -import { Dropdown, DropdownMenuItemContent } from '../dropdown_menu'; -import { IconButton } from '../icon_button'; - -import { boostItemState, messages, quoteItemState } from './boost_button_utils'; +import { + boostItemState, + messages, + quoteItemState, +} from '../boost_button_utils'; const StandaloneBoostButton: FC = ({ statusId, diff --git a/app/javascript/mastodon/containers/status_container.jsx b/app/javascript/mastodon/components/status/legacy/container.js similarity index 85% rename from app/javascript/mastodon/containers/status_container.jsx rename to app/javascript/mastodon/components/status/legacy/container.js index 6b0261c6563..1aee3319817 100644 --- a/app/javascript/mastodon/containers/status_container.jsx +++ b/app/javascript/mastodon/components/status/legacy/container.js @@ -3,21 +3,21 @@ import { connect } from 'react-redux'; import { unmuteAccount, unblockAccount, -} from '../actions/accounts'; -import { initBlockModal } from '../actions/blocks'; +} from '@/mastodon/actions/accounts'; +import { initBlockModal } from '@/mastodon/actions/blocks'; import { replyCompose, mentionCompose, directCompose, -} from '../actions/compose'; -import { quoteComposeById } from '../actions/compose_typed'; +} from '@/mastodon/actions/compose'; +import { quoteComposeById } from '@/mastodon/actions/compose_typed'; import { initDomainBlockModal, unblockDomain, -} from '../actions/domain_blocks'; +} from '@/mastodon/actions/domain_blocks'; import { initAddFilter, -} from '../actions/filters'; +} from '@/mastodon/actions/filters'; import { toggleReblog, toggleFavourite, @@ -25,11 +25,11 @@ import { unbookmark, pin, unpin, -} from '../actions/interactions'; -import { openModal } from '../actions/modal'; -import { initMuteModal } from '../actions/mutes'; -import { deployPictureInPicture } from '../actions/picture_in_picture'; -import { initReport } from '../actions/reports'; +} from '@/mastodon/actions/interactions'; +import { openModal } from '@/mastodon/actions/modal'; +import { initMuteModal } from '@/mastodon/actions/mutes'; +import { deployPictureInPicture } from '@/mastodon/actions/picture_in_picture'; +import { initReport } from '@/mastodon/actions/reports'; import { muteStatus, unmuteStatus, @@ -39,12 +39,13 @@ import { editStatus, translateStatus, undoStatusTranslation, -} from '../actions/statuses'; -import { setStatusQuotePolicy } from '../actions/statuses_typed'; -import { injectIntl } from '../components/intl'; -import Status from '../components/status'; -import { deleteModal } from '../initial_state'; -import { makeGetStatus, makeGetPictureInPicture } from '../selectors'; +} from '@/mastodon/actions/statuses'; +import { setStatusQuotePolicy } from '@/mastodon/actions/statuses_typed'; +import { injectIntl } from '@/mastodon/components/intl'; +import { deleteModal } from '@/mastodon/initial_state'; +import { makeGetStatus, makeGetPictureInPicture } from '@/mastodon/selectors'; + +import Status from './status'; const makeMapStateToProps = () => { const getStatus = makeGetStatus(); diff --git a/app/javascript/mastodon/components/status_content.jsx b/app/javascript/mastodon/components/status/legacy/content.jsx similarity index 93% rename from app/javascript/mastodon/components/status_content.jsx rename to app/javascript/mastodon/components/status/legacy/content.jsx index 54471a1073b..908cf5be2e0 100644 --- a/app/javascript/mastodon/components/status_content.jsx +++ b/app/javascript/mastodon/components/status/legacy/content.jsx @@ -10,15 +10,15 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import { connect } from 'react-redux'; import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react'; -import { Icon } from 'mastodon/components/icon'; -import { Poll } from 'mastodon/components/poll'; -import { identityContextPropShape, withIdentity } from 'mastodon/identity_context'; -import { languages as preloadedLanguages } from 'mastodon/initial_state'; +import { Icon } from '@/mastodon/components/icon'; +import { Poll } from '@/mastodon/components/poll'; +import { identityContextPropShape, withIdentity } from '@/mastodon/identity_context'; +import { languages as preloadedLanguages } from '@/mastodon/initial_state'; +import { EmojiHTML } from '@/mastodon/components/emoji/html'; +import { injectIntl } from '@/mastodon/components/intl'; +import { compareUrls } from '@/mastodon/utils/compare_urls'; -import { EmojiHTML } from './emoji/html'; -import { injectIntl } from './intl'; -import { HandledLink } from './status/handled_link'; -import { compareUrls } from '../utils/compare_urls'; +import { HandledLink } from '../handled_link'; const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top) diff --git a/app/javascript/mastodon/components/content_warning.tsx b/app/javascript/mastodon/components/status/legacy/content_warning.tsx similarity index 77% rename from app/javascript/mastodon/components/content_warning.tsx rename to app/javascript/mastodon/components/status/legacy/content_warning.tsx index 7f9e96cbd1e..c8ece5473e0 100644 --- a/app/javascript/mastodon/components/content_warning.tsx +++ b/app/javascript/mastodon/components/status/legacy/content_warning.tsx @@ -1,7 +1,7 @@ -import { useStatus } from '../hooks/useStatus'; +import { EmojiHTML } from '@/mastodon/components/emoji/html'; +import { useStatus } from '@/mastodon/hooks/useStatus'; -import { EmojiHTML } from './emoji/html'; -import { StatusBanner, BannerVariant } from './status_banner'; +import { StatusBanner, BannerVariant } from './banner'; export const ContentWarning: React.FC<{ statusId: string; diff --git a/app/javascript/mastodon/components/filter_warning.tsx b/app/javascript/mastodon/components/status/legacy/filter_warning.tsx similarity index 89% rename from app/javascript/mastodon/components/filter_warning.tsx rename to app/javascript/mastodon/components/status/legacy/filter_warning.tsx index 9e984a90f4b..6ee3cf4a254 100644 --- a/app/javascript/mastodon/components/filter_warning.tsx +++ b/app/javascript/mastodon/components/status/legacy/filter_warning.tsx @@ -1,6 +1,6 @@ import { FormattedMessage } from 'react-intl'; -import { StatusBanner, BannerVariant } from './status_banner'; +import { StatusBanner, BannerVariant } from './banner'; export const FilterWarning: React.FC<{ title: string; diff --git a/app/javascript/mastodon/components/__tests__/hashtag_bar.tsx b/app/javascript/mastodon/components/status/legacy/hashtag_bar.test.tsx similarity index 99% rename from app/javascript/mastodon/components/__tests__/hashtag_bar.tsx rename to app/javascript/mastodon/components/status/legacy/hashtag_bar.test.tsx index d0efa03c063..53ef9bae6bd 100644 --- a/app/javascript/mastodon/components/__tests__/hashtag_bar.tsx +++ b/app/javascript/mastodon/components/status/legacy/hashtag_bar.test.tsx @@ -1,6 +1,6 @@ import { mediaAttachmentFactoryAPI } from '@/testing/factories'; -import { computeHashtagBarForStatus } from '../hashtag_bar'; +import { computeHashtagBarForStatus } from './hashtag_bar'; function createStatus( content: string, diff --git a/app/javascript/mastodon/components/hashtag_bar.tsx b/app/javascript/mastodon/components/status/legacy/hashtag_bar.tsx similarity index 98% rename from app/javascript/mastodon/components/hashtag_bar.tsx rename to app/javascript/mastodon/components/status/legacy/hashtag_bar.tsx index 9dacc2c76e7..6521cb0a68a 100644 --- a/app/javascript/mastodon/components/hashtag_bar.tsx +++ b/app/javascript/mastodon/components/status/legacy/hashtag_bar.tsx @@ -8,12 +8,12 @@ import type { List, Record } from 'immutable'; import { groupBy, minBy } from 'lodash'; -import type { ApiTagJSON } from '../api_types/statuses'; +import type { ApiTagJSON } from '../../../api_types/statuses'; import type { MediaAttachmentShape, StatusShape, StatusTranslation, -} from '../models/status'; +} from '../../../models/status'; // Fit on a single line on desktop const VISIBLE_HASHTAGS = 3; diff --git a/app/javascript/mastodon/components/status/legacy/header.tsx b/app/javascript/mastodon/components/status/legacy/header.tsx new file mode 100644 index 00000000000..cb9eeb8c3c3 --- /dev/null +++ b/app/javascript/mastodon/components/status/legacy/header.tsx @@ -0,0 +1,131 @@ +import type { FC, HTMLAttributes, MouseEventHandler, ReactNode } from 'react'; + +import { defineMessage, useIntl } from 'react-intl'; + +import classNames from 'classnames'; +import { Link } from 'react-router-dom'; + +import { Avatar } from '@/mastodon/components/avatar'; +import { AvatarOverlay } from '@/mastodon/components/avatar_overlay'; +import type { DisplayNameProps } from '@/mastodon/components/display_name'; +import { LinkedDisplayName } from '@/mastodon/components/display_name'; +import { RelativeTimestamp } from '@/mastodon/components/relative_timestamp'; +import { VisibilityIcon } from '@/mastodon/components/visibility_icon'; +import type { Account, AccountShapeFull } from '@/mastodon/models/account'; +import { selectAccountStatus } from '@/mastodon/selectors/statuses'; +import { useAppSelector } from '@/mastodon/store'; + +export interface StatusHeaderProps { + statusId: string; + account?: Account | AccountShapeFull; + avatarSize?: number; + contentBeforeDate?: ReactNode; + contentAfterDate?: ReactNode; + wrapperProps?: HTMLAttributes; + displayNameProps?: DisplayNameProps; + onHeaderClick?: MouseEventHandler; + className?: string; + featured?: boolean; +} + +export type StatusHeaderRenderFn = (args: StatusHeaderProps) => ReactNode; + +export const StatusHeader: FC = ({ + statusId, + account, + className, + avatarSize = 48, + wrapperProps, + contentBeforeDate, + contentAfterDate, + onHeaderClick, +}) => { + const status = useAppSelector((state) => + selectAccountStatus(state, statusId), + ); + if (!status) { + return null; + } + const statusAccount = status.account; + const editedAt = status.edited_at; + + return ( + /* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */ +
+ + + {contentBeforeDate} + + + + + + + {editedAt && } + + + {contentAfterDate} +
+ ); +}; + +const editMessage = defineMessage({ + id: 'status.edited', + defaultMessage: 'Edited {date}', +}); + +const StatusEditedAt: FC<{ editedAt: string }> = ({ editedAt }) => { + const intl = useIntl(); + return ( + + {' '} + * + + ); +}; + +const StatusDisplayName: FC<{ + statusAccount?: AccountShapeFull; + friendAccount?: Account | AccountShapeFull; + avatarSize: number; +}> = ({ statusAccount, friendAccount, avatarSize }) => { + const AccountComponent = friendAccount ? AvatarOverlay : Avatar; + return ( + +
+ +
+
+ ); +}; diff --git a/app/javascript/mastodon/components/picture_in_picture_placeholder.tsx b/app/javascript/mastodon/components/status/legacy/picture_in_picture_placeholder.tsx similarity index 100% rename from app/javascript/mastodon/components/picture_in_picture_placeholder.tsx rename to app/javascript/mastodon/components/status/legacy/picture_in_picture_placeholder.tsx diff --git a/app/javascript/mastodon/components/status_quoted.stories.tsx b/app/javascript/mastodon/components/status/legacy/quoted.stories.tsx similarity index 95% rename from app/javascript/mastodon/components/status_quoted.stories.tsx rename to app/javascript/mastodon/components/status/legacy/quoted.stories.tsx index 976036fa2df..ee98cf35611 100644 --- a/app/javascript/mastodon/components/status_quoted.stories.tsx +++ b/app/javascript/mastodon/components/status/legacy/quoted.stories.tsx @@ -6,8 +6,8 @@ import { statusQuotedFactoryAPI, } from '@/testing/factories'; -import type { StatusQuoteManagerProps } from './status_quoted'; -import { StatusQuoteManager } from './status_quoted'; +import type { StatusQuoteManagerProps } from './quoted'; +import { StatusQuoteManager } from './quoted'; const meta = { title: 'Components/Status/StatusQuoteManager', diff --git a/app/javascript/mastodon/components/status_quoted.tsx b/app/javascript/mastodon/components/status/legacy/quoted.tsx similarity index 95% rename from app/javascript/mastodon/components/status_quoted.tsx rename to app/javascript/mastodon/components/status/legacy/quoted.tsx index 6caa5230121..ae9c545a019 100644 --- a/app/javascript/mastodon/components/status_quoted.tsx +++ b/app/javascript/mastodon/components/status/legacy/quoted.tsx @@ -27,15 +27,15 @@ import { getAccountHidden } from 'mastodon/selectors/accounts'; import type { RootState } from 'mastodon/store'; import { useAppDispatch, useAppSelector } from 'mastodon/store'; -import { isRedesignStatusEnabled } from '../utils/environment'; +import { isRedesignStatusEnabled } from '../../../utils/environment'; +import { Button } from '../../button'; +import { IconButton } from '../../icon_button'; +import { LoadingIndicator } from '../../loading_indicator'; +import { TypedStatusContainer } from '../types'; +import type { StatusContainerProps, StatusContextType } from '../types'; -import { Button } from './button'; -import { IconButton } from './icon_button'; -import { LoadingIndicator } from './loading_indicator'; -import type { StatusHeaderRenderFn } from './status/header'; -import { StatusHeader } from './status/header'; -import { TypedStatusContainer } from './status/types'; -import type { StatusContainerProps, StatusContextType } from './status/types'; +import type { StatusHeaderRenderFn } from './header'; +import { StatusHeader } from './header'; const MAX_QUOTE_POSTS_NESTING_LEVEL = 1; @@ -420,7 +420,7 @@ export const StatusQuoteManager = (props: StatusQuoteManagerProps) => { }; const LazyStatusRedesign = lazy(() => - import('./status/status').then(({ StatusRedesign }) => ({ + import('../status').then(({ StatusRedesign }) => ({ default: StatusRedesign, })), ); diff --git a/app/javascript/mastodon/components/status.jsx b/app/javascript/mastodon/components/status/legacy/status.jsx similarity index 94% rename from app/javascript/mastodon/components/status.jsx rename to app/javascript/mastodon/components/status/legacy/status.jsx index d97c207a933..8ca6ffe943f 100644 --- a/app/javascript/mastodon/components/status.jsx +++ b/app/javascript/mastodon/components/status/legacy/status.jsx @@ -9,31 +9,31 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react'; import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react'; -import { Hotkeys } from 'mastodon/components/hotkeys'; -import { ContentWarning } from 'mastodon/components/content_warning'; -import { FilterWarning } from 'mastodon/components/filter_warning'; -import { Icon } from 'mastodon/components/icon'; -import { PictureInPicturePlaceholder } from 'mastodon/components/picture_in_picture_placeholder'; -import { withOptionalRouter, WithOptionalRouterPropTypes } from 'mastodon/utils/react_router'; +import { Hotkeys } from '@/mastodon/components/hotkeys'; +import { Icon } from '@/mastodon/components/icon'; +import { PictureInPicturePlaceholder } from './picture_in_picture_placeholder'; +import { withOptionalRouter, WithOptionalRouterPropTypes } from '@/mastodon/utils/react_router'; -import Card from '../features/status/components/card'; +import Card from '@/mastodon/features/status/components/card'; // We use the component (and not the container) since we do not want // to use the progress bar to show download progress -import Bundle from '../features/ui/components/bundle'; -import { MediaGallery, Video, Audio } from '../features/ui/util/async-components'; -import { SensitiveMediaContext } from '../features/ui/util/sensitive_media_context'; -import { displayMedia } from '../initial_state'; +import Bundle from '@/mastodon/features/ui/components/bundle'; +import { MediaGallery, Video, Audio } from '@/mastodon/features/ui/util/async-components'; +import { SensitiveMediaContext } from '@/mastodon/features/ui/util/sensitive_media_context'; +import { displayMedia } from '@/mastodon/initial_state'; +import { CollectionPreviewCard } from '@/mastodon/features/collections/components/collection_preview_card'; +import { compareUrls } from '@/mastodon/utils/compare_urls'; +import { FOCUS_TARGET } from '@/mastodon/components/navigation_focus_target'; +import { LinkedDisplayName } from '@/mastodon/components/display_name'; -import { injectIntl } from './intl'; -import { StatusHeader } from './status/header' -import { LinkedDisplayName } from './display_name'; +import { injectIntl } from '../../intl'; +import { ContentWarning } from './content_warning'; +import { FilterWarning } from './filter_warning'; +import { StatusHeader } from './header' import { getHashtagBarForStatus } from './hashtag_bar'; -import StatusActionBar from './status_action_bar'; -import StatusContent from './status_content'; -import { StatusThreadLabel } from './status_thread_label'; -import { CollectionPreviewCard } from '../features/collections/components/collection_preview_card'; -import { compareUrls } from '../utils/compare_urls'; -import { FOCUS_TARGET } from './navigation_focus_target'; +import StatusActionBar from './action_bar'; +import StatusContent from './content'; +import { StatusThreadLabel } from './thread_label'; const domParser = new DOMParser(); diff --git a/app/javascript/mastodon/components/status_thread_label.tsx b/app/javascript/mastodon/components/status/legacy/thread_label.tsx similarity index 95% rename from app/javascript/mastodon/components/status_thread_label.tsx rename to app/javascript/mastodon/components/status/legacy/thread_label.tsx index 147a6586eda..a38e2db277c 100644 --- a/app/javascript/mastodon/components/status_thread_label.tsx +++ b/app/javascript/mastodon/components/status/legacy/thread_label.tsx @@ -4,7 +4,7 @@ import ReplyIcon from '@/material-icons/400-24px/reply.svg?react'; import { Icon } from 'mastodon/components/icon'; import { useAppSelector } from 'mastodon/store'; -import { LinkedDisplayName } from './display_name'; +import { LinkedDisplayName } from '../../display_name'; export const StatusThreadLabel: React.FC<{ accountId: string; diff --git a/app/javascript/mastodon/components/status/redesign/header.tsx b/app/javascript/mastodon/components/status/redesign/header.tsx deleted file mode 100644 index b9f0cd1540f..00000000000 --- a/app/javascript/mastodon/components/status/redesign/header.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { useId } from 'react'; - -import { FormattedMessage } from 'react-intl'; - -import classNames from 'classnames'; -import { Link } from 'react-router-dom'; - -import type { AccountStatusShape } from '@/mastodon/models/status'; - -import { Avatar } from '../../avatar'; -import { DisplayName } from '../../display_name'; -import { useAccountHandle } from '../../display_name/default'; -import { RelativeTimestamp } from '../../relative_timestamp'; -import { Skeleton } from '../../skeleton'; -import { statusLink } from '../utils'; - -import classes from './styles.module.scss'; - -interface StatusRedesignHeaderProps { - status: Pick< - AccountStatusShape, - 'id' | 'account' | 'created_at' | 'visibility' - >; - children?: React.ReactNode; - className?: string; -} - -export const StatusRedesignHeader: React.FC = ({ - status, - children, - className, -}) => { - const account = status.account; - const handle = useAccountHandle(account); - - const handleId = useId(); - const accountLinkProps = { - to: { - pathname: `/@${account.acct}`, - state: { reference: 'status' }, - }, - title: `@${account.acct}`, - 'data-id': account.id, - 'data-hover-card-account': account.id, - 'data-hover-card-reference': 'status', - }; - - let displayName = ( - - - - ); - if (status.visibility === 'private') { - displayName = ( - - ); - } - - return ( -
- - - - -
-

- {displayName} - • - - - -

- -

- - {handle ?? } - -

-
- - {children} -
- ); -}; diff --git a/app/javascript/mastodon/components/status/status.tsx b/app/javascript/mastodon/components/status/status.tsx index bc99a5fe179..cb423c26f5f 100644 --- a/app/javascript/mastodon/components/status/status.tsx +++ b/app/javascript/mastodon/components/status/status.tsx @@ -7,7 +7,6 @@ import type { Merge } from 'type-fest'; import { selectExpandedStatus } from '@/mastodon/selectors/statuses'; import { createAppSelector, useAppSelector } from '@/mastodon/store'; -import { computeHashtagBarForStatus } from '../hashtag_bar'; import { Hotkeys } from '../hotkeys'; import { Poll } from '../poll'; @@ -15,15 +14,16 @@ import { StatusActionBar } from './action_bar'; import { StatusAttachments } from './attachments'; import { StatusContent } from './content'; import { StatusHashtagBar } from './hashtag_bar'; +import { StatusRedesignHeader } from './header'; import type { StatusHandlers } from './hooks'; import { StatusContext, useStatusHandlers, useTextForScreenReader, } from './hooks'; +import { computeHashtagBarForStatus } from './legacy/hashtag_bar'; import { StatusMeta } from './meta'; import { StatusPrepend } from './prepend'; -import { StatusRedesignHeader } from './redesign/header'; import classes from './styles.module.scss'; import { TranslateButton } from './translate'; import type { StatusContainerProps, StatusContextType } from './types'; diff --git a/app/javascript/mastodon/components/status/styles.module.scss b/app/javascript/mastodon/components/status/styles.module.scss index 8d55b3e6ff2..403c0a4d5ea 100644 --- a/app/javascript/mastodon/components/status/styles.module.scss +++ b/app/javascript/mastodon/components/status/styles.module.scss @@ -217,6 +217,15 @@ @include mixins.type-micro; color: var(--color-text-secondary); + + a { + color: inherit; + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } } .actions { diff --git a/app/javascript/mastodon/components/status/types.ts b/app/javascript/mastodon/components/status/types.ts index 32f661e96f2..4948a45bb5b 100644 --- a/app/javascript/mastodon/components/status/types.ts +++ b/app/javascript/mastodon/components/status/types.ts @@ -1,12 +1,11 @@ import type { ComponentType, ReactNode } from 'react'; -import StatusContainer from '@/mastodon/containers/status_container'; +import StatusContainer from '@/mastodon/components/status/legacy/container'; import type { Account as TAccount } from '@/mastodon/models/account'; import type { Status as TStatus } from '@/mastodon/models/status'; -import Status from '../status'; - -import type { StatusHeaderRenderFn } from './header'; +import type { StatusHeaderRenderFn } from './legacy/header'; +import Status from './legacy/status'; export type StatusContextType = | 'account' diff --git a/app/javascript/mastodon/components/status_list.jsx b/app/javascript/mastodon/components/status_list.jsx index a0ef4fffce4..44aa210f902 100644 --- a/app/javascript/mastodon/components/status_list.jsx +++ b/app/javascript/mastodon/components/status_list.jsx @@ -11,7 +11,7 @@ import { RegenerationIndicator } from 'mastodon/components/regeneration_indicato import { InlineFollowSuggestions } from 'mastodon/features/home_timeline/components/inline_follow_suggestions'; import { PinnedShowAllButton } from '@/mastodon/features/account_timeline/components/pinned_statuses'; -import { StatusQuoteManager } from '../components/status_quoted'; +import { Status } from './status'; import { LoadGap } from './load_gap'; import ScrollableList from './scrollable_list'; @@ -79,7 +79,7 @@ export default class StatusList extends ImmutablePureComponent { ); default: return ( - } return ( - - +
); diff --git a/app/javascript/mastodon/features/compose/components/quoted_post.tsx b/app/javascript/mastodon/features/compose/components/quoted_post.tsx index 8be3c7e62c6..7ddb4f316ea 100644 --- a/app/javascript/mastodon/features/compose/components/quoted_post.tsx +++ b/app/javascript/mastodon/features/compose/components/quoted_post.tsx @@ -4,7 +4,7 @@ import type { FC } from 'react'; import { Map } from 'immutable'; import { quoteComposeCancel } from '@/mastodon/actions/compose_typed'; -import { QuotedStatus } from '@/mastodon/components/status_quoted'; +import { QuotedStatus } from '@/mastodon/components/status/legacy/quoted'; import { useAppDispatch, useAppSelector } from '@/mastodon/store'; import { QuotePlaceholder } from './quote_placeholder'; diff --git a/app/javascript/mastodon/features/direct_timeline/components/conversation.tsx b/app/javascript/mastodon/features/direct_timeline/components/conversation.tsx index 48cac2894ac..cecaf29b168 100644 --- a/app/javascript/mastodon/features/direct_timeline/components/conversation.tsx +++ b/app/javascript/mastodon/features/direct_timeline/components/conversation.tsx @@ -13,6 +13,7 @@ import type { import { LinkedDisplayName } from '@/mastodon/components/display_name'; import { AnimateEmojiProvider } from '@/mastodon/components/emoji/context'; +import StatusContent from '@/mastodon/components/status/legacy/content'; import type { Account } from '@/mastodon/models/account'; import type { StatusShape } from '@/mastodon/models/status'; import { @@ -39,7 +40,6 @@ import { Dropdown } from 'mastodon/components/dropdown_menu'; import { Hotkeys } from 'mastodon/components/hotkeys'; import { IconButton } from 'mastodon/components/icon_button'; import { RelativeTimestamp } from 'mastodon/components/relative_timestamp'; -import StatusContent from 'mastodon/components/status_content'; import { makeGetStatus } from 'mastodon/selectors'; const messages = defineMessages({ diff --git a/app/javascript/mastodon/features/notifications/components/notification.jsx b/app/javascript/mastodon/features/notifications/components/notification.jsx index 005a37a4d87..47ceed5047d 100644 --- a/app/javascript/mastodon/features/notifications/components/notification.jsx +++ b/app/javascript/mastodon/features/notifications/components/notification.jsx @@ -22,7 +22,7 @@ import { LinkedDisplayName } from '@/mastodon/components/display_name'; import { Icon } from 'mastodon/components/icon'; import { injectIntl } from '@/mastodon/components/intl'; import { Hotkeys } from 'mastodon/components/hotkeys'; -import { StatusQuoteManager } from 'mastodon/components/status_quoted'; +import { Status } from 'mastodon/components/status'; import { me } from 'mastodon/initial_state'; import { WithRouterPropTypes } from 'mastodon/utils/react_router'; @@ -165,7 +165,7 @@ class Notification extends ImmutablePureComponent { renderMention (notification) { return ( -