mirror of
https://github.com/glitch-soc/mastodon.git
synced 2026-03-29 11:11:11 +02:00
Profile redesign: Additional badges (#37683)
This commit is contained in:
64
app/javascript/mastodon/components/badge.stories.tsx
Normal file
64
app/javascript/mastodon/components/badge.stories.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||
|
||||
import CelebrationIcon from '@/material-icons/400-24px/celebration-fill.svg?react';
|
||||
|
||||
import * as badges from './badge';
|
||||
|
||||
const meta = {
|
||||
component: badges.Badge,
|
||||
title: 'Components/Badge',
|
||||
args: {
|
||||
label: 'Example',
|
||||
},
|
||||
} satisfies Meta<typeof badges.Badge>;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
||||
export const Domain: Story = {
|
||||
args: {
|
||||
domain: 'example.com',
|
||||
},
|
||||
};
|
||||
|
||||
export const CustomIcon: Story = {
|
||||
args: {
|
||||
icon: <CelebrationIcon />,
|
||||
},
|
||||
};
|
||||
|
||||
export const Admin: Story = {
|
||||
args: {
|
||||
roleId: '1',
|
||||
},
|
||||
render(args) {
|
||||
return <badges.AdminBadge {...args} />;
|
||||
},
|
||||
};
|
||||
|
||||
export const Group: Story = {
|
||||
render(args) {
|
||||
return <badges.GroupBadge {...args} />;
|
||||
},
|
||||
};
|
||||
|
||||
export const Automated: Story = {
|
||||
render(args) {
|
||||
return <badges.AutomatedBadge {...args} />;
|
||||
},
|
||||
};
|
||||
|
||||
export const Muted: Story = {
|
||||
render(args) {
|
||||
return <badges.MutedBadge {...args} />;
|
||||
},
|
||||
};
|
||||
|
||||
export const Blocked: Story = {
|
||||
render(args) {
|
||||
return <badges.BlockedBadge {...args} />;
|
||||
},
|
||||
};
|
||||
@@ -4,17 +4,28 @@ import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import AdminIcon from '@/images/icons/icon_admin.svg?react';
|
||||
import BlockIcon from '@/material-icons/400-24px/block.svg?react';
|
||||
import GroupsIcon from '@/material-icons/400-24px/group.svg?react';
|
||||
import PersonIcon from '@/material-icons/400-24px/person.svg?react';
|
||||
import SmartToyIcon from '@/material-icons/400-24px/smart_toy.svg?react';
|
||||
import VolumeOffIcon from '@/material-icons/400-24px/volume_off.svg?react';
|
||||
|
||||
export const Badge: FC<{
|
||||
interface BadgeProps {
|
||||
label: ReactNode;
|
||||
icon?: ReactNode;
|
||||
className?: string;
|
||||
domain?: ReactNode;
|
||||
roleId?: string;
|
||||
}> = ({ icon = <PersonIcon />, label, className, domain, roleId }) => (
|
||||
}
|
||||
|
||||
export const Badge: FC<BadgeProps> = ({
|
||||
icon = <PersonIcon />,
|
||||
label,
|
||||
className,
|
||||
domain,
|
||||
roleId,
|
||||
}) => (
|
||||
<div
|
||||
className={classNames('account-role', className)}
|
||||
data-account-role-id={roleId}
|
||||
@@ -25,13 +36,23 @@ export const Badge: FC<{
|
||||
</div>
|
||||
);
|
||||
|
||||
export const GroupBadge: FC<{ className?: string }> = ({ className }) => (
|
||||
export const AdminBadge: FC<Partial<BadgeProps>> = (props) => (
|
||||
<Badge
|
||||
icon={<AdminIcon />}
|
||||
label={
|
||||
<FormattedMessage id='account.badges.admin' defaultMessage='Admin' />
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
export const GroupBadge: FC<Partial<BadgeProps>> = (props) => (
|
||||
<Badge
|
||||
icon={<GroupsIcon />}
|
||||
label={
|
||||
<FormattedMessage id='account.badges.group' defaultMessage='Group' />
|
||||
}
|
||||
className={className}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -44,3 +65,23 @@ export const AutomatedBadge: FC<{ className?: string }> = ({ className }) => (
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
|
||||
export const MutedBadge: FC<Partial<BadgeProps>> = (props) => (
|
||||
<Badge
|
||||
icon={<VolumeOffIcon />}
|
||||
label={
|
||||
<FormattedMessage id='account.badges.muted' defaultMessage='Muted' />
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
export const BlockedBadge: FC<Partial<BadgeProps>> = (props) => (
|
||||
<Badge
|
||||
icon={<BlockIcon />}
|
||||
label={
|
||||
<FormattedMessage id='account.badges.blocked' defaultMessage='Blocked' />
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -101,7 +101,7 @@ export const AccountHeader: React.FC<{
|
||||
)}
|
||||
|
||||
<div className='account__header__image'>
|
||||
{me !== account.id && relationship && (
|
||||
{me !== account.id && relationship && !isRedesignEnabled() && (
|
||||
<AccountInfo relationship={relationship} />
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
import type { FC, ReactNode } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import IconAdmin from '@/images/icons/icon_admin.svg?react';
|
||||
import { AutomatedBadge, Badge, GroupBadge } from '@/mastodon/components/badge';
|
||||
import { Icon } from '@/mastodon/components/icon';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { fetchRelationships } from '@/mastodon/actions/accounts';
|
||||
import {
|
||||
AdminBadge,
|
||||
AutomatedBadge,
|
||||
Badge,
|
||||
BlockedBadge,
|
||||
GroupBadge,
|
||||
MutedBadge,
|
||||
} from '@/mastodon/components/badge';
|
||||
import { useAccount } from '@/mastodon/hooks/useAccount';
|
||||
import type { AccountRole } from '@/mastodon/models/account';
|
||||
import { useAppSelector } from '@/mastodon/store';
|
||||
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
||||
|
||||
import { isRedesignEnabled } from '../common';
|
||||
|
||||
@@ -16,6 +27,17 @@ export const AccountBadges: FC<{ accountId: string }> = ({ accountId }) => {
|
||||
const localDomain = useAppSelector(
|
||||
(state) => state.meta.get('domain') as string,
|
||||
);
|
||||
const relationship = useAppSelector((state) =>
|
||||
state.relationships.get(accountId),
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
useEffect(() => {
|
||||
if (!relationship) {
|
||||
dispatch(fetchRelationships([accountId]));
|
||||
}
|
||||
}, [accountId, dispatch, relationship]);
|
||||
|
||||
const badges = [];
|
||||
|
||||
if (!account) {
|
||||
@@ -24,39 +46,113 @@ export const AccountBadges: FC<{ accountId: string }> = ({ accountId }) => {
|
||||
|
||||
const className = isRedesignEnabled() ? classes.badge : '';
|
||||
|
||||
if (account.bot) {
|
||||
badges.push(<AutomatedBadge key='bot-badge' className={className} />);
|
||||
} else if (account.group) {
|
||||
badges.push(<GroupBadge key='group-badge' className={className} />);
|
||||
}
|
||||
|
||||
const domain = account.acct.includes('@')
|
||||
? account.acct.split('@')[1]
|
||||
: localDomain;
|
||||
account.roles.forEach((role) => {
|
||||
let icon: ReactNode = undefined;
|
||||
if (isAdminBadge(role)) {
|
||||
icon = (
|
||||
<Icon
|
||||
icon={IconAdmin}
|
||||
id='badge-admin'
|
||||
className={classes.badgeIcon}
|
||||
noFill
|
||||
/>
|
||||
badges.push(
|
||||
<AdminBadge
|
||||
key={role.id}
|
||||
label={role.name}
|
||||
className={className}
|
||||
domain={`(${domain})`}
|
||||
roleId={role.id}
|
||||
/>,
|
||||
);
|
||||
} else {
|
||||
badges.push(
|
||||
<Badge
|
||||
key={role.id}
|
||||
label={role.name}
|
||||
className={className}
|
||||
domain={isRedesignEnabled() ? `(${domain})` : domain}
|
||||
roleId={role.id}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
badges.push(
|
||||
<Badge
|
||||
key={role.id}
|
||||
label={role.name}
|
||||
className={className}
|
||||
domain={isRedesignEnabled() ? `(${domain})` : domain}
|
||||
roleId={role.id}
|
||||
icon={icon}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
|
||||
if (account.bot) {
|
||||
badges.push(<AutomatedBadge key='bot-badge' className={className} />);
|
||||
}
|
||||
if (account.group) {
|
||||
badges.push(<GroupBadge key='group-badge' className={className} />);
|
||||
}
|
||||
if (isRedesignEnabled() && relationship) {
|
||||
if (relationship.blocking) {
|
||||
badges.push(
|
||||
<BlockedBadge
|
||||
key='blocking'
|
||||
className={classNames(className, classes.badgeBlocked)}
|
||||
/>,
|
||||
);
|
||||
} else if (relationship.domain_blocking) {
|
||||
badges.push(
|
||||
<BlockedBadge
|
||||
key='domain-blocking'
|
||||
className={classNames(className, classes.badgeBlocked)}
|
||||
domain={domain}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='account.badges.domain_blocked'
|
||||
defaultMessage='Blocked domain'
|
||||
/>
|
||||
}
|
||||
/>,
|
||||
);
|
||||
} else if (relationship.muting) {
|
||||
badges.push(
|
||||
<MutedBadge
|
||||
key='muted-badge'
|
||||
className={classNames(className, classes.badgeMuted)}
|
||||
/>,
|
||||
);
|
||||
} else if (
|
||||
relationship.followed_by &&
|
||||
(relationship.following || relationship.requested)
|
||||
) {
|
||||
badges.push(
|
||||
<Badge
|
||||
key='mutuals-badge'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='account.badges.mutuals'
|
||||
defaultMessage='You follow each other'
|
||||
/>
|
||||
}
|
||||
className={className}
|
||||
/>,
|
||||
);
|
||||
} else if (relationship.followed_by) {
|
||||
badges.push(
|
||||
<Badge
|
||||
key='follows-you-badge'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='account.badges.follows_you'
|
||||
defaultMessage='Follows you'
|
||||
/>
|
||||
}
|
||||
className={className}
|
||||
/>,
|
||||
);
|
||||
} else if (relationship.requested_by) {
|
||||
badges.push(
|
||||
<Badge
|
||||
key='requested-to-follow-badge'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='account.badges.requested_to_follow'
|
||||
defaultMessage='Requested to follow you'
|
||||
/>
|
||||
}
|
||||
className={className}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!badges.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
.badgeMuted {
|
||||
background-color: var(--color-bg-inverted);
|
||||
color: var(--color-text-on-inverted);
|
||||
}
|
||||
|
||||
.badgeBlocked {
|
||||
background-color: var(--color-bg-error-base);
|
||||
color: var(--color-text-on-error-base);
|
||||
}
|
||||
|
||||
svg.badgeIcon {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,15 @@
|
||||
"account.activity": "Activity",
|
||||
"account.add_note": "Add a personal note",
|
||||
"account.add_or_remove_from_list": "Add or Remove from lists",
|
||||
"account.badges.admin": "Admin",
|
||||
"account.badges.blocked": "Blocked",
|
||||
"account.badges.bot": "Automated",
|
||||
"account.badges.domain_blocked": "Blocked domain",
|
||||
"account.badges.follows_you": "Follows you",
|
||||
"account.badges.group": "Group",
|
||||
"account.badges.muted": "Muted",
|
||||
"account.badges.mutuals": "You follow each other",
|
||||
"account.badges.requested_to_follow": "Requested to follow you",
|
||||
"account.block": "Block @{name}",
|
||||
"account.block_domain": "Block domain {domain}",
|
||||
"account.block_short": "Block",
|
||||
|
||||
Reference in New Issue
Block a user