Profile redesign: Account name (#37527)

This commit is contained in:
Echo 2026-01-20 12:10:46 +01:00 committed by GitHub
parent fa9b905fdf
commit a1c17fef3a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 140 additions and 41 deletions

View File

@ -1,3 +1,4 @@
import type { ReactNode } from 'react';
import { useState, useRef, useCallback, useId } from 'react';
import { FormattedMessage } from 'react-intl';
@ -15,7 +16,9 @@ export const DomainPill: React.FC<{
domain: string;
username: string;
isSelf: boolean;
}> = ({ domain, username, isSelf }) => {
children?: ReactNode;
className?: string;
}> = ({ domain, username, isSelf, children, className }) => {
const accessibilityId = useId();
const [open, setOpen] = useState(false);
const [expanded, setExpanded] = useState(false);
@ -32,7 +35,9 @@ export const DomainPill: React.FC<{
return (
<>
<button
className={classNames('account__domain-pill', { active: open })}
className={classNames('account__domain-pill', className, {
active: open,
})}
ref={triggerRef}
onClick={handleClick}
aria-expanded={open}
@ -40,6 +45,7 @@ export const DomainPill: React.FC<{
type='button'
>
{domain}
{children}
</button>
<Overlay

View File

@ -1,25 +1,22 @@
import { useCallback } from 'react';
import { useIntl } from 'react-intl';
import classNames from 'classnames';
import { Helmet } from 'react-helmet';
import { AccountBio } from '@/mastodon/components/account_bio';
import { DisplayName } from '@/mastodon/components/display_name';
import { AnimateEmojiProvider } from '@/mastodon/components/emoji/context';
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
import { openModal } from 'mastodon/actions/modal';
import { Avatar } from 'mastodon/components/avatar';
import { Icon } from 'mastodon/components/icon';
import { AccountNote } from 'mastodon/features/account/components/account_note';
import { DomainPill } from 'mastodon/features/account/components/domain_pill';
import FollowRequestNoteContainer from 'mastodon/features/account/containers/follow_request_note_container';
import { autoPlayGif, me, domain as localDomain } from 'mastodon/initial_state';
import type { Account } from 'mastodon/models/account';
import { getAccountHidden } from 'mastodon/selectors/accounts';
import { useAppSelector, useAppDispatch } from 'mastodon/store';
import { isRedesignEnabled } from '../common';
import { AccountName } from './account_name';
import { AccountBadges } from './badges';
import { AccountButtons } from './buttons';
import { FamiliarFollowers } from './familiar_followers';
@ -28,6 +25,7 @@ import { AccountInfo } from './info';
import { MemorialNote } from './memorial_note';
import { MovedNote } from './moved_note';
import { AccountNumberFields } from './number_fields';
import redesignClasses from './redesign.module.scss';
import { AccountTabs } from './tabs';
const titleFromAccount = (account: Account) => {
@ -47,7 +45,6 @@ export const AccountHeader: React.FC<{
hideTabs?: boolean;
}> = ({ accountId, hideTabs }) => {
const dispatch = useAppDispatch();
const intl = useIntl();
const account = useAppSelector((state) => state.accounts.get(accountId));
const relationship = useAppSelector((state) =>
state.relationships.get(accountId),
@ -85,8 +82,6 @@ export const AccountHeader: React.FC<{
const suspendedOrHidden = hidden || account.suspended;
const isLocal = !account.acct.includes('@');
const username = account.acct.split('@')[0];
const domain = isLocal ? localDomain : account.acct.split('@')[1];
return (
<div className='account-timeline__header'>
@ -133,38 +128,27 @@ export const AccountHeader: React.FC<{
/>
</a>
<AccountButtons
accountId={accountId}
className='account__header__buttons--desktop'
/>
{!isRedesignEnabled() && (
<AccountButtons
accountId={accountId}
className='account__header__buttons--desktop'
/>
)}
</div>
<div className='account__header__tabs__name'>
<h1>
<DisplayName account={account} variant='simple' />
<small>
<span>
@{username}
<span className='invisible'>@{domain}</span>
</span>
<DomainPill
username={username ?? ''}
domain={domain ?? ''}
isSelf={me === account.id}
/>
{account.locked && (
<Icon
id='lock'
icon={LockIcon}
aria-label={intl.formatMessage({
id: 'account.locked_info',
defaultMessage:
'This account privacy status is set to locked. The owner manually reviews who can follow them.',
})}
/>
)}
</small>
</h1>
<div
className={classNames(
'account__header__tabs__name',
isRedesignEnabled() && redesignClasses.nameWrapper,
)}
>
<AccountName
accountId={accountId}
className={classNames(
isRedesignEnabled() && redesignClasses.name,
)}
/>
{isRedesignEnabled() && <AccountButtons accountId={accountId} />}
</div>
<AccountBadges accountId={accountId} />

View File

@ -0,0 +1,68 @@
import type { FC } from 'react';
import { useIntl } from 'react-intl';
import { DisplayName } from '@/mastodon/components/display_name';
import { Icon } from '@/mastodon/components/icon';
import { useAccount } from '@/mastodon/hooks/useAccount';
import { useAppSelector } from '@/mastodon/store';
import InfoIcon from '@/material-icons/400-24px/info.svg?react';
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
import { DomainPill } from '../../account/components/domain_pill';
import { isRedesignEnabled } from '../common';
import classes from './redesign.module.scss';
export const AccountName: FC<{ accountId: string; className?: string }> = ({
accountId,
className,
}) => {
const intl = useIntl();
const account = useAccount(accountId);
const me = useAppSelector((state) => state.meta.get('me') as string);
const localDomain = useAppSelector(
(state) => state.meta.get('domain') as string,
);
if (!account) {
return null;
}
const [username = '', domain = localDomain] = account.acct.split('@');
return (
<h1 className={className}>
<DisplayName account={account} variant='simple' />
<small>
<span>
@{username}
{isRedesignEnabled() && '@'}
<span className='invisible'>
{!isRedesignEnabled() && '@'}
{domain}
</span>
</span>
<DomainPill
username={username}
domain={domain}
isSelf={me === account.id}
className={(isRedesignEnabled() && classes.domainPill) || ''}
>
{isRedesignEnabled() && <Icon id='info' icon={InfoIcon} />}
</DomainPill>
{!isRedesignEnabled() && account.locked && (
<Icon
id='lock'
icon={LockIcon}
aria-label={intl.formatMessage({
id: 'account.locked_info',
defaultMessage:
'This account privacy status is set to locked. The owner manually reviews who can follow them.',
})}
/>
)}
</small>
</h1>
);
};

View File

@ -1,3 +1,44 @@
.nameWrapper {
display: flex;
gap: 16px;
}
.name {
flex-grow: 1;
font-size: 22px;
white-space: initial;
text-overflow: initial;
line-height: normal;
:global(.icon-info) {
margin-left: 2px;
width: 1em;
height: 1em;
align-self: center;
}
}
// Overrides .account__header__tabs__name h1 small
h1.name > small {
gap: 0;
}
.domainPill {
appearance: none;
border: none;
background: none;
padding: 0;
text-decoration: underline;
color: inherit;
font-size: 1em;
font-weight: initial;
&:global(.active) {
background: none;
color: inherit;
}
}
.fieldList {
margin-top: 16px;
}