Add "Follows you" badge to AccountListItem component (#38828)
This commit is contained in:
parent
6c5bd4f9a8
commit
ffd7160980
3
app/javascript/images/icons/icon_clock.svg
Normal file
3
app/javascript/images/icons/icon_clock.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M13.2502 8.00029C13.2502 5.10089 10.8996 2.75044 8.00024 2.75029C5.10075 2.75029 2.75024 5.10079 2.75024 8.00029C2.7504 10.8997 5.10084 13.2503 8.00024 13.2503C10.8995 13.2501 13.2501 10.8996 13.2502 8.00029ZM7.41663 4.50029C7.41663 4.17812 7.67808 3.91667 8.00024 3.91667C8.32228 3.91683 8.58301 4.17822 8.58301 4.50029V7.63884L10.5945 8.64458C10.8825 8.78865 10.999 9.13922 10.8551 9.42729C10.711 9.71541 10.3605 9.83276 10.0724 9.68877L7.73877 8.52153C7.54143 8.4227 7.41673 8.221 7.41663 8.00029V4.50029ZM14.4166 8.00029C14.4165 11.5439 11.5438 14.4165 8.00024 14.4167C4.45651 14.4167 1.58316 11.544 1.58301 8.00029C1.58301 4.45646 4.45642 1.58305 8.00024 1.58305C11.5439 1.58321 14.4166 4.45656 14.4166 8.00029Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 810 B |
@ -1,6 +1,8 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||
|
||||
import { accountFactoryState } from '@/testing/factories';
|
||||
import { accountFactoryState, relationshipsFactory } from '@/testing/factories';
|
||||
|
||||
import { PendingBadge } from '../badge';
|
||||
|
||||
import { AccountListItem } from './index';
|
||||
|
||||
@ -26,12 +28,30 @@ type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
||||
export const FollowsYou: Story = {
|
||||
parameters: {
|
||||
state: {
|
||||
relationships: {
|
||||
'1': relationshipsFactory({
|
||||
followed_by: true,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithCustomStats: Story = {
|
||||
args: {
|
||||
stats: ['posts', 'last-active'],
|
||||
},
|
||||
};
|
||||
|
||||
export const WithCustomBadge: Story = {
|
||||
args: {
|
||||
badge: <PendingBadge />,
|
||||
},
|
||||
};
|
||||
|
||||
export const WithBorder: Story = {
|
||||
args: {
|
||||
withBorder: true,
|
||||
|
||||
@ -1,18 +1,23 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { Account } from 'mastodon/components/account';
|
||||
import { VerifiedBadge } from 'mastodon/components/badge';
|
||||
import { FollowsYouBadge, VerifiedBadge } from 'mastodon/components/badge';
|
||||
import { useAccount } from 'mastodon/hooks/useAccount';
|
||||
import { useRelationship } from 'mastodon/hooks/useRelationship';
|
||||
import { domain } from 'mastodon/initial_state';
|
||||
import type { Relationship } from 'mastodon/models/relationship';
|
||||
|
||||
import { Avatar } from '../avatar';
|
||||
import { useAccountHandle } from '../display_name/default';
|
||||
import { DisplayNameSimple } from '../display_name/simple';
|
||||
import { EmojiHTML } from '../emoji/html';
|
||||
import { FollowButton } from '../follow_button';
|
||||
import { FormattedDateWrapper } from '../formatted_date';
|
||||
import { ListItemLink, ListItemWrapper } from '../list_item';
|
||||
import { NumberFields, NumberFieldsItem } from '../number_fields';
|
||||
import { RelativeTimestamp } from '../relative_timestamp';
|
||||
import { ShortNumber } from '../short_number';
|
||||
@ -29,9 +34,10 @@ type Stat = 'followers' | 'following' | 'posts' | 'joined' | 'last-active';
|
||||
interface Props {
|
||||
accountId: string | undefined;
|
||||
stats?: Stat[];
|
||||
renderButton?: (options: RenderButtonOptions) => React.ReactNode;
|
||||
withBio?: boolean;
|
||||
withBorder?: boolean;
|
||||
badge?: ReactNode;
|
||||
renderButton?: (options: RenderButtonOptions) => React.ReactNode;
|
||||
}
|
||||
|
||||
const DEFAULT_STATS: Stat[] = ['followers', 'posts', 'last-active'];
|
||||
@ -48,10 +54,12 @@ export const AccountListItem: React.FC<Props> = ({
|
||||
stats = DEFAULT_STATS,
|
||||
withBio = true,
|
||||
withBorder = true,
|
||||
badge: badgeProp,
|
||||
renderButton = defaultRenderButton,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const account = useAccount(accountId);
|
||||
const handle = useAccountHandle(account, domain);
|
||||
const relationship = useRelationship(accountId);
|
||||
|
||||
const createdThisYear = useMemo(
|
||||
@ -63,22 +71,34 @@ export const AccountListItem: React.FC<Props> = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const badge =
|
||||
badgeProp ?? (relationship?.followed_by ? <FollowsYouBadge /> : null);
|
||||
|
||||
const firstVerifiedField = account.fields.find((item) => !!item.verified_at);
|
||||
|
||||
return (
|
||||
<div className={classes.wrapper} data-with-border={withBorder}>
|
||||
<div className={classes.header}>
|
||||
<Account
|
||||
id={accountId}
|
||||
minimal
|
||||
size={40}
|
||||
withMenu={false}
|
||||
withBorder={false}
|
||||
className={classes.account}
|
||||
/>
|
||||
|
||||
{renderButton({ accountId, relationship })}
|
||||
</div>
|
||||
<ListItemWrapper
|
||||
className={classes.main}
|
||||
icon={<Avatar account={account} size={40} />}
|
||||
sideContent={
|
||||
<span className={classes.button}>
|
||||
{renderButton({ accountId, relationship })}
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<ListItemLink
|
||||
to={`/@${account.acct}`}
|
||||
data-hover-card-account={accountId}
|
||||
subtitle={handle}
|
||||
>
|
||||
<DisplayNameSimple
|
||||
account={account}
|
||||
className={classes.displayName}
|
||||
/>
|
||||
{badge && <span className={classes.badge}>{badge}</span>}
|
||||
</ListItemLink>
|
||||
</ListItemWrapper>
|
||||
|
||||
<NumberFields>
|
||||
{stats.includes('followers') && (
|
||||
|
||||
@ -10,22 +10,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
width: 100%;
|
||||
.main {
|
||||
--list-item-padding: 0;
|
||||
}
|
||||
|
||||
.account {
|
||||
--account-name-size: 15px;
|
||||
--account-handle-color: var(--color-text-secondary);
|
||||
--account-handle-size: 13px;
|
||||
--account-bio-color: var(--color-text-primary);
|
||||
--account-bio-size: 13px;
|
||||
--account-outer-spacing: 0;
|
||||
.displayName {
|
||||
// Spacing for badge
|
||||
margin-inline-end: 6px;
|
||||
}
|
||||
|
||||
flex-grow: 1;
|
||||
margin-inline-end: 16px;
|
||||
.badge {
|
||||
// Sort out vertical alignment next to name
|
||||
vertical-align: -4px;
|
||||
}
|
||||
|
||||
.button {
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
.verifiedBadge {
|
||||
|
||||
@ -5,6 +5,8 @@ import { FormattedMessage, useIntl } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import AdminIcon from '@/images/icons/icon_admin.svg?react';
|
||||
import ClockIcon from '@/images/icons/icon_clock.svg?react';
|
||||
import FollowerIcon from '@/images/icons/icon_follower.svg?react';
|
||||
import IconVerified from '@/images/icons/icon_verified.svg?react';
|
||||
import type { OnAttributeHandler } from '@/mastodon/utils/html';
|
||||
import BlockIcon from '@/material-icons/400-24px/block.svg?react';
|
||||
@ -32,6 +34,11 @@ interface BadgeProps extends React.ComponentPropsWithoutRef<'div'> {
|
||||
| 'danger';
|
||||
}
|
||||
|
||||
type PresetBadgeProps = Omit<
|
||||
BadgeProps,
|
||||
'label' | 'icon' | 'domain' | 'roleId'
|
||||
>;
|
||||
|
||||
export const Badge: FC<BadgeProps> = ({
|
||||
icon = <PersonIcon />,
|
||||
variant = 'default',
|
||||
@ -83,13 +90,32 @@ export const GroupBadge: FC<Partial<BadgeProps>> = ({ label, ...props }) => (
|
||||
/>
|
||||
);
|
||||
|
||||
export const AutomatedBadge: FC<{ className?: string }> = ({ className }) => (
|
||||
export const AutomatedBadge: FC<PresetBadgeProps> = (props) => (
|
||||
<Badge
|
||||
icon={<SmartToyIcon />}
|
||||
label={
|
||||
<FormattedMessage id='account.badges.bot' defaultMessage='Automated' />
|
||||
}
|
||||
className={className}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
export const FollowsYouBadge: FC<PresetBadgeProps> = (props) => (
|
||||
<Badge
|
||||
icon={<FollowerIcon />}
|
||||
label={
|
||||
<FormattedMessage id='account.follows_you' defaultMessage='Follows you' />
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
export const PendingBadge: FC<PresetBadgeProps> = (props) => (
|
||||
<Badge
|
||||
variant='warning'
|
||||
icon={<ClockIcon />}
|
||||
label={<FormattedMessage id='account.pending' defaultMessage='Pending' />}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
.badge {
|
||||
color: var(--color-text-primary);
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
display: inline-flex;
|
||||
max-width: 100%;
|
||||
padding: 4px;
|
||||
@ -61,7 +62,6 @@
|
||||
|
||||
.warning {
|
||||
background-color: var(--color-bg-warning-softest);
|
||||
color: var(--color-text-warning);
|
||||
}
|
||||
|
||||
.danger {
|
||||
|
||||
@ -7,9 +7,8 @@ import classNames from 'classnames';
|
||||
|
||||
import Overlay from 'react-overlays/esm/Overlay';
|
||||
|
||||
import FollowerIcon from '@/images/icons/icon_follower.svg?react';
|
||||
import { showAlert } from '@/mastodon/actions/alerts';
|
||||
import { Badge } from '@/mastodon/components/badge';
|
||||
import { FollowsYouBadge } from '@/mastodon/components/badge';
|
||||
import { Button } from '@/mastodon/components/button';
|
||||
import { DisplayName } from '@/mastodon/components/display_name';
|
||||
import { Icon } from '@/mastodon/components/icon';
|
||||
@ -60,17 +59,7 @@ export const AccountName: FC<{ accountId: string }> = ({ accountId }) => {
|
||||
<h1>
|
||||
<DisplayName account={account} variant='simple' />
|
||||
</h1>
|
||||
{relationship?.followed_by && (
|
||||
<Badge
|
||||
icon={<FollowerIcon className={classes.followerBadgeIcon} />}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='account.follows_you'
|
||||
defaultMessage='Follows you'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{relationship?.followed_by && <FollowsYouBadge />}
|
||||
</div>
|
||||
|
||||
<AccountNameHelp
|
||||
|
||||
@ -37,10 +37,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.followerBadgeIcon {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.badges {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
@ -131,6 +131,7 @@
|
||||
"account.note.edit_button": "Edit",
|
||||
"account.note.title": "Personal note (visible only to you)",
|
||||
"account.open_original_page": "Open original page",
|
||||
"account.pending": "Pending",
|
||||
"account.posts": "Posts",
|
||||
"account.remove_from_followers": "Remove {name} from followers",
|
||||
"account.report": "Report @{name}",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user