Profile redesign: Remove feature flag (#38513)
Some checks are pending
Check i18n / check-i18n (push) Waiting to run
Chromatic / Check for relevant changes (push) Waiting to run
Chromatic / Run Chromatic (push) Blocked by required conditions
CodeQL / Analyze (actions) (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
CodeQL / Analyze (ruby) (push) Waiting to run
Crowdin / Upload translations / upload-translations (push) Waiting to run
Check formatting / lint (push) Waiting to run
CSS Linting / lint (push) Waiting to run
JavaScript Linting / lint (push) Waiting to run
Ruby Linting / lint (push) Waiting to run
JavaScript Testing / test (push) Waiting to run
Historical data migration test / test (14-alpine) (push) Waiting to run
Historical data migration test / test (15-alpine) (push) Waiting to run
Historical data migration test / test (16-alpine) (push) Waiting to run
Historical data migration test / test (17-alpine) (push) Waiting to run
Ruby Testing / build (production) (push) Waiting to run
Ruby Testing / build (test) (push) Waiting to run
Ruby Testing / test (.ruby-version) (push) Blocked by required conditions
Ruby Testing / test (3.3) (push) Blocked by required conditions
Ruby Testing / test (3.4) (push) Blocked by required conditions
Ruby Testing / End to End testing (.ruby-version) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.3) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.4) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:7.17.29) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:8.19.2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, opensearchproject/opensearch:2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.3, docker.elastic.co/elasticsearch/elasticsearch:7.17.29) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.4, docker.elastic.co/elasticsearch/elasticsearch:7.17.29) (push) Blocked by required conditions

This commit is contained in:
Echo 2026-04-01 17:03:43 +02:00 committed by GitHub
parent 159d710bc1
commit ca5c0a144a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 67 additions and 628 deletions

View File

@ -6,7 +6,6 @@ import classNames from 'classnames';
import { Link } from 'react-router-dom';
import { useIdentity } from '@/mastodon/identity_context';
import { isServerFeatureEnabled } from '@/mastodon/utils/environment';
import {
fetchRelationships,
followAccount,
@ -171,23 +170,10 @@ export const FollowButton: React.FC<{
'button--compact': compact,
});
if (isServerFeatureEnabled('profile_redesign')) {
return (
<Link to='/profile/edit' className={buttonClasses}>
{label}
</Link>
);
}
return (
<a
href='/settings/profile'
target='_blank'
rel='noopener'
className={buttonClasses}
>
<Link to='/profile/edit' className={buttonClasses}>
{label}
</a>
</Link>
);
}

View File

@ -7,7 +7,6 @@ import { useHistory } from 'react-router';
import { List as ImmutableList } from 'immutable';
import { useAccount } from '@/mastodon/hooks/useAccount';
import { isServerFeatureEnabled } from '@/mastodon/utils/environment';
import { fetchEndorsedAccounts } from 'mastodon/actions/accounts';
import { fetchFeaturedTags } from 'mastodon/actions/featured_tags';
import { Account } from 'mastodon/components/account';
@ -49,11 +48,7 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
const history = useHistory();
useEffect(() => {
if (
account &&
!account.show_featured &&
isServerFeatureEnabled('profile_redesign')
) {
if (account && !account.show_featured) {
history.push(`/@${account.acct}`);
}
}, [account, history]);
@ -111,8 +106,7 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
);
}
const noTags =
featuredTags.isEmpty() || isServerFeatureEnabled('profile_redesign');
const noTags = featuredTags.isEmpty();
if (
noTags &&

View File

@ -4,7 +4,6 @@ import { FormattedMessage } from 'react-intl';
import { List as ImmutableList, isList } from 'immutable';
import { isServerFeatureEnabled } from '@/mastodon/utils/environment';
import { openModal } from 'mastodon/actions/modal';
import { expandAccountMediaTimeline } from 'mastodon/actions/timelines';
import { ColumnBackButton } from 'mastodon/components/column_back_button';
@ -27,8 +26,6 @@ import { MediaItem } from './components/media_item';
const emptyList = ImmutableList<MediaAttachment>();
const redesignEnabled = isServerFeatureEnabled('profile_redesign');
const selectGalleryTimeline = createAppSelector(
[
(_state, accountId?: string | null) => accountId,
@ -58,7 +55,7 @@ const selectGalleryTimeline = createAppSelector(
const { show_media, show_media_replies } = account;
// If the account disabled showing media, don't display anything.
if (!show_media && redesignEnabled) {
if (!show_media) {
return {
items,
hasMore: false,
@ -67,7 +64,7 @@ const selectGalleryTimeline = createAppSelector(
};
}
const withReplies = show_media_replies && redesignEnabled;
const withReplies = show_media_replies;
const timeline = timelines.get(
`account:${accountId}:media${withReplies ? ':with_replies' : ''}`,
);

View File

@ -1,9 +1,4 @@
import type { AccountFieldShape } from '@/mastodon/models/account';
import { isServerFeatureEnabled } from '@/mastodon/utils/environment';
export function isRedesignEnabled() {
return isServerFeatureEnabled('profile_redesign');
}
export interface AccountField extends AccountFieldShape {
nameHasEmojis: boolean;

View File

@ -7,7 +7,6 @@ import { openModal } from '@/mastodon/actions/modal';
import { AccountBio } from '@/mastodon/components/account_bio';
import { Avatar } from '@/mastodon/components/avatar';
import { AnimateEmojiProvider } from '@/mastodon/components/emoji/context';
import { AccountNote } from '@/mastodon/features/account/components/account_note';
import FollowRequestNoteContainer from '@/mastodon/features/account/containers/follow_request_note_container';
import { useLayout } from '@/mastodon/hooks/useLayout';
import { useVisibility } from '@/mastodon/hooks/useVisibility';
@ -20,15 +19,12 @@ 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 { AccountSubscriptionForm } from './account_subscription_form';
import { AccountBadges } from './badges';
import { AccountButtons } from './buttons';
import { FamiliarFollowers } from './familiar_followers';
import { AccountHeaderFields } from './fields';
import { AccountInfo } from './info';
import { MemorialNote } from './memorial_note';
import { MovedNote } from './moved_note';
import { AccountNote as AccountNoteRedesign } from './note';
@ -52,8 +48,6 @@ export const AccountHeader: React.FC<{
accountId: string;
hideTabs?: boolean;
}> = ({ accountId, hideTabs }) => {
const isRedesign = isRedesignEnabled();
const dispatch = useAppDispatch();
const account = useAppSelector((state) => state.accounts.get(accountId));
const relationship = useAppSelector((state) =>
@ -120,13 +114,9 @@ export const AccountHeader: React.FC<{
<div
className={classNames(
'account__header__image',
isRedesign && redesignClasses.header,
redesignClasses.header,
)}
>
{me !== account.id && relationship && !isRedesign && (
<AccountInfo relationship={relationship} />
)}
{!suspendedOrHidden && (
<img
src={autoPlayGif ? account.header : account.header_static}
@ -139,13 +129,13 @@ export const AccountHeader: React.FC<{
<div
className={classNames(
'account__header__bar',
isRedesign && redesignClasses.barWrapper,
redesignClasses.barWrapper,
)}
>
<div
className={classNames(
'account__header__tabs',
isRedesign && redesignClasses.avatarWrapper,
redesignClasses.avatarWrapper,
)}
>
<a
@ -157,33 +147,24 @@ export const AccountHeader: React.FC<{
>
<Avatar
account={suspendedOrHidden ? undefined : account}
size={isRedesign ? 80 : 92}
size={80}
/>
</a>
{!isRedesign && (
<AccountButtons
accountId={accountId}
className='account__header__buttons--desktop'
/>
)}
</div>
<div
className={classNames(
'account__header__tabs__name',
isRedesign && redesignClasses.nameWrapper,
redesignClasses.nameWrapper,
)}
>
<AccountName accountId={accountId} />
{isRedesign && (
<AccountButtons
accountId={accountId}
className={redesignClasses.buttonsDesktop}
noShare={!isMe || 'share' in navigator}
forceMenu={'share' in navigator}
/>
)}
<AccountButtons
accountId={accountId}
className={redesignClasses.buttonsDesktop}
noShare={!isMe || 'share' in navigator}
forceMenu={'share' in navigator}
/>
</div>
<AccountBadges accountId={accountId} />
@ -192,31 +173,19 @@ export const AccountHeader: React.FC<{
<FamiliarFollowers accountId={accountId} />
)}
{!isRedesign && (
<AccountButtons
className='account__header__buttons--mobile'
accountId={accountId}
noShare
/>
)}
{!suspendedOrHidden && (
<div className='account__header__extra'>
<div className='account__header__bio'>
{me &&
account.id !== me &&
(isRedesign ? (
<AccountNoteRedesign accountId={accountId} />
) : (
<AccountNote accountId={accountId} />
))}
{me && account.id !== me && (
<AccountNoteRedesign accountId={accountId} />
)}
<AccountBio
showDropdown
accountId={accountId}
className={classNames(
'account__header__content',
isRedesign && redesignClasses.bio,
redesignClasses.bio,
)}
/>
@ -231,20 +200,18 @@ export const AccountHeader: React.FC<{
</div>
)}
{isRedesign && (
<AccountButtons
className={classNames(
redesignClasses.buttonsMobile,
!isIntersecting && redesignClasses.buttonsMobileIsStuck,
)}
accountId={accountId}
noShare
/>
)}
<AccountButtons
className={classNames(
redesignClasses.buttonsMobile,
!isIntersecting && redesignClasses.buttonsMobileIsStuck,
)}
accountId={accountId}
noShare
/>
</div>
</AnimateEmojiProvider>
{!hideTabs && !hidden && <AccountTabs acct={account.acct} />}
{!hideTabs && !hidden && <AccountTabs />}
<div ref={observedRef} />
<Helmet>

View File

@ -14,10 +14,6 @@ import { useAppSelector } from '@/mastodon/store';
import AtIcon from '@/material-icons/400-24px/alternate_email.svg?react';
import HelpIcon from '@/material-icons/400-24px/help.svg?react';
import DomainIcon from '@/material-icons/400-24px/language.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';
@ -34,7 +30,6 @@ const messages = defineMessages({
});
export const AccountName: FC<{ accountId: string }> = ({ accountId }) => {
const intl = useIntl();
const account = useAccount(accountId);
const me = useAppSelector((state) => state.meta.get('me') as string);
const localDomain = useAppSelector(
@ -47,32 +42,6 @@ export const AccountName: FC<{ accountId: string }> = ({ accountId }) => {
const [username = '', domain = localDomain] = account.acct.split('@');
if (!isRedesignEnabled()) {
return (
<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(messages.lockedInfo)}
/>
)}
</small>
</h1>
);
}
return (
<div className={classes.name}>
<h1>

View File

@ -20,8 +20,6 @@ import { useAccount } from '@/mastodon/hooks/useAccount';
import type { AccountRole } from '@/mastodon/models/account';
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
import { isRedesignEnabled } from '../common';
import classes from './redesign.module.scss';
export const AccountBadges: FC<{ accountId: string }> = ({ accountId }) => {
@ -46,9 +44,6 @@ export const AccountBadges: FC<{ accountId: string }> = ({ accountId }) => {
return null;
}
const isRedesign = isRedesignEnabled();
const className = isRedesign ? classes.badge : '';
const domain = account.acct.includes('@')
? account.acct.split('@')[1]
: localDomain;
@ -58,7 +53,7 @@ export const AccountBadges: FC<{ accountId: string }> = ({ accountId }) => {
<AdminBadge
key={role.id}
label={role.name}
className={className}
className={classes.badge}
domain={`(${domain})`}
roleId={role.id}
/>,
@ -68,8 +63,8 @@ export const AccountBadges: FC<{ accountId: string }> = ({ accountId }) => {
<Badge
key={role.id}
label={role.name}
className={className}
domain={isRedesign ? `(${domain})` : domain}
className={classes.badge}
domain={`(${domain})`}
roleId={role.id}
/>,
);
@ -77,17 +72,17 @@ export const AccountBadges: FC<{ accountId: string }> = ({ accountId }) => {
});
if (account.bot) {
badges.push(<AutomatedBadge key='bot-badge' className={className} />);
badges.push(<AutomatedBadge key='bot-badge' className={classes.badge} />);
}
if (account.group) {
badges.push(<GroupBadge key='group-badge' className={className} />);
badges.push(<GroupBadge key='group-badge' className={classes.badge} />);
}
if (isRedesign && relationship) {
if (relationship) {
if (relationship.blocking) {
badges.push(
<BlockedBadge
key='blocking'
className={classNames(className, classes.badgeBlocked)}
className={classNames(classes.badge, classes.badgeBlocked)}
/>,
);
}
@ -95,7 +90,7 @@ export const AccountBadges: FC<{ accountId: string }> = ({ accountId }) => {
badges.push(
<BlockedBadge
key='domain-blocking'
className={classNames(className, classes.badgeBlocked)}
className={classNames(classes.badge, classes.badgeBlocked)}
domain={domain}
label={
<FormattedMessage
@ -110,7 +105,7 @@ export const AccountBadges: FC<{ accountId: string }> = ({ accountId }) => {
badges.push(
<MutedBadge
key='muted-badge'
className={classNames(className, classes.badgeMuted)}
className={classNames(classes.badge, classes.badgeMuted)}
expiresAt={relationship.muting_expires_at}
/>,
);
@ -136,5 +131,5 @@ export const PinnedBadge: FC = () => (
function isAdminBadge(role: AccountRole) {
const name = role.name.toLowerCase();
return isRedesignEnabled() && (name === 'admin' || name === 'owner');
return name === 'admin' || name === 'owner';
}

View File

@ -16,8 +16,6 @@ import NotificationsIcon from '@/material-icons/400-24px/notifications.svg?react
import NotificationsActiveIcon from '@/material-icons/400-24px/notifications_active-fill.svg?react';
import ShareIcon from '@/material-icons/400-24px/share.svg?react';
import { isRedesignEnabled } from '../common';
import { AccountMenu } from './menu';
const messages = defineMessages({
@ -97,7 +95,6 @@ const AccountButtonsOther: FC<
accountId={accountId}
className='account__header__follow-button'
labelLength='long'
withUnmute={!isRedesignEnabled()}
/>
)}
{isFollowing && (

View File

@ -1,68 +1,30 @@
import { useCallback, useMemo, useRef, useState } from 'react';
import type { FC } from 'react';
import { defineMessage, FormattedMessage, useIntl } from 'react-intl';
import { defineMessage, useIntl } from 'react-intl';
import classNames from 'classnames';
import IconVerified from '@/images/icons/icon_verified.svg?react';
import { openModal } from '@/mastodon/actions/modal';
import { AccountFields } from '@/mastodon/components/account_fields';
import { CustomEmojiProvider } from '@/mastodon/components/emoji/context';
import type { EmojiHTMLProps } from '@/mastodon/components/emoji/html';
import { EmojiHTML } from '@/mastodon/components/emoji/html';
import { FormattedDateWrapper } from '@/mastodon/components/formatted_date';
import { Icon } from '@/mastodon/components/icon';
import { IconButton } from '@/mastodon/components/icon_button';
import { MiniCard } from '@/mastodon/components/mini_card';
import { useElementHandledLink } from '@/mastodon/components/status/handled_link';
import { useAccount } from '@/mastodon/hooks/useAccount';
import { useResizeObserver } from '@/mastodon/hooks/useObserver';
import type { Account } from '@/mastodon/models/account';
import { useAppDispatch } from '@/mastodon/store';
import MoreIcon from '@/material-icons/400-24px/more_horiz.svg?react';
import { cleanExtraEmojis } from '../../emoji/normalize';
import type { AccountField } from '../common';
import { isRedesignEnabled } from '../common';
import { useFieldHtml } from '../hooks/useFieldHtml';
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 verifyMessage = defineMessage({
id: 'account.link_verified_on',
defaultMessage: 'Ownership of this link was checked on {date}',
@ -75,13 +37,22 @@ const dateFormatOptions: Intl.DateTimeFormatOptions = {
minute: '2-digit',
};
const RedesignAccountHeaderFields: FC<{ account: Account }> = ({ account }) => {
export const AccountHeaderFields: FC<{ accountId: string }> = ({
accountId,
}) => {
const account = useAccount(accountId);
const emojis = useMemo(
() => cleanExtraEmojis(account.emojis),
[account.emojis],
() => cleanExtraEmojis(account?.emojis),
[account?.emojis],
);
const accountFields = account?.fields;
const fields: AccountField[] = useMemo(() => {
const fields = account.fields.toJS();
const fields = accountFields?.toJS();
if (!fields) {
return [];
}
if (!emojis) {
return fields.map((field) => ({
...field,
@ -102,10 +73,10 @@ const RedesignAccountHeaderFields: FC<{ account: Account }> = ({ account }) => {
field.value_plain?.includes(`:${code}:`),
),
}));
}, [account.fields, emojis]);
}, [accountFields, emojis]);
const htmlHandlers = useElementHandledLink({
hashtagAccountId: account.id,
hashtagAccountId: account?.id,
});
const { wrapperRef } = useColumnWrap();

View File

@ -40,8 +40,6 @@ import PersonRemoveIcon from '@/material-icons/400-24px/person_remove.svg?react'
import ReportIcon from '@/material-icons/400-24px/report.svg?react';
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 }) => {
@ -63,19 +61,9 @@ export const AccountMenu: FC<{ accountId: string }> = ({ accountId }) => {
return [];
}
if (isRedesignEnabled()) {
return redesignMenuItems({
account,
signedIn: !isMe && signedIn,
permissions,
intl,
relationship,
dispatch,
});
}
return currentMenuItems({
return redesignMenuItems({
account,
signedIn,
signedIn: !isMe && signedIn,
permissions,
intl,
relationship,
@ -178,270 +166,6 @@ const messages = defineMessages({
},
});
function currentMenuItems({
account,
signedIn,
permissions,
intl,
relationship,
dispatch,
}: MenuItemsParams): MenuItem[] {
const items: MenuItem[] = [];
const isRemote = account.acct !== account.username;
if (signedIn && !account.suspended) {
items.push(
{
text: intl.formatMessage(messages.mention, {
name: account.username,
}),
action: () => {
dispatch(mentionCompose(account));
},
},
{
text: intl.formatMessage(messages.direct, {
name: account.username,
}),
action: () => {
dispatch(directCompose(account));
},
},
null,
);
}
if (isRemote) {
items.push({
text: intl.formatMessage(messages.openOriginalPage),
href: account.url,
});
if (signedIn) {
items.push(null);
}
}
if (!signedIn) {
return items;
}
if (relationship?.following) {
// Timeline options
if (!relationship.muting) {
if (relationship.showing_reblogs) {
items.push({
text: intl.formatMessage(messages.hideReblogs, {
name: account.username,
}),
action: () => {
dispatch(followAccount(account.id, { reblogs: false }));
},
});
} else {
items.push({
text: intl.formatMessage(messages.showReblogs, {
name: account.username,
}),
action: () => {
dispatch(followAccount(account.id, { reblogs: true }));
},
});
}
items.push(
{
text: intl.formatMessage(messages.languages),
action: () => {
dispatch(
openModal({
modalType: 'SUBSCRIBED_LANGUAGES',
modalProps: {
accountId: account.id,
},
}),
);
},
},
null,
);
}
items.push(
{
text: intl.formatMessage(
relationship.endorsed ? messages.unendorse : messages.endorse,
),
action: () => {
if (relationship.endorsed) {
dispatch(unpinAccount(account.id));
} else {
dispatch(pinAccount(account.id));
}
},
},
{
text: intl.formatMessage(messages.add_or_remove_from_list),
action: () => {
dispatch(
openModal({
modalType: 'LIST_ADDER',
modalProps: {
accountId: account.id,
},
}),
);
},
},
null,
);
}
if (relationship?.followed_by) {
const handleRemoveFromFollowers = () => {
dispatch(
openModal({
modalType: 'CONFIRM',
modalProps: {
title: intl.formatMessage(messages.confirmRemoveFromFollowersTitle),
message: intl.formatMessage(
messages.confirmRemoveFromFollowersMessage,
{ name: <strong>{account.acct}</strong> },
),
confirm: intl.formatMessage(
messages.confirmRemoveFromFollowersButton,
),
onConfirm: () => {
void dispatch(
removeAccountFromFollowers({ accountId: account.id }),
);
},
},
}),
);
};
items.push({
text: intl.formatMessage(messages.removeFromFollowers, {
name: account.username,
}),
action: handleRemoveFromFollowers,
dangerous: true,
});
}
if (relationship?.muting) {
items.push({
text: intl.formatMessage(messages.unmute, {
name: account.username,
}),
action: () => {
dispatch(unmuteAccount(account.id));
},
});
} else {
items.push({
text: intl.formatMessage(messages.mute, {
name: account.username,
}),
action: () => {
dispatch(initMuteModal(account));
},
dangerous: true,
});
}
if (relationship?.blocking) {
items.push({
text: intl.formatMessage(messages.unblock, {
name: account.username,
}),
action: () => {
dispatch(unblockAccount(account.id));
},
});
} else {
items.push({
text: intl.formatMessage(messages.block, {
name: account.username,
}),
action: () => {
dispatch(blockAccount(account.id));
},
dangerous: true,
});
}
if (!account.suspended) {
items.push({
text: intl.formatMessage(messages.report, {
name: account.username,
}),
action: () => {
dispatch(initReport(account));
},
dangerous: true,
});
}
const remoteDomain = isRemote ? account.acct.split('@')[1] : null;
if (remoteDomain) {
items.push(null);
if (relationship?.domain_blocking) {
items.push({
text: intl.formatMessage(messages.unblockDomain, {
domain: remoteDomain,
}),
action: () => {
dispatch(unblockDomain(remoteDomain));
},
});
} else {
items.push({
text: intl.formatMessage(messages.blockDomain, {
domain: remoteDomain,
}),
action: () => {
dispatch(initDomainBlockModal(account));
},
dangerous: true,
});
}
}
if (
(permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS ||
(isRemote &&
(permissions & PERMISSION_MANAGE_FEDERATION) ===
PERMISSION_MANAGE_FEDERATION)
) {
items.push(null);
if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
items.push({
text: intl.formatMessage(messages.admin_account, {
name: account.username,
}),
href: `/admin/accounts/${account.id}`,
});
}
if (
isRemote &&
(permissions & PERMISSION_MANAGE_FEDERATION) ===
PERMISSION_MANAGE_FEDERATION
) {
items.push({
text: intl.formatMessage(messages.admin_domain, {
domain: remoteDomain,
}),
href: `/admin/instances/${remoteDomain}`,
});
}
}
return items;
}
const redesignMessages = defineMessages({
share: { id: 'account.menu.share', defaultMessage: 'Share…' },
copy: { id: 'account.menu.copy', defaultMessage: 'Copy link' },

View File

@ -3,13 +3,6 @@ import type { FC } from 'react';
import { FormattedMessage, useIntl } from 'react-intl';
import { NavLink } from 'react-router-dom';
import {
FollowersCounter,
FollowingCounter,
StatusesCounter,
} from '@/mastodon/components/counters';
import { FormattedDateWrapper } from '@/mastodon/components/formatted_date';
import {
NumberFields,
@ -18,54 +11,9 @@ import {
import { ShortNumber } from '@/mastodon/components/short_number';
import { useAccount } from '@/mastodon/hooks/useAccount';
import { isRedesignEnabled } from '../common';
const LegacyNumberFields: 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>
);
};
const RedesignNumberFields: FC<{ accountId: string }> = ({ accountId }) => {
export const AccountNumberFields: FC<{ accountId: string }> = ({
accountId,
}) => {
const intl = useIntl();
const account = useAccount(accountId);
const createdThisYear = useMemo(
@ -125,7 +73,3 @@ const RedesignNumberFields: FC<{ accountId: string }> = ({ accountId }) => {
</NumberFields>
);
};
export const AccountNumberFields = isRedesignEnabled()
? RedesignNumberFields
: LegacyNumberFields;

View File

@ -8,40 +8,13 @@ import { NavLink } from 'react-router-dom';
import { useAccount } from '@/mastodon/hooks/useAccount';
import { useAccountId } from '@/mastodon/hooks/useAccountId';
import { isRedesignEnabled } from '../common';
import classes from './redesign.module.scss';
export const AccountTabs: FC<{ acct: string }> = ({ acct }) => {
if (isRedesignEnabled()) {
return <RedesignTabs />;
}
return (
<div className='account__section-headline'>
<NavLink exact to={`/@${acct}/featured`}>
<FormattedMessage id='account.featured' defaultMessage='Featured' />
</NavLink>
<NavLink exact to={`/@${acct}`}>
<FormattedMessage id='account.posts' defaultMessage='Posts' />
</NavLink>
<NavLink exact to={`/@${acct}/with_replies`}>
<FormattedMessage
id='account.posts_with_replies'
defaultMessage='Posts and replies'
/>
</NavLink>
<NavLink exact to={`/@${acct}/media`}>
<FormattedMessage id='account.media' defaultMessage='Media' />
</NavLink>
</div>
);
};
const isActive: Required<NavLinkProps>['isActive'] = (match, location) =>
match?.url === location.pathname ||
(!!match?.url && location.pathname.startsWith(`${match.url}/tagged/`));
const RedesignTabs: FC = () => {
export const AccountTabs: FC = () => {
const accountId = useAccountId();
const account = useAccount(accountId);

View File

@ -23,7 +23,7 @@ export const AccountFilters: FC = () => {
}
return (
<>
<AccountTabs acct={acct} />
<AccountTabs />
<div className={classes.filtersWrapper}>
<FilterDropdown />
</div>

View File

@ -18,7 +18,6 @@ import type { StatusHeaderRenderFn } from '@/mastodon/components/status/header';
import { selectTimelineByKey } from '@/mastodon/selectors/timelines';
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
import { isRedesignEnabled } from '../common';
import { PinnedBadge } from '../components/badges';
import { useAccountContext } from './context';
@ -88,10 +87,6 @@ export const renderPinnedStatusHeader: StatusHeaderRenderFn = ({
export const PinnedShowAllButton: FC = () => {
const { onShowAllPinned } = useAccountContext();
if (!isRedesignEnabled()) {
return null;
}
return (
<Button
onClick={onShowAllPinned}

View File

@ -24,7 +24,6 @@ import { identityContextPropShape, withIdentity } from 'mastodon/identity_contex
import { layoutFromWindow } from 'mastodon/is_mobile';
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
import { checkAnnualReport } from '@/mastodon/reducers/slices/annual_report';
import { isServerFeatureEnabled } from '@/mastodon/utils/environment';
import { uploadCompose, resetCompose, changeComposeSpoilerness } from '../../actions/compose';
import { clearHeight } from '../../actions/height_cache';
@ -182,20 +181,6 @@ class SwitchingColumnsArea extends PureComponent {
rootRedirect = '/about';
}
const profileRedesignRoutes = [];
if (isServerFeatureEnabled('profile_redesign')) {
profileRedesignRoutes.push(
<WrappedRoute key="edit" path='/profile/edit' component={AccountEdit} content={children} />,
<WrappedRoute key="featured_tags" path='/profile/featured_tags' component={AccountEditFeaturedTags} content={children} />
)
} else {
// If profile editing is not enabled, redirect to the home timeline as the current editing pages are outside React Router.
profileRedesignRoutes.push(
<Redirect key="edit-redirect" from='/profile/edit' to='/' exact />,
<Redirect key="featured-tags-redirect" from='/profile/featured_tags' to='/' exact />,
);
}
return (
<ColumnsContextProvider multiColumn={!singleColumn}>
<ColumnsArea ref={this.setRef} singleColumn={singleColumn}>
@ -242,7 +227,8 @@ class SwitchingColumnsArea extends PureComponent {
<WrappedRoute path='/search' component={Search} content={children} />
<WrappedRoute path={['/publish', '/statuses/new']} component={Compose} content={children} />
{...profileRedesignRoutes}
<WrappedRoute path='/profile/edit' component={AccountEdit} content={children} />
<WrappedRoute path='/profile/featured_tags' component={AccountEditFeaturedTags} content={children} />
<WrappedRoute path={['/@:acct', '/accounts/:id']} exact component={AccountTimeline} content={children} />
<WrappedRoute path={['/@:acct/featured', '/accounts/:id/featured']} component={AccountFeatured} content={children} />

View File

@ -1,5 +1,3 @@
import { isServerFeatureEnabled } from '@/mastodon/utils/environment';
export function EmojiPicker () {
return import('../../emoji/emoji_picker');
}
@ -79,10 +77,7 @@ export function PinnedStatuses () {
}
export function AccountTimeline () {
if (isServerFeatureEnabled('profile_redesign')) {
return import('../../account_timeline/v2');
}
return import('../../account_timeline');
return import('../../account_timeline/v2');
}
export function AccountGallery () {

View File

@ -122,7 +122,6 @@
"account.note.title": "Personal note (visible only to you)",
"account.open_original_page": "Open original page",
"account.posts": "Posts",
"account.posts_with_replies": "Posts and replies",
"account.remove_from_followers": "Remove {name} from followers",
"account.report": "Report @{name}",
"account.requested_follow": "{name} has requested to follow you",

View File

@ -12,7 +12,7 @@ export function isProduction() {
else return import.meta.env.PROD;
}
export type ServerFeatures = 'fasp' | 'collections' | 'profile_redesign';
export type ServerFeatures = 'fasp' | 'collections';
export function isServerFeatureEnabled(feature: ServerFeatures) {
return initialState?.features.includes(feature) ?? false;

View File

@ -1,48 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Account notes', :inline_jobs, :js, :streaming do
include ProfileStories
let(:email) { 'test@example.com' }
let(:password) { 'password' }
let(:confirmed_at) { Time.zone.now }
let(:finished_onboarding) { true }
let!(:other_account) { Fabricate(:account) }
let(:note_text) { 'This is a personal note' }
before { as_a_logged_in_user }
it 'can be written and viewed' do
visit_profile(other_account)
fill_in frontend_translations('account_note.placeholder'), with: note_text
# This is a bit awkward since there is no button to save the change
# The easiest way is to send ctrl+enter ourselves
find_field(class: 'account__header__account-note__content').send_keys [:control, :enter]
expect(page)
.to have_css('.account__header__account-note__content', text: note_text)
# Navigate back and forth and ensure the comment is still here
visit root_url
visit_profile(other_account)
expect(AccountNote.find_by(account: bob.account, target_account: other_account).comment)
.to eq note_text
expect(page)
.to have_css('.account__header__account-note__content', text: note_text)
end
def visit_profile(account)
visit short_account_path(account)
expect(page)
.to have_css('div.app-holder')
.and have_css('form.compose-form')
end
end