Profile redesign: Account name (#37527)
This commit is contained in:
parent
fa9b905fdf
commit
a1c17fef3a
@ -1,3 +1,4 @@
|
|||||||
|
import type { ReactNode } from 'react';
|
||||||
import { useState, useRef, useCallback, useId } from 'react';
|
import { useState, useRef, useCallback, useId } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
@ -15,7 +16,9 @@ export const DomainPill: React.FC<{
|
|||||||
domain: string;
|
domain: string;
|
||||||
username: string;
|
username: string;
|
||||||
isSelf: boolean;
|
isSelf: boolean;
|
||||||
}> = ({ domain, username, isSelf }) => {
|
children?: ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}> = ({ domain, username, isSelf, children, className }) => {
|
||||||
const accessibilityId = useId();
|
const accessibilityId = useId();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
@ -32,7 +35,9 @@ export const DomainPill: React.FC<{
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
className={classNames('account__domain-pill', { active: open })}
|
className={classNames('account__domain-pill', className, {
|
||||||
|
active: open,
|
||||||
|
})}
|
||||||
ref={triggerRef}
|
ref={triggerRef}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
aria-expanded={open}
|
aria-expanded={open}
|
||||||
@ -40,6 +45,7 @@ export const DomainPill: React.FC<{
|
|||||||
type='button'
|
type='button'
|
||||||
>
|
>
|
||||||
{domain}
|
{domain}
|
||||||
|
{children}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Overlay
|
<Overlay
|
||||||
|
|||||||
@ -1,25 +1,22 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { useIntl } from 'react-intl';
|
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
import { AccountBio } from '@/mastodon/components/account_bio';
|
import { AccountBio } from '@/mastodon/components/account_bio';
|
||||||
import { DisplayName } from '@/mastodon/components/display_name';
|
|
||||||
import { AnimateEmojiProvider } from '@/mastodon/components/emoji/context';
|
import { AnimateEmojiProvider } from '@/mastodon/components/emoji/context';
|
||||||
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
|
||||||
import { openModal } from 'mastodon/actions/modal';
|
import { openModal } from 'mastodon/actions/modal';
|
||||||
import { Avatar } from 'mastodon/components/avatar';
|
import { Avatar } from 'mastodon/components/avatar';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
|
||||||
import { AccountNote } from 'mastodon/features/account/components/account_note';
|
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 FollowRequestNoteContainer from 'mastodon/features/account/containers/follow_request_note_container';
|
||||||
import { autoPlayGif, me, domain as localDomain } from 'mastodon/initial_state';
|
import { autoPlayGif, me, domain as localDomain } from 'mastodon/initial_state';
|
||||||
import type { Account } from 'mastodon/models/account';
|
import type { Account } from 'mastodon/models/account';
|
||||||
import { getAccountHidden } from 'mastodon/selectors/accounts';
|
import { getAccountHidden } from 'mastodon/selectors/accounts';
|
||||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||||
|
|
||||||
|
import { isRedesignEnabled } from '../common';
|
||||||
|
|
||||||
|
import { AccountName } from './account_name';
|
||||||
import { AccountBadges } from './badges';
|
import { AccountBadges } from './badges';
|
||||||
import { AccountButtons } from './buttons';
|
import { AccountButtons } from './buttons';
|
||||||
import { FamiliarFollowers } from './familiar_followers';
|
import { FamiliarFollowers } from './familiar_followers';
|
||||||
@ -28,6 +25,7 @@ import { AccountInfo } from './info';
|
|||||||
import { MemorialNote } from './memorial_note';
|
import { MemorialNote } from './memorial_note';
|
||||||
import { MovedNote } from './moved_note';
|
import { MovedNote } from './moved_note';
|
||||||
import { AccountNumberFields } from './number_fields';
|
import { AccountNumberFields } from './number_fields';
|
||||||
|
import redesignClasses from './redesign.module.scss';
|
||||||
import { AccountTabs } from './tabs';
|
import { AccountTabs } from './tabs';
|
||||||
|
|
||||||
const titleFromAccount = (account: Account) => {
|
const titleFromAccount = (account: Account) => {
|
||||||
@ -47,7 +45,6 @@ export const AccountHeader: React.FC<{
|
|||||||
hideTabs?: boolean;
|
hideTabs?: boolean;
|
||||||
}> = ({ accountId, hideTabs }) => {
|
}> = ({ accountId, hideTabs }) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const intl = useIntl();
|
|
||||||
const account = useAppSelector((state) => state.accounts.get(accountId));
|
const account = useAppSelector((state) => state.accounts.get(accountId));
|
||||||
const relationship = useAppSelector((state) =>
|
const relationship = useAppSelector((state) =>
|
||||||
state.relationships.get(accountId),
|
state.relationships.get(accountId),
|
||||||
@ -85,8 +82,6 @@ export const AccountHeader: React.FC<{
|
|||||||
|
|
||||||
const suspendedOrHidden = hidden || account.suspended;
|
const suspendedOrHidden = hidden || account.suspended;
|
||||||
const isLocal = !account.acct.includes('@');
|
const isLocal = !account.acct.includes('@');
|
||||||
const username = account.acct.split('@')[0];
|
|
||||||
const domain = isLocal ? localDomain : account.acct.split('@')[1];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='account-timeline__header'>
|
<div className='account-timeline__header'>
|
||||||
@ -133,38 +128,27 @@ export const AccountHeader: React.FC<{
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<AccountButtons
|
{!isRedesignEnabled() && (
|
||||||
accountId={accountId}
|
<AccountButtons
|
||||||
className='account__header__buttons--desktop'
|
accountId={accountId}
|
||||||
/>
|
className='account__header__buttons--desktop'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='account__header__tabs__name'>
|
<div
|
||||||
<h1>
|
className={classNames(
|
||||||
<DisplayName account={account} variant='simple' />
|
'account__header__tabs__name',
|
||||||
<small>
|
isRedesignEnabled() && redesignClasses.nameWrapper,
|
||||||
<span>
|
)}
|
||||||
@{username}
|
>
|
||||||
<span className='invisible'>@{domain}</span>
|
<AccountName
|
||||||
</span>
|
accountId={accountId}
|
||||||
<DomainPill
|
className={classNames(
|
||||||
username={username ?? ''}
|
isRedesignEnabled() && redesignClasses.name,
|
||||||
domain={domain ?? ''}
|
)}
|
||||||
isSelf={me === account.id}
|
/>
|
||||||
/>
|
{isRedesignEnabled() && <AccountButtons accountId={accountId} />}
|
||||||
{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>
|
</div>
|
||||||
|
|
||||||
<AccountBadges accountId={accountId} />
|
<AccountBadges accountId={accountId} />
|
||||||
|
|||||||
@ -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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -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 {
|
.fieldList {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user