glitch-soc/mastodon

View on GitHub
app/javascript/mastodon/features/account_timeline/components/account_header.tsx

Summary

Maintainability
F
1 mo
Test Coverage
File `account_header.tsx` has 895 lines of code (exceeds 250 allowed). Consider refactoring.
import { useCallback, useMemo } from 'react';
 
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
 
import classNames from 'classnames';
import { Helmet } from 'react-helmet';
import { NavLink } from 'react-router-dom';
 
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
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 {
followAccount,
unblockAccount,
unmuteAccount,
pinAccount,
unpinAccount,
} from 'mastodon/actions/accounts';
import { initBlockModal } from 'mastodon/actions/blocks';
import { mentionCompose, directCompose } from 'mastodon/actions/compose';
import {
initDomainBlockModal,
unblockDomain,
} from 'mastodon/actions/domain_blocks';
import { openModal } from 'mastodon/actions/modal';
import { initMuteModal } from 'mastodon/actions/mutes';
import { initReport } from 'mastodon/actions/reports';
import { Avatar } from 'mastodon/components/avatar';
import { Badge, AutomatedBadge, GroupBadge } from 'mastodon/components/badge';
import { Button } from 'mastodon/components/button';
import { CopyIconButton } from 'mastodon/components/copy_icon_button';
import {
FollowersCounter,
FollowingCounter,
StatusesCounter,
} from 'mastodon/components/counters';
import { Dropdown } from 'mastodon/components/dropdown_menu';
import { FollowButton } from 'mastodon/components/follow_button';
import { FormattedDateWrapper } from 'mastodon/components/formatted_date';
import { Icon } from 'mastodon/components/icon';
import { IconButton } from 'mastodon/components/icon_button';
import { ShortNumber } from 'mastodon/components/short_number';
import { DomainPill } from 'mastodon/features/account/components/domain_pill';
import AccountNoteContainer from 'mastodon/features/account/containers/account_note_container';
import FollowRequestNoteContainer from 'mastodon/features/account/containers/follow_request_note_container';
import { useLinks } from 'mastodon/hooks/useLinks';
import { useIdentity } from 'mastodon/identity_context';
import { autoPlayGif, me, domain as localDomain } from 'mastodon/initial_state';
import type { Account } from 'mastodon/models/account';
import type { MenuItem } from 'mastodon/models/dropdown_menu';
import {
PERMISSION_MANAGE_USERS,
PERMISSION_MANAGE_FEDERATION,
} from 'mastodon/permissions';
import { getAccountHidden } from 'mastodon/selectors/accounts';
import { useAppSelector, useAppDispatch } from 'mastodon/store';
 
import { MemorialNote } from './memorial_note';
import { MovedNote } from './moved_note';
 
Similar blocks of code found in 2 locations. Consider refactoring.
const messages = defineMessages({
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
follow: { id: 'account.follow', defaultMessage: 'Follow' },
followBack: { id: 'account.follow_back', defaultMessage: 'Follow back' },
mutual: { id: 'account.mutual', defaultMessage: 'Mutual' },
cancel_follow_request: {
id: 'account.cancel_follow_request',
defaultMessage: 'Withdraw follow request',
},
requested: {
id: 'account.requested',
defaultMessage: 'Awaiting approval. Click to cancel follow request',
},
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
linkVerifiedOn: {
id: 'account.link_verified_on',
defaultMessage: 'Ownership of this link was checked on {date}',
},
account_locked: {
id: 'account.locked_info',
defaultMessage:
'This account privacy status is set to locked. The owner manually reviews who can follow them.',
},
mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
direct: { id: 'account.direct', defaultMessage: 'Privately mention @{name}' },
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
block: { id: 'account.block', defaultMessage: 'Block @{name}' },
mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
report: { id: 'account.report', defaultMessage: 'Report @{name}' },
share: { id: 'account.share', defaultMessage: "Share @{name}'s profile" },
copy: { id: 'account.copy', defaultMessage: 'Copy link to profile' },
media: { id: 'account.media', defaultMessage: 'Media' },
blockDomain: {
id: 'account.block_domain',
defaultMessage: 'Block domain {domain}',
},
unblockDomain: {
id: 'account.unblock_domain',
defaultMessage: 'Unblock domain {domain}',
},
hideReblogs: {
id: 'account.hide_reblogs',
defaultMessage: 'Hide boosts from @{name}',
},
showReblogs: {
id: 'account.show_reblogs',
defaultMessage: 'Show boosts from @{name}',
},
enableNotifications: {
id: 'account.enable_notifications',
defaultMessage: 'Notify me when @{name} posts',
},
disableNotifications: {
id: 'account.disable_notifications',
defaultMessage: 'Stop notifying me when @{name} posts',
},
pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned posts' },
preferences: {
id: 'navigation_bar.preferences',
defaultMessage: 'Preferences',
},
follow_requests: {
id: 'navigation_bar.follow_requests',
defaultMessage: 'Follow requests',
},
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favorites' },
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
followed_tags: {
id: 'navigation_bar.followed_tags',
defaultMessage: 'Followed hashtags',
},
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
domain_blocks: {
id: 'navigation_bar.domain_blocks',
defaultMessage: 'Blocked domains',
},
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
endorse: { id: 'account.endorse', defaultMessage: 'Feature on profile' },
unendorse: {
id: 'account.unendorse',
defaultMessage: "Don't feature on profile",
},
add_or_remove_from_list: {
id: 'account.add_or_remove_from_list',
defaultMessage: 'Add or Remove from lists',
},
admin_account: {
id: 'status.admin_account',
defaultMessage: 'Open moderation interface for @{name}',
},
admin_domain: {
id: 'status.admin_domain',
defaultMessage: 'Open moderation interface for {domain}',
},
languages: {
id: 'account.languages',
defaultMessage: 'Change subscribed languages',
},
openOriginalPage: {
id: 'account.open_original_page',
defaultMessage: 'Open original page',
},
});
 
Identical blocks of code found in 2 locations. Consider refactoring.
const titleFromAccount = (account: Account) => {
const displayName = account.display_name;
const acct =
account.acct === account.username
? `${account.username}@${localDomain}`
: account.acct;
const prefix =
displayName.trim().length === 0 ? account.username : displayName;
 
return `${prefix} (@${acct})`;
};
 
const dateFormatOptions: Intl.DateTimeFormatOptions = {
month: 'short',
day: 'numeric',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
};
 
export const AccountHeader: React.FC<{
accountId: string;
hideTabs?: boolean;
Function `AccountHeader` has a Cognitive Complexity of 144 (exceeds 5 allowed). Consider refactoring.
}> = ({ accountId, hideTabs }) => {
const dispatch = useAppDispatch();
const intl = useIntl();
const { signedIn, permissions } = useIdentity();
const account = useAppSelector((state) => state.accounts.get(accountId));
const relationship = useAppSelector((state) =>
state.relationships.get(accountId),
);
const hidden = useAppSelector((state) => getAccountHidden(state, accountId));
const handleLinkClick = useLinks();
 
Similar blocks of code found in 4 locations. Consider refactoring.
const handleBlock = useCallback(() => {
if (!account) {
return;
}
 
if (relationship?.blocking) {
dispatch(unblockAccount(account.id));
} else {
dispatch(initBlockModal(account));
}
}, [dispatch, account, relationship]);
 
const handleMention = useCallback(() => {
if (!account) {
return;
}
 
dispatch(mentionCompose(account));
}, [dispatch, account]);
 
const handleDirect = useCallback(() => {
if (!account) {
return;
}
 
dispatch(directCompose(account));
}, [dispatch, account]);
 
const handleReport = useCallback(() => {
if (!account) {
return;
}
 
dispatch(initReport(account));
}, [dispatch, account]);
 
Similar blocks of code found in 4 locations. Consider refactoring.
const handleReblogToggle = useCallback(() => {
if (!account) {
return;
}
 
if (relationship?.showing_reblogs) {
dispatch(followAccount(account.id, { reblogs: false }));
} else {
dispatch(followAccount(account.id, { reblogs: true }));
}
}, [dispatch, account, relationship]);
 
Similar blocks of code found in 4 locations. Consider refactoring.
const handleNotifyToggle = useCallback(() => {
if (!account) {
return;
}
 
if (relationship?.notifying) {
dispatch(followAccount(account.id, { notify: false }));
} else {
dispatch(followAccount(account.id, { notify: true }));
}
}, [dispatch, account, relationship]);
 
Similar blocks of code found in 4 locations. Consider refactoring.
const handleMute = useCallback(() => {
if (!account) {
return;
}
 
if (relationship?.muting) {
dispatch(unmuteAccount(account.id));
} else {
dispatch(initMuteModal(account));
}
}, [dispatch, account, relationship]);
 
const handleBlockDomain = useCallback(() => {
if (!account) {
return;
}
 
dispatch(initDomainBlockModal(account));
}, [dispatch, account]);
 
Identical blocks of code found in 2 locations. Consider refactoring.
const handleUnblockDomain = useCallback(() => {
if (!account) {
return;
}
 
const domain = account.acct.split('@')[1];
 
if (!domain) {
return;
}
 
dispatch(unblockDomain(domain));
}, [dispatch, account]);
 
Identical blocks of code found in 2 locations. Consider refactoring.
const handleEndorseToggle = useCallback(() => {
if (!account) {
return;
}
 
if (relationship?.endorsed) {
dispatch(unpinAccount(account.id));
} else {
dispatch(pinAccount(account.id));
}
}, [dispatch, account, relationship]);
 
Similar blocks of code found in 4 locations. Consider refactoring.
const handleAddToList = useCallback(() => {
if (!account) {
return;
}
 
dispatch(
openModal({
modalType: 'LIST_ADDER',
modalProps: {
accountId: account.id,
},
}),
);
}, [dispatch, account]);
 
Similar blocks of code found in 4 locations. Consider refactoring.
const handleChangeLanguages = useCallback(() => {
if (!account) {
return;
}
 
dispatch(
openModal({
modalType: 'SUBSCRIBED_LANGUAGES',
modalProps: {
accountId: account.id,
},
}),
);
}, [dispatch, account]);
 
Similar blocks of code found in 2 locations. Consider refactoring.
const handleOpenAvatar = useCallback(
(e: React.MouseEvent) => {
if (e.button !== 0 || e.ctrlKey || e.metaKey) {
return;
}
 
e.preventDefault();
 
if (!account) {
return;
}
 
dispatch(
openModal({
modalType: 'IMAGE',
modalProps: {
src: account.avatar,
alt: '',
},
}),
);
},
[dispatch, account],
);
 
Similar blocks of code found in 2 locations. Consider refactoring.
const handleShare = useCallback(() => {
if (!account) {
return;
}
 
void navigator.share({
url: account.url,
});
}, [account]);
 
Similar blocks of code found in 4 locations. Consider refactoring.
const handleMouseEnter = useCallback(
({ currentTarget }: React.MouseEvent) => {
if (autoPlayGif) {
return;
}
 
currentTarget
.querySelectorAll<HTMLImageElement>('.custom-emoji')
.forEach((emoji) => {
emoji.src = emoji.getAttribute('data-original') ?? '';
});
},
[],
);
 
Similar blocks of code found in 4 locations. Consider refactoring.
const handleMouseLeave = useCallback(
({ currentTarget }: React.MouseEvent) => {
if (autoPlayGif) {
return;
}
 
currentTarget
.querySelectorAll<HTMLImageElement>('.custom-emoji')
.forEach((emoji) => {
emoji.src = emoji.getAttribute('data-static') ?? '';
});
},
[],
);
 
const suspended = account?.suspended;
const isRemote = account?.acct !== account?.username;
const remoteDomain = isRemote ? account?.acct.split('@')[1] : null;
 
Function `menu` has 186 lines of code (exceeds 25 allowed). Consider refactoring.
Similar blocks of code found in 2 locations. Consider refactoring.
const menu = useMemo(() => {
const arr: MenuItem[] = [];
 
if (!account) {
return arr;
}
 
if (signedIn && account.id !== me && !account.suspended) {
arr.push({
text: intl.formatMessage(messages.mention, {
name: account.username,
}),
action: handleMention,
});
arr.push({
text: intl.formatMessage(messages.direct, {
name: account.username,
}),
action: handleDirect,
});
arr.push(null);
}
 
if (isRemote) {
arr.push({
text: intl.formatMessage(messages.openOriginalPage),
href: account.url,
});
arr.push(null);
}
 
if (account.id === me) {
arr.push({
text: intl.formatMessage(messages.edit_profile),
href: '/settings/profile',
});
arr.push({
text: intl.formatMessage(messages.preferences),
href: '/settings/preferences',
});
arr.push({ text: intl.formatMessage(messages.pins), to: '/pinned' });
arr.push(null);
arr.push({
text: intl.formatMessage(messages.follow_requests),
to: '/follow_requests',
});
arr.push({
text: intl.formatMessage(messages.favourites),
to: '/favourites',
});
arr.push({ text: intl.formatMessage(messages.lists), to: '/lists' });
arr.push({
text: intl.formatMessage(messages.followed_tags),
to: '/followed_tags',
});
arr.push(null);
arr.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
arr.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
arr.push({
text: intl.formatMessage(messages.domain_blocks),
to: '/domain_blocks',
});
} else if (signedIn) {
if (relationship?.following) {
if (!relationship.muting) {
if (relationship.showing_reblogs) {
arr.push({
text: intl.formatMessage(messages.hideReblogs, {
name: account.username,
}),
action: handleReblogToggle,
});
} else {
arr.push({
text: intl.formatMessage(messages.showReblogs, {
name: account.username,
}),
action: handleReblogToggle,
});
}
 
arr.push({
text: intl.formatMessage(messages.languages),
action: handleChangeLanguages,
});
arr.push(null);
}
 
arr.push({
text: intl.formatMessage(
account.getIn(['relationship', 'endorsed'])
? messages.unendorse
: messages.endorse,
),
action: handleEndorseToggle,
});
arr.push({
text: intl.formatMessage(messages.add_or_remove_from_list),
action: handleAddToList,
});
arr.push(null);
}
 
if (relationship?.muting) {
arr.push({
text: intl.formatMessage(messages.unmute, {
name: account.username,
}),
action: handleMute,
});
} else {
arr.push({
text: intl.formatMessage(messages.mute, {
name: account.username,
}),
action: handleMute,
dangerous: true,
});
}
 
if (relationship?.blocking) {
arr.push({
text: intl.formatMessage(messages.unblock, {
name: account.username,
}),
action: handleBlock,
});
} else {
arr.push({
text: intl.formatMessage(messages.block, {
name: account.username,
}),
action: handleBlock,
dangerous: true,
});
}
 
if (!account.suspended) {
arr.push({
text: intl.formatMessage(messages.report, {
name: account.username,
}),
action: handleReport,
dangerous: true,
});
}
}
 
if (signedIn && isRemote) {
arr.push(null);
 
if (relationship?.domain_blocking) {
arr.push({
text: intl.formatMessage(messages.unblockDomain, {
domain: remoteDomain,
}),
action: handleUnblockDomain,
});
} else {
arr.push({
text: intl.formatMessage(messages.blockDomain, {
domain: remoteDomain,
}),
action: handleBlockDomain,
dangerous: true,
});
}
}
 
if (
(account.id !== me &&
(permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) ||
(isRemote &&
(permissions & PERMISSION_MANAGE_FEDERATION) ===
PERMISSION_MANAGE_FEDERATION)
) {
arr.push(null);
if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
arr.push({
text: intl.formatMessage(messages.admin_account, {
name: account.username,
}),
href: `/admin/accounts/${account.id}`,
});
}
if (
isRemote &&
(permissions & PERMISSION_MANAGE_FEDERATION) ===
PERMISSION_MANAGE_FEDERATION
) {
arr.push({
text: intl.formatMessage(messages.admin_domain, {
domain: remoteDomain,
}),
href: `/admin/instances/${remoteDomain}`,
});
}
}
 
return arr;
}, [
account,
relationship,
permissions,
isRemote,
remoteDomain,
intl,
signedIn,
handleAddToList,
handleBlock,
handleBlockDomain,
handleChangeLanguages,
handleDirect,
handleEndorseToggle,
handleMention,
handleMute,
handleReblogToggle,
handleReport,
handleUnblockDomain,
]);
 
if (!account) {
return null;
}
 
Identical blocks of code found in 2 locations. Consider refactoring.
let actionBtn: React.ReactNode,
bellBtn: React.ReactNode,
lockedIcon: React.ReactNode,
shareBtn: React.ReactNode;
 
const info: React.ReactNode[] = [];
 
if (me !== account.id && relationship?.blocking) {
info.push(
<span key='blocked' className='relationship-tag'>
<FormattedMessage id='account.blocked' defaultMessage='Blocked' />
</span>,
);
}
 
Similar blocks of code found in 3 locations. Consider refactoring.
if (me !== account.id && relationship?.muting) {
info.push(
<span key='muted' className='relationship-tag'>
<FormattedMessage id='account.muted' defaultMessage='Muted' />
</span>,
);
} else if (me !== account.id && relationship?.domain_blocking) {
info.push(
<span key='domain_blocked' className='relationship-tag'>
<FormattedMessage
id='account.domain_blocked'
defaultMessage='Domain blocked'
/>
</span>,
);
}
 
Similar blocks of code found in 2 locations. Consider refactoring.
if (relationship?.requested || relationship?.following) {
bellBtn = (
<IconButton
icon={relationship.notifying ? 'bell' : 'bell-o'}
iconComponent={
relationship.notifying ? NotificationsActiveIcon : NotificationsIcon
}
active={relationship.notifying}
title={intl.formatMessage(
relationship.notifying
? messages.disableNotifications
: messages.enableNotifications,
{ name: account.username },
)}
onClick={handleNotifyToggle}
/>
);
}
 
Similar blocks of code found in 2 locations. Consider refactoring.
if ('share' in navigator) {
shareBtn = (
<IconButton
className='optional'
icon=''
iconComponent={ShareIcon}
title={intl.formatMessage(messages.share, {
name: account.username,
})}
onClick={handleShare}
/>
);
} else {
shareBtn = (
<CopyIconButton
className='optional'
title={intl.formatMessage(messages.copy)}
value={account.url}
/>
);
}
 
Similar blocks of code found in 2 locations. Consider refactoring.
if (relationship?.blocking) {
actionBtn = (
<Button
text={intl.formatMessage(messages.unblock, {
name: account.username,
})}
onClick={handleBlock}
/>
);
} else {
actionBtn = <FollowButton accountId={accountId} />;
}
 
if (account.moved && !relationship?.following) {
actionBtn = '';
}
 
Similar blocks of code found in 2 locations. Consider refactoring.
if (account.locked) {
lockedIcon = (
<Icon
id='lock'
icon={LockIcon}
title={intl.formatMessage(messages.account_locked)}
/>
);
}
 
const content = { __html: account.note_emojified };
const displayNameHtml = { __html: account.display_name_html };
const fields = account.fields;
const isLocal = !account.acct.includes('@');
const username = account.acct.split('@')[0];
const domain = isLocal ? localDomain : account.acct.split('@')[1];
const isIndexable = !account.noindex;
 
const badges = [];
 
Identical blocks of code found in 2 locations. Consider refactoring.
if (account.bot) {
badges.push(<AutomatedBadge key='bot-badge' />);
} else if (account.group) {
badges.push(<GroupBadge key='group-badge' />);
}
 
Similar blocks of code found in 2 locations. Consider refactoring.
account.get('roles', []).forEach((role) => {
badges.push(
<Badge
key={`role-badge-${role.get('id')}`}
label={<span>{role.get('name')}</span>}
domain={domain}
roleId={role.get('id')}
/>,
);
});
 
return (
<div className='account-timeline__header'>
{!hidden && account.memorial && <MemorialNote />}
Similar blocks of code found in 2 locations. Consider refactoring.
{!hidden && account.moved && (
<MovedNote accountId={account.id} targetAccountId={account.moved} />
)}
 
Similar blocks of code found in 2 locations. Consider refactoring.
<div
className={classNames('account__header', {
inactive: !!account.moved,
})}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
{!(suspended || hidden || account.moved) &&
relationship?.requested_by && (
<FollowRequestNoteContainer account={account} />
)}
 
Similar blocks of code found in 2 locations. Consider refactoring.
<div className='account__header__image'>
<div className='account__header__info'>{info}</div>
 
{!(suspended || hidden) && (
<img
src={autoPlayGif ? account.header : account.header_static}
alt=''
className='parallax'
/>
)}
</div>
 
<div className='account__header__bar'>
Identical blocks of code found in 2 locations. Consider refactoring.
<div className='account__header__tabs'>
<a
className='avatar'
href={account.avatar}
rel='noopener'
target='_blank'
onClick={handleOpenAvatar}
>
<Avatar
account={suspended || hidden ? undefined : account}
size={90}
/>
</a>
 
<div className='account__header__tabs__buttons'>
{!hidden && bellBtn}
{!hidden && shareBtn}
<Dropdown
disabled={menu.length === 0}
items={menu}
icon='ellipsis-v'
iconComponent={MoreHorizIcon}
/>
{!hidden && actionBtn}
</div>
</div>
 
Identical blocks of code found in 2 locations. Consider refactoring.
<div className='account__header__tabs__name'>
<h1>
<span dangerouslySetInnerHTML={displayNameHtml} />
<small>
<span>
@{username}
<span className='invisible'>@{domain}</span>
</span>
<DomainPill
username={username ?? ''}
domain={domain ?? ''}
isSelf={me === account.id}
/>
{lockedIcon}
</small>
</h1>
</div>
 
{badges.length > 0 && (
<div className='account__header__badges'>{badges}</div>
)}
 
{!(suspended || hidden) && (
<div className='account__header__extra'>
Similar blocks of code found in 2 locations. Consider refactoring.
<div
className='account__header__bio'
onClickCapture={handleLinkClick}
>
{account.id !== me && signedIn && (
<AccountNoteContainer accountId={accountId} />
)}
 
{account.note.length > 0 && account.note !== '<p></p>' && (
<div
className='account__header__content translate'
dangerouslySetInnerHTML={content}
/>
)}
 
<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>
 
{fields.map((pair, i) => (
<dl
key={i}
className={classNames({
verified: pair.verified_at,
})}
>
<dt
dangerouslySetInnerHTML={{
__html: pair.name_emojified,
}}
title={pair.name}
className='translate'
/>
 
<dd className='translate' title={pair.value_plain ?? ''}>
{pair.verified_at && (
<span
title={intl.formatMessage(messages.linkVerifiedOn, {
date: intl.formatDate(
pair.verified_at,
dateFormatOptions,
),
})}
>
<Icon
id='check'
icon={CheckIcon}
className='verified__mark'
/>
</span>
)}{' '}
<span
dangerouslySetInnerHTML={{
__html: pair.value_emojified,
}}
/>
</dd>
</dl>
))}
</div>
</div>
 
<div className='account__header__extra__links'>
<NavLink
to={`/@${account.acct}`}
title={intl.formatNumber(account.statuses_count)}
>
<ShortNumber
value={account.statuses_count}
renderer={StatusesCounter}
/>
</NavLink>
 
Similar blocks of code found in 2 locations. Consider refactoring.
<NavLink
exact
to={`/@${account.acct}/following`}
title={intl.formatNumber(account.following_count)}
>
<ShortNumber
value={account.following_count}
renderer={FollowingCounter}
/>
</NavLink>
 
Similar blocks of code found in 2 locations. Consider refactoring.
<NavLink
exact
to={`/@${account.acct}/followers`}
title={intl.formatNumber(account.followers_count)}
>
<ShortNumber
value={account.followers_count}
renderer={FollowersCounter}
/>
</NavLink>
</div>
</div>
)}
</div>
</div>
 
{!(hideTabs || hidden) && (
<div className='account__section-headline'>
Similar blocks of code found in 7 locations. Consider refactoring.
<NavLink exact to={`/@${account.acct}/featured`}>
<FormattedMessage id='account.featured' defaultMessage='Featured' />
</NavLink>
Similar blocks of code found in 7 locations. Consider refactoring.
<NavLink exact to={`/@${account.acct}`}>
<FormattedMessage id='account.posts' defaultMessage='Posts' />
</NavLink>
Similar blocks of code found in 7 locations. Consider refactoring.
<NavLink exact to={`/@${account.acct}/with_replies`}>
<FormattedMessage
id='account.posts_with_replies'
defaultMessage='Posts and replies'
/>
</NavLink>
Similar blocks of code found in 7 locations. Consider refactoring.
<NavLink exact to={`/@${account.acct}/media`}>
<FormattedMessage id='account.media' defaultMessage='Media' />
</NavLink>
</div>
)}
 
Identical blocks of code found in 2 locations. Consider refactoring.
<Helmet>
<title>{titleFromAccount(account)}</title>
<meta
name='robots'
content={isLocal && isIndexable ? 'all' : 'noindex'}
/>
<link rel='canonical' href={account.url} />
</Helmet>
</div>
);
};