diff --git a/app/javascript/images/icons/icon_verified.svg b/app/javascript/images/icons/icon_verified.svg index 65873b9dc43..62bdcb57102 100644 --- a/app/javascript/images/icons/icon_verified.svg +++ b/app/javascript/images/icons/icon_verified.svg @@ -2,9 +2,9 @@ - - - + + + diff --git a/app/javascript/mastodon/components/dropdown_menu.tsx b/app/javascript/mastodon/components/dropdown_menu.tsx index 6ed138c3015..b5ee0db34ed 100644 --- a/app/javascript/mastodon/components/dropdown_menu.tsx +++ b/app/javascript/mastodon/components/dropdown_menu.tsx @@ -311,6 +311,7 @@ interface DropdownProps { status?: ImmutableMap; needsStatusRefresh?: boolean; forceDropdown?: boolean; + className?: string; renderItem?: RenderItemFn; renderHeader?: RenderHeaderFn; onOpen?: // Must use a union type for the full function as a union with void is not allowed. @@ -335,6 +336,7 @@ export const Dropdown = ({ status, needsStatusRefresh, forceDropdown = false, + className, renderItem, renderHeader, onOpen, @@ -434,6 +436,7 @@ export const Dropdown = ({ modalProps: { actions: items, onClick: handleItemClick, + className, }, }), ); @@ -462,6 +465,7 @@ export const Dropdown = ({ handleClose, statusId, needsStatusRefresh, + className, ], ); @@ -515,7 +519,7 @@ export const Dropdown = ({ popperConfig={popperConfig} > {({ props, arrowProps, placement }) => ( -
+
diff --git a/app/javascript/mastodon/features/account_timeline/components/fields.tsx b/app/javascript/mastodon/features/account_timeline/components/fields.tsx index 5d22acc09a2..539546759d5 100644 --- a/app/javascript/mastodon/features/account_timeline/components/fields.tsx +++ b/app/javascript/mastodon/features/account_timeline/components/fields.tsx @@ -15,8 +15,7 @@ import { FormattedDateWrapper } from '@/mastodon/components/formatted_date'; import { Icon } from '@/mastodon/components/icon'; import { useElementHandledLink } from '@/mastodon/components/status/handled_link'; import { useAccount } from '@/mastodon/hooks/useAccount'; -import type { Account } from '@/mastodon/models/account'; -import { isValidUrl } from '@/mastodon/utils/checks'; +import type { Account, AccountFieldShape } from '@/mastodon/models/account'; import type { OnElementHandler } from '@/mastodon/utils/html'; import { cleanExtraEmojis } from '../../emoji/normalize'; @@ -76,8 +75,8 @@ const RedesignAccountHeaderFields: FC<{ account: Account }> = ({ account }) => { [account.emojis], ); const textHasCustomEmoji = useCallback( - (text: string) => { - if (!emojis) { + (text?: string | null) => { + if (!emojis || !text) { return false; } for (const emoji of Object.keys(emojis)) { @@ -92,62 +91,96 @@ const RedesignAccountHeaderFields: FC<{ account: Account }> = ({ account }) => { const htmlHandlers = useElementHandledLink({ hashtagAccountId: account.id, }); - const intl = useIntl(); + + if (account.fields.isEmpty()) { + return null; + } return (
- {account.fields.map( - ( - { name, name_emojified, value_emojified, value_plain, verified_at }, - key, - ) => ( -
- - - {verified_at && ( - - )} -
- ), - )} + {account.fields.map((field, key) => ( + + ))}
); }; +const FieldRow: FC< + { + textHasCustomEmoji: (text?: string | null) => boolean; + htmlHandlers: ReturnType; + } & AccountFieldShape +> = ({ + textHasCustomEmoji, + htmlHandlers, + name, + name_emojified, + value_emojified, + value_plain, + verified_at, +}) => { + const intl = useIntl(); + const [showAll, setShowAll] = useState(false); + const handleClick = useCallback(() => { + setShowAll((prev) => !prev); + }, []); + + return ( + /* eslint-disable -- This method of showing field contents is not very accessible, but it's what we've got for now */ +
+ +
+ + + {verified_at && ( + + )} +
+
+ ); +}; + const FieldHTML: FC< { - as: 'dd' | 'dt'; + as?: 'span' | 'dt'; text: string; textEmojified: string; textHasCustomEmoji: boolean; @@ -164,11 +197,6 @@ const FieldHTML: FC< onElement, ...props }) => { - const [showAll, setShowAll] = useState(false); - const handleClick = useCallback(() => { - setShowAll((prev) => !prev); - }, []); - const handleElement: OnElementHandler = useCallback( (element, props, children, extra) => { if (element instanceof HTMLAnchorElement) { @@ -186,17 +214,13 @@ const FieldHTML: FC< }, [onElement, textHasCustomEmoji], ); + return ( diff --git a/app/javascript/mastodon/features/account_timeline/components/menu.tsx b/app/javascript/mastodon/features/account_timeline/components/menu.tsx index f03878eb8c8..1fcd2f691e8 100644 --- a/app/javascript/mastodon/features/account_timeline/components/menu.tsx +++ b/app/javascript/mastodon/features/account_timeline/components/menu.tsx @@ -42,6 +42,8 @@ import ShareIcon from '@/material-icons/400-24px/share.svg?react'; import { isRedesignEnabled } from '../common'; +import classes from './redesign.module.scss'; + export const AccountMenu: FC<{ accountId: string }> = ({ accountId }) => { const intl = useIntl(); const { signedIn, permissions } = useIdentity(); @@ -86,6 +88,7 @@ export const AccountMenu: FC<{ accountId: string }> = ({ accountId }) => { items={menuItems} icon='ellipsis-v' iconComponent={MoreHorizIcon} + className={classes.buttonMenu} /> ); }; diff --git a/app/javascript/mastodon/features/account_timeline/components/redesign.module.scss b/app/javascript/mastodon/features/account_timeline/components/redesign.module.scss index a1ee8275075..5d7a5fa6500 100644 --- a/app/javascript/mastodon/features/account_timeline/components/redesign.module.scss +++ b/app/javascript/mastodon/features/account_timeline/components/redesign.module.scss @@ -24,11 +24,10 @@ .name { flex-grow: 1; - font-size: 22px; - white-space: initial; - line-height: normal; > h1 { + font-size: 22px; + line-height: normal; white-space: initial; } } @@ -149,11 +148,33 @@ $button-fallback-breakpoint: #{$button-breakpoint} + 55px; border-top: 1px solid var(--color-border-primary); } +.buttonMenu { + // Override the modal for mobile. + &:global(.actions-modal) { + max-height: none; + } + + li :global(.icon) { + width: 20px; + height: 20px; + } +} + +.bio { + font-size: 15px; +} + .badge { background-color: var(--color-bg-secondary); border: none; color: var(--color-text-secondary); font-weight: 500; + padding: 4px; + font-size: 13px; + + :global(.account__header__badges) > & { + line-height: 1; + } > span { font-weight: unset; @@ -194,12 +215,13 @@ svg.badgeIcon { .fieldList { display: grid; - grid-template-columns: 160px 1fr min-content; + grid-template-columns: 160px 1fr; column-gap: 12px; - margin: 4px 0 16px; + margin: 16px 0; + border-top: 0.5px solid var(--color-border-primary); @container (width < 420px) { - grid-template-columns: 100px 1fr min-content; + grid-template-columns: 100px 1fr; } } @@ -208,11 +230,10 @@ svg.badgeIcon { grid-column: 1 / -1; align-items: start; grid-template-columns: subgrid; - padding: 0 4px; + padding: 8px; + border-bottom: 0.5px solid var(--color-border-primary); > :is(dt, dd) { - margin: 8px 0; - &:not(.fieldShowAll) { display: -webkit-box; -webkit-box-orient: vertical; @@ -227,43 +248,34 @@ svg.badgeIcon { color: var(--color-text-secondary); } - &:not(.fieldVerified) > dd { - grid-column: span 2; + > dd { + display: flex; + align-items: center; + gap: 4px; } a { - font-weight: 500; - color: var(--color-text-brand); + color: inherit; text-decoration: none; - transition: 0.2s ease-in-out; &:hover, &:focus { - color: var(--color-text-brand-soft); + text-decoration: underline; } } } .fieldVerified { - background-color: var(--color-bg-brand-softer); -} - -.fieldLink:is(dd, dt) { - margin: 0; -} - -.fieldLink > a { - display: block; - padding: 8px 0; + background-color: var(--color-bg-success-softer); } .fieldVerifiedIcon { width: 16px; height: 16px; - margin-top: 8px; } .fieldNumbersWrapper { + font-size: 13px; padding: 0; a { @@ -323,10 +335,15 @@ svg.badgeIcon { border-bottom: 1px solid var(--color-border-primary); display: flex; gap: 12px; - padding: 0 12px; + padding: 0 24px; - @container (width >= 500px) { - padding: 0 24px; + @container (width < 500px) { + padding: 0 12px; + + a { + flex: 1 1 0px; + text-align: center; + } } a { diff --git a/app/javascript/mastodon/features/account_timeline/v2/styles.module.scss b/app/javascript/mastodon/features/account_timeline/v2/styles.module.scss index e16eca5254d..a57a5e738a0 100644 --- a/app/javascript/mastodon/features/account_timeline/v2/styles.module.scss +++ b/app/javascript/mastodon/features/account_timeline/v2/styles.module.scss @@ -39,6 +39,7 @@ cursor: pointer; display: flex; align-items: center; + font-size: 15px; } } diff --git a/app/javascript/mastodon/features/ui/components/actions_modal.tsx b/app/javascript/mastodon/features/ui/components/actions_modal.tsx index 13deedcd1a8..8d85f36f0cd 100644 --- a/app/javascript/mastodon/features/ui/components/actions_modal.tsx +++ b/app/javascript/mastodon/features/ui/components/actions_modal.tsx @@ -11,8 +11,9 @@ import { export const ActionsModal: React.FC<{ actions: MenuItem[]; onClick: React.MouseEventHandler; -}> = ({ actions, onClick }) => ( -
+ className?: string; +}> = ({ actions, onClick, className }) => ( +
    {actions.map((option, i: number) => { if (option === null) { diff --git a/app/javascript/mastodon/models/account.ts b/app/javascript/mastodon/models/account.ts index bd099b23342..b834a022ad0 100644 --- a/app/javascript/mastodon/models/account.ts +++ b/app/javascript/mastodon/models/account.ts @@ -14,7 +14,7 @@ import { CustomEmojiFactory } from './custom_emoji'; import type { CustomEmoji } from './custom_emoji'; // AccountField -interface AccountFieldShape extends Required { +export interface AccountFieldShape extends Required { name_emojified: string; value_emojified: string; value_plain: string | null;