Profile fields redesign (#37513)
This commit is contained in:
parent
918563704f
commit
047338e684
@ -11,11 +11,13 @@ import classes from './styles.module.css';
|
|||||||
|
|
||||||
interface MiniCardListProps {
|
interface MiniCardListProps {
|
||||||
cards?: (Pick<MiniCardProps, 'label' | 'value'> & { key?: Key })[];
|
cards?: (Pick<MiniCardProps, 'label' | 'value'> & { key?: Key })[];
|
||||||
|
className?: string;
|
||||||
onOverflowClick?: MouseEventHandler;
|
onOverflowClick?: MouseEventHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MiniCardList: FC<MiniCardListProps> = ({
|
export const MiniCardList: FC<MiniCardListProps> = ({
|
||||||
cards = [],
|
cards = [],
|
||||||
|
className,
|
||||||
onOverflowClick,
|
onOverflowClick,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
@ -27,29 +29,37 @@ export const MiniCardList: FC<MiniCardListProps> = ({
|
|||||||
maxWidth,
|
maxWidth,
|
||||||
} = useOverflow();
|
} = useOverflow();
|
||||||
|
|
||||||
|
if (!cards.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.wrapper} ref={wrapperRef}>
|
<div className={classNames(classes.wrapper, className)} ref={wrapperRef}>
|
||||||
<dl className={classes.list} ref={listRef} style={{ maxWidth }}>
|
<dl className={classes.list} ref={listRef} style={{ maxWidth }}>
|
||||||
{cards.map((card, index) => (
|
{cards.map((card, index) => (
|
||||||
<MiniCard
|
<MiniCard
|
||||||
key={card.key ?? index}
|
key={card.key ?? index}
|
||||||
label={card.label}
|
label={card.label}
|
||||||
value={card.value}
|
value={card.value}
|
||||||
hidden={index >= hiddenIndex}
|
hidden={hasOverflow && index >= hiddenIndex}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</dl>
|
</dl>
|
||||||
<button
|
{cards.length > 1 && (
|
||||||
type='button'
|
<div>
|
||||||
className={classNames(classes.more, !hasOverflow && classes.hidden)}
|
<button
|
||||||
onClick={onOverflowClick}
|
type='button'
|
||||||
>
|
className={classNames(classes.more, !hasOverflow && classes.hidden)}
|
||||||
<FormattedMessage
|
onClick={onOverflowClick}
|
||||||
id='minicard.more_items'
|
>
|
||||||
defaultMessage='+ {count} more'
|
<FormattedMessage
|
||||||
values={{ count: hiddenCount }}
|
id='minicard.more_items'
|
||||||
/>
|
defaultMessage='+{count}'
|
||||||
</button>
|
values={{ count: hiddenCount }}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,18 +7,6 @@ const meta = {
|
|||||||
title: 'Components/MiniCard',
|
title: 'Components/MiniCard',
|
||||||
component: MiniCardList,
|
component: MiniCardList,
|
||||||
args: {
|
args: {
|
||||||
cards: [
|
|
||||||
{ label: 'Pronouns', value: 'they/them' },
|
|
||||||
{
|
|
||||||
label: 'Website',
|
|
||||||
value: <a href='https://example.com'>bowie-the-db.meow</a>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Free playlists',
|
|
||||||
value: <a href='https://soundcloud.com/bowie-the-dj'>soundcloud.com</a>,
|
|
||||||
},
|
|
||||||
{ label: 'Location', value: 'Purris, France' },
|
|
||||||
],
|
|
||||||
onOverflowClick: action('Overflow clicked'),
|
onOverflowClick: action('Overflow clicked'),
|
||||||
},
|
},
|
||||||
render(args) {
|
render(args) {
|
||||||
@ -43,7 +31,22 @@ export default meta;
|
|||||||
|
|
||||||
type Story = StoryObj<typeof meta>;
|
type Story = StoryObj<typeof meta>;
|
||||||
|
|
||||||
export const Default: Story = {};
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
cards: [
|
||||||
|
{ label: 'Pronouns', value: 'they/them' },
|
||||||
|
{
|
||||||
|
label: 'Website',
|
||||||
|
value: <a href='https://example.com'>bowie-the-db.meow</a>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Free playlists',
|
||||||
|
value: <a href='https://soundcloud.com/bowie-the-dj'>soundcloud.com</a>,
|
||||||
|
},
|
||||||
|
{ label: 'Location', value: 'Purris, France' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const LongValue: Story = {
|
export const LongValue: Story = {
|
||||||
args: {
|
args: {
|
||||||
@ -60,3 +63,9 @@ export const LongValue: Story = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const OneCard: Story = {
|
||||||
|
args: {
|
||||||
|
cards: [{ label: 'Pronouns', value: 'they/them' }],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.list {
|
.list {
|
||||||
min-width: 0;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -21,16 +20,19 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
|
||||||
max-width: 20vw;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.more {
|
.more {
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
background: none;
|
background: none;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
height: 100%;
|
||||||
|
transition: all 300ms linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more:hover {
|
||||||
|
background-color: var(--color-bg-brand-softer);
|
||||||
|
color: var(--color-text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hidden {
|
.hidden {
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
import { isClientFeatureEnabled } from '@/mastodon/utils/environment';
|
||||||
|
|
||||||
|
export function isRedesignEnabled() {
|
||||||
|
return isClientFeatureEnabled('profile_redesign');
|
||||||
|
}
|
||||||
@ -1,18 +1,16 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { useIntl, FormattedMessage } from 'react-intl';
|
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 { AccountFields } from '@/mastodon/components/account_fields';
|
|
||||||
import { DisplayName } from '@/mastodon/components/display_name';
|
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 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 { FormattedDateWrapper } from 'mastodon/components/formatted_date';
|
|
||||||
import { Icon } from 'mastodon/components/icon';
|
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 { DomainPill } from 'mastodon/features/account/components/domain_pill';
|
||||||
@ -25,10 +23,11 @@ import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
|||||||
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';
|
||||||
|
import { AccountHeaderFields } from './fields';
|
||||||
import { AccountInfo } from './info';
|
import { AccountInfo } from './info';
|
||||||
import { AccountLinks } from './links';
|
|
||||||
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 { AccountTabs } from './tabs';
|
import { AccountTabs } from './tabs';
|
||||||
|
|
||||||
const titleFromAccount = (account: Account) => {
|
const titleFromAccount = (account: Account) => {
|
||||||
@ -192,32 +191,10 @@ export const AccountHeader: React.FC<{
|
|||||||
className='account__header__content'
|
className='account__header__content'
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='account__header__fields'>
|
<AccountHeaderFields accountId={accountId} />
|
||||||
<dl>
|
|
||||||
<dt>
|
|
||||||
<FormattedMessage
|
|
||||||
id='account.joined_short'
|
|
||||||
defaultMessage='Joined'
|
|
||||||
/>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
<FormattedDateWrapper
|
|
||||||
value={account.created_at}
|
|
||||||
year='numeric'
|
|
||||||
month='short'
|
|
||||||
day='2-digit'
|
|
||||||
/>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
<AccountFields
|
|
||||||
fields={account.fields}
|
|
||||||
emojis={account.emojis}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AccountLinks accountId={accountId} />
|
<AccountNumberFields accountId={accountId} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -0,0 +1,97 @@
|
|||||||
|
import { useCallback, useMemo } from 'react';
|
||||||
|
import type { FC } from 'react';
|
||||||
|
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import { openModal } from '@/mastodon/actions/modal';
|
||||||
|
import { AccountFields } from '@/mastodon/components/account_fields';
|
||||||
|
import { EmojiHTML } from '@/mastodon/components/emoji/html';
|
||||||
|
import { FormattedDateWrapper } from '@/mastodon/components/formatted_date';
|
||||||
|
import { MiniCardList } from '@/mastodon/components/mini_card/list';
|
||||||
|
import { useElementHandledLink } from '@/mastodon/components/status/handled_link';
|
||||||
|
import { useAccount } from '@/mastodon/hooks/useAccount';
|
||||||
|
import type { Account } from '@/mastodon/models/account';
|
||||||
|
import { useAppDispatch } from '@/mastodon/store';
|
||||||
|
|
||||||
|
import { isRedesignEnabled } from '../common';
|
||||||
|
|
||||||
|
import classes from './redesign.module.scss';
|
||||||
|
|
||||||
|
export const AccountHeaderFields: FC<{ accountId: string }> = ({
|
||||||
|
accountId,
|
||||||
|
}) => {
|
||||||
|
const account = useAccount(accountId);
|
||||||
|
|
||||||
|
if (!account) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRedesignEnabled()) {
|
||||||
|
return <RedesignAccountHeaderFields account={account} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='account__header__fields'>
|
||||||
|
<dl>
|
||||||
|
<dt>
|
||||||
|
<FormattedMessage id='account.joined_short' defaultMessage='Joined' />
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
<FormattedDateWrapper
|
||||||
|
value={account.created_at}
|
||||||
|
year='numeric'
|
||||||
|
month='short'
|
||||||
|
day='2-digit'
|
||||||
|
/>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<AccountFields fields={account.fields} emojis={account.emojis} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const RedesignAccountHeaderFields: FC<{ account: Account }> = ({ account }) => {
|
||||||
|
const htmlHandlers = useElementHandledLink();
|
||||||
|
const cards = useMemo(
|
||||||
|
() =>
|
||||||
|
account.fields.toArray().map(({ value_emojified, name_emojified }) => ({
|
||||||
|
label: (
|
||||||
|
<EmojiHTML
|
||||||
|
htmlString={name_emojified}
|
||||||
|
extraEmojis={account.emojis}
|
||||||
|
className='translate'
|
||||||
|
as='span'
|
||||||
|
{...htmlHandlers}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
value: (
|
||||||
|
<EmojiHTML
|
||||||
|
as='span'
|
||||||
|
htmlString={value_emojified}
|
||||||
|
extraEmojis={account.emojis}
|
||||||
|
{...htmlHandlers}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
})),
|
||||||
|
[account.emojis, account.fields, htmlHandlers],
|
||||||
|
);
|
||||||
|
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const handleOverflowClick = useCallback(() => {
|
||||||
|
dispatch(
|
||||||
|
openModal({
|
||||||
|
modalType: 'ACCOUNT_FIELDS',
|
||||||
|
modalProps: { accountId: account.id },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}, [account.id, dispatch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MiniCardList
|
||||||
|
cards={cards}
|
||||||
|
className={classes.fieldList}
|
||||||
|
onOverflowClick={handleOverflowClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
import type { FC } from 'react';
|
||||||
|
|
||||||
|
import { FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import { DisplayName } from '@/mastodon/components/display_name';
|
||||||
|
import { AnimateEmojiProvider } from '@/mastodon/components/emoji/context';
|
||||||
|
import { EmojiHTML } from '@/mastodon/components/emoji/html';
|
||||||
|
import { IconButton } from '@/mastodon/components/icon_button';
|
||||||
|
import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
|
||||||
|
import { useElementHandledLink } from '@/mastodon/components/status/handled_link';
|
||||||
|
import { useAccount } from '@/mastodon/hooks/useAccount';
|
||||||
|
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||||
|
|
||||||
|
import classes from './redesign.module.scss';
|
||||||
|
|
||||||
|
export const AccountFieldsModal: FC<{
|
||||||
|
accountId: string;
|
||||||
|
onClose: () => void;
|
||||||
|
}> = ({ accountId, onClose }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const account = useAccount(accountId);
|
||||||
|
const htmlHandlers = useElementHandledLink();
|
||||||
|
|
||||||
|
if (!account) {
|
||||||
|
return (
|
||||||
|
<div className='modal-root__modal dialog-modal'>
|
||||||
|
<LoadingIndicator />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='modal-root__modal dialog-modal'>
|
||||||
|
<div className='dialog-modal__header'>
|
||||||
|
<IconButton
|
||||||
|
icon='close'
|
||||||
|
className={classes.modalCloseButton}
|
||||||
|
onClick={onClose}
|
||||||
|
iconComponent={CloseIcon}
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id: 'account_fields_modal.close',
|
||||||
|
defaultMessage: 'Close',
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<span className={`${classes.modalTitle} dialog-modal__header__title`}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='account_fields_modal.title'
|
||||||
|
defaultMessage="{name}'s info"
|
||||||
|
values={{
|
||||||
|
name: <DisplayName account={account} variant='simple' />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className='dialog-modal__content'>
|
||||||
|
<AnimateEmojiProvider>
|
||||||
|
<dl className={classes.modalFieldsList}>
|
||||||
|
{account.fields.map((field, index) => (
|
||||||
|
<div key={index} className={classes.modalFieldItem}>
|
||||||
|
<EmojiHTML
|
||||||
|
as='dt'
|
||||||
|
htmlString={field.name_emojified}
|
||||||
|
extraEmojis={account.emojis}
|
||||||
|
className='translate'
|
||||||
|
{...htmlHandlers}
|
||||||
|
/>
|
||||||
|
<EmojiHTML
|
||||||
|
as='dd'
|
||||||
|
htmlString={field.value_emojified}
|
||||||
|
extraEmojis={account.emojis}
|
||||||
|
{...htmlHandlers}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</dl>
|
||||||
|
</AnimateEmojiProvider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,58 +0,0 @@
|
|||||||
import type { FC } from 'react';
|
|
||||||
|
|
||||||
import { useIntl } from 'react-intl';
|
|
||||||
|
|
||||||
import { NavLink } from 'react-router-dom';
|
|
||||||
|
|
||||||
import {
|
|
||||||
FollowersCounter,
|
|
||||||
FollowingCounter,
|
|
||||||
StatusesCounter,
|
|
||||||
} from '@/mastodon/components/counters';
|
|
||||||
import { ShortNumber } from '@/mastodon/components/short_number';
|
|
||||||
import { useAccount } from '@/mastodon/hooks/useAccount';
|
|
||||||
|
|
||||||
export const AccountLinks: FC<{ accountId: string }> = ({ accountId }) => {
|
|
||||||
const intl = useIntl();
|
|
||||||
const account = useAccount(accountId);
|
|
||||||
|
|
||||||
if (!account) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='account__header__extra__links'>
|
|
||||||
<NavLink
|
|
||||||
to={`/@${account.acct}`}
|
|
||||||
title={intl.formatNumber(account.statuses_count)}
|
|
||||||
>
|
|
||||||
<ShortNumber
|
|
||||||
value={account.statuses_count}
|
|
||||||
renderer={StatusesCounter}
|
|
||||||
/>
|
|
||||||
</NavLink>
|
|
||||||
|
|
||||||
<NavLink
|
|
||||||
exact
|
|
||||||
to={`/@${account.acct}/following`}
|
|
||||||
title={intl.formatNumber(account.following_count)}
|
|
||||||
>
|
|
||||||
<ShortNumber
|
|
||||||
value={account.following_count}
|
|
||||||
renderer={FollowingCounter}
|
|
||||||
/>
|
|
||||||
</NavLink>
|
|
||||||
|
|
||||||
<NavLink
|
|
||||||
exact
|
|
||||||
to={`/@${account.acct}/followers`}
|
|
||||||
title={intl.formatNumber(account.followers_count)}
|
|
||||||
>
|
|
||||||
<ShortNumber
|
|
||||||
value={account.followers_count}
|
|
||||||
renderer={FollowersCounter}
|
|
||||||
/>
|
|
||||||
</NavLink>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -0,0 +1,94 @@
|
|||||||
|
import type { FC } from 'react';
|
||||||
|
|
||||||
|
import { FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { NavLink } from 'react-router-dom';
|
||||||
|
|
||||||
|
import {
|
||||||
|
FollowersCounter,
|
||||||
|
FollowingCounter,
|
||||||
|
StatusesCounter,
|
||||||
|
} from '@/mastodon/components/counters';
|
||||||
|
import { FormattedDateWrapper } from '@/mastodon/components/formatted_date';
|
||||||
|
import { ShortNumber } from '@/mastodon/components/short_number';
|
||||||
|
import { useAccount } from '@/mastodon/hooks/useAccount';
|
||||||
|
|
||||||
|
import { isRedesignEnabled } from '../common';
|
||||||
|
|
||||||
|
import classes from './redesign.module.scss';
|
||||||
|
|
||||||
|
export const AccountNumberFields: FC<{ accountId: string }> = ({
|
||||||
|
accountId,
|
||||||
|
}) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const account = useAccount(accountId);
|
||||||
|
|
||||||
|
if (!account) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'account__header__extra__links',
|
||||||
|
isRedesignEnabled() && classes.fieldNumbersWrapper,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{!isRedesignEnabled() && (
|
||||||
|
<NavLink
|
||||||
|
to={`/@${account.acct}`}
|
||||||
|
title={intl.formatNumber(account.statuses_count)}
|
||||||
|
>
|
||||||
|
<ShortNumber
|
||||||
|
value={account.statuses_count}
|
||||||
|
renderer={StatusesCounter}
|
||||||
|
/>
|
||||||
|
</NavLink>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<NavLink
|
||||||
|
exact
|
||||||
|
to={`/@${account.acct}/following`}
|
||||||
|
title={intl.formatNumber(account.following_count)}
|
||||||
|
>
|
||||||
|
<ShortNumber
|
||||||
|
value={account.following_count}
|
||||||
|
renderer={FollowingCounter}
|
||||||
|
/>
|
||||||
|
</NavLink>
|
||||||
|
|
||||||
|
<NavLink
|
||||||
|
exact
|
||||||
|
to={`/@${account.acct}/followers`}
|
||||||
|
title={intl.formatNumber(account.followers_count)}
|
||||||
|
>
|
||||||
|
<ShortNumber
|
||||||
|
value={account.followers_count}
|
||||||
|
renderer={FollowersCounter}
|
||||||
|
/>
|
||||||
|
</NavLink>
|
||||||
|
|
||||||
|
{isRedesignEnabled() && (
|
||||||
|
<NavLink exact to={`/@${account.acct}`}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='account.joined_long'
|
||||||
|
defaultMessage='Joined on {date}'
|
||||||
|
values={{
|
||||||
|
date: (
|
||||||
|
<strong>
|
||||||
|
<FormattedDateWrapper
|
||||||
|
value={account.created_at}
|
||||||
|
year='numeric'
|
||||||
|
month='short'
|
||||||
|
day='2-digit'
|
||||||
|
/>
|
||||||
|
</strong>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</NavLink>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
.fieldList {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fieldNumbersWrapper {
|
||||||
|
a {
|
||||||
|
font-weight: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalCloseButton {
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalTitle {
|
||||||
|
flex-grow: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalFieldsList {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalFieldItem {
|
||||||
|
&:not(:first-child) {
|
||||||
|
padding-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-child)::after {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -85,6 +85,7 @@ export const MODAL_COMPONENTS = {
|
|||||||
'IGNORE_NOTIFICATIONS': IgnoreNotificationsModal,
|
'IGNORE_NOTIFICATIONS': IgnoreNotificationsModal,
|
||||||
'ANNUAL_REPORT': AnnualReportModal,
|
'ANNUAL_REPORT': AnnualReportModal,
|
||||||
'COMPOSE_PRIVACY': () => Promise.resolve({ default: VisibilityModal }),
|
'COMPOSE_PRIVACY': () => Promise.resolve({ default: VisibilityModal }),
|
||||||
|
'ACCOUNT_FIELDS': () => import('mastodon/features/account_timeline/components/fields_modal.tsx').then(module => ({ default: module.AccountFieldsModal })),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class ModalRoot extends PureComponent {
|
export default class ModalRoot extends PureComponent {
|
||||||
|
|||||||
@ -57,6 +57,7 @@
|
|||||||
"account.go_to_profile": "Go to profile",
|
"account.go_to_profile": "Go to profile",
|
||||||
"account.hide_reblogs": "Hide boosts from @{name}",
|
"account.hide_reblogs": "Hide boosts from @{name}",
|
||||||
"account.in_memoriam": "In Memoriam.",
|
"account.in_memoriam": "In Memoriam.",
|
||||||
|
"account.joined_long": "Joined on {date}",
|
||||||
"account.joined_short": "Joined",
|
"account.joined_short": "Joined",
|
||||||
"account.languages": "Change subscribed languages",
|
"account.languages": "Change subscribed languages",
|
||||||
"account.link_verified_on": "Ownership of this link was checked on {date}",
|
"account.link_verified_on": "Ownership of this link was checked on {date}",
|
||||||
@ -90,6 +91,8 @@
|
|||||||
"account.unmute": "Unmute @{name}",
|
"account.unmute": "Unmute @{name}",
|
||||||
"account.unmute_notifications_short": "Unmute notifications",
|
"account.unmute_notifications_short": "Unmute notifications",
|
||||||
"account.unmute_short": "Unmute",
|
"account.unmute_short": "Unmute",
|
||||||
|
"account_fields_modal.close": "Close",
|
||||||
|
"account_fields_modal.title": "{name}'s info",
|
||||||
"account_note.placeholder": "Click to add note",
|
"account_note.placeholder": "Click to add note",
|
||||||
"admin.dashboard.daily_retention": "User retention rate by day after sign-up",
|
"admin.dashboard.daily_retention": "User retention rate by day after sign-up",
|
||||||
"admin.dashboard.monthly_retention": "User retention rate by month after sign-up",
|
"admin.dashboard.monthly_retention": "User retention rate by month after sign-up",
|
||||||
@ -589,7 +592,7 @@
|
|||||||
"load_pending": "{count, plural, one {# new item} other {# new items}}",
|
"load_pending": "{count, plural, one {# new item} other {# new items}}",
|
||||||
"loading_indicator.label": "Loading…",
|
"loading_indicator.label": "Loading…",
|
||||||
"media_gallery.hide": "Hide",
|
"media_gallery.hide": "Hide",
|
||||||
"minicard.more_items": "+ {count} more",
|
"minicard.more_items": "+{count}",
|
||||||
"moved_to_account_banner.text": "Your account {disabledAccount} is currently disabled because you moved to {movedToAccount}.",
|
"moved_to_account_banner.text": "Your account {disabledAccount} is currently disabled because you moved to {movedToAccount}.",
|
||||||
"mute_modal.hide_from_notifications": "Hide from notifications",
|
"mute_modal.hide_from_notifications": "Hide from notifications",
|
||||||
"mute_modal.hide_options": "Hide options",
|
"mute_modal.hide_options": "Hide options",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user