feat: Add "Followers you know" widget to user profiles (#34652)
This commit is contained in:
parent
c9a554bdca
commit
b135a831ea
12 changed files with 213 additions and 17 deletions
|
@ -59,6 +59,7 @@ import {
|
|||
import { getAccountHidden } from 'mastodon/selectors/accounts';
|
||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||
|
||||
import { FamiliarFollowers } from './familiar_followers';
|
||||
import { MemorialNote } from './memorial_note';
|
||||
import { MovedNote } from './moved_note';
|
||||
|
||||
|
@ -1022,6 +1023,7 @@ export const AccountHeader: React.FC<{
|
|||
/>
|
||||
</NavLink>
|
||||
</div>
|
||||
<FamiliarFollowers accountId={accountId} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
import { useEffect } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { fetchAccountsFamiliarFollowers } from '@/mastodon/actions/accounts_familiar_followers';
|
||||
import { AvatarGroup } from '@/mastodon/components/avatar_group';
|
||||
import type { Account } from '@/mastodon/models/account';
|
||||
import { getAccountFamiliarFollowers } from '@/mastodon/selectors/accounts';
|
||||
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
||||
|
||||
const AccountLink: React.FC<{ account?: Account }> = ({ account }) => (
|
||||
<Link to={`/@${account?.username}`} data-hover-card-account={account?.id}>
|
||||
{account?.display_name}
|
||||
</Link>
|
||||
);
|
||||
|
||||
const FamiliarFollowersReadout: React.FC<{ familiarFollowers: Account[] }> = ({
|
||||
familiarFollowers,
|
||||
}) => {
|
||||
const messageData = {
|
||||
name1: <AccountLink account={familiarFollowers.at(0)} />,
|
||||
name2: <AccountLink account={familiarFollowers.at(1)} />,
|
||||
othersCount: familiarFollowers.length - 2,
|
||||
};
|
||||
|
||||
if (familiarFollowers.length === 1) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='account.familiar_followers_one'
|
||||
defaultMessage='Followed by {name1}'
|
||||
values={messageData}
|
||||
/>
|
||||
);
|
||||
} else if (familiarFollowers.length === 2) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='account.familiar_followers_two'
|
||||
defaultMessage='Followed by {name1} and {name2}'
|
||||
values={messageData}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='account.familiar_followers_many'
|
||||
defaultMessage='Followed by {name1}, {name2}, and {othersCount, plural, one {# other} other {# others}}'
|
||||
values={messageData}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const FamiliarFollowers: React.FC<{ accountId: string }> = ({
|
||||
accountId,
|
||||
}) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const familiarFollowers = useAppSelector((state) =>
|
||||
getAccountFamiliarFollowers(state, accountId),
|
||||
);
|
||||
|
||||
const hasNoData = familiarFollowers === null;
|
||||
|
||||
useEffect(() => {
|
||||
if (hasNoData) {
|
||||
void dispatch(fetchAccountsFamiliarFollowers({ id: accountId }));
|
||||
}
|
||||
}, [dispatch, accountId, hasNoData]);
|
||||
|
||||
if (hasNoData || familiarFollowers.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='account__header__familiar-followers'>
|
||||
<AvatarGroup
|
||||
compact
|
||||
accountIds={familiarFollowers.slice(0, 3).map((account) => account.id)}
|
||||
/>
|
||||
<FamiliarFollowersReadout familiarFollowers={familiarFollowers} />
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,31 +0,0 @@
|
|||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { Avatar } from 'mastodon/components/avatar';
|
||||
import { NOTIFICATIONS_GROUP_MAX_AVATARS } from 'mastodon/models/notification_group';
|
||||
import { useAppSelector } from 'mastodon/store';
|
||||
|
||||
const AvatarWrapper: React.FC<{ accountId: string }> = ({ accountId }) => {
|
||||
const account = useAppSelector((state) => state.accounts.get(accountId));
|
||||
|
||||
if (!account) return null;
|
||||
|
||||
return (
|
||||
<Link
|
||||
to={`/@${account.acct}`}
|
||||
title={`@${account.acct}`}
|
||||
data-hover-card-account={account.id}
|
||||
>
|
||||
<Avatar account={account} size={28} />
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export const AvatarGroup: React.FC<{ accountIds: string[] }> = ({
|
||||
accountIds,
|
||||
}) => (
|
||||
<div className='notification-group__avatar-group'>
|
||||
{accountIds.slice(0, NOTIFICATIONS_GROUP_MAX_AVATARS).map((accountId) => (
|
||||
<AvatarWrapper key={accountId} accountId={accountId} />
|
||||
))}
|
||||
</div>
|
||||
);
|
|
@ -7,12 +7,13 @@ import { HotKeys } from 'react-hotkeys';
|
|||
|
||||
import { replyComposeById } from 'mastodon/actions/compose';
|
||||
import { navigateToStatus } from 'mastodon/actions/statuses';
|
||||
import { AvatarGroup } from 'mastodon/components/avatar_group';
|
||||
import type { IconProp } from 'mastodon/components/icon';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
||||
import { NOTIFICATIONS_GROUP_MAX_AVATARS } from 'mastodon/models/notification_group';
|
||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||
|
||||
import { AvatarGroup } from './avatar_group';
|
||||
import { DisplayedName } from './displayed_name';
|
||||
import { EmbeddedStatus } from './embedded_status';
|
||||
|
||||
|
@ -98,7 +99,12 @@ export const NotificationGroupWithStatus: React.FC<{
|
|||
<div className='notification-group__main'>
|
||||
<div className='notification-group__main__header'>
|
||||
<div className='notification-group__main__header__wrapper'>
|
||||
<AvatarGroup accountIds={accountIds} />
|
||||
<AvatarGroup
|
||||
accountIds={accountIds.slice(
|
||||
0,
|
||||
NOTIFICATIONS_GROUP_MAX_AVATARS,
|
||||
)}
|
||||
/>
|
||||
|
||||
{actions && (
|
||||
<div className='notification-group__actions'>{actions}</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue