diff --git a/app/javascript/mastodon/components/badge.stories.tsx b/app/javascript/mastodon/components/badge.stories.tsx new file mode 100644 index 0000000000..aaddcaa91a --- /dev/null +++ b/app/javascript/mastodon/components/badge.stories.tsx @@ -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; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; + +export const Domain: Story = { + args: { + domain: 'example.com', + }, +}; + +export const CustomIcon: Story = { + args: { + icon: , + }, +}; + +export const Admin: Story = { + args: { + roleId: '1', + }, + render(args) { + return ; + }, +}; + +export const Group: Story = { + render(args) { + return ; + }, +}; + +export const Automated: Story = { + render(args) { + return ; + }, +}; + +export const Muted: Story = { + render(args) { + return ; + }, +}; + +export const Blocked: Story = { + render(args) { + return ; + }, +}; diff --git a/app/javascript/mastodon/components/badge.tsx b/app/javascript/mastodon/components/badge.tsx index b7dc169edb..0ffb7baa8a 100644 --- a/app/javascript/mastodon/components/badge.tsx +++ b/app/javascript/mastodon/components/badge.tsx @@ -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 = , label, className, domain, roleId }) => ( +} + +export const Badge: FC = ({ + icon = , + label, + className, + domain, + roleId, +}) => (
); -export const GroupBadge: FC<{ className?: string }> = ({ className }) => ( +export const AdminBadge: FC> = (props) => ( + } + label={ + + } + {...props} + /> +); + +export const GroupBadge: FC> = (props) => ( } label={ } - className={className} + {...props} /> ); @@ -44,3 +65,23 @@ export const AutomatedBadge: FC<{ className?: string }> = ({ className }) => ( className={className} /> ); + +export const MutedBadge: FC> = (props) => ( + } + label={ + + } + {...props} + /> +); + +export const BlockedBadge: FC> = (props) => ( + } + label={ + + } + {...props} + /> +); diff --git a/app/javascript/mastodon/features/account_timeline/components/account_header.tsx b/app/javascript/mastodon/features/account_timeline/components/account_header.tsx index 24a21a2011..8f7451636d 100644 --- a/app/javascript/mastodon/features/account_timeline/components/account_header.tsx +++ b/app/javascript/mastodon/features/account_timeline/components/account_header.tsx @@ -101,7 +101,7 @@ export const AccountHeader: React.FC<{ )}
- {me !== account.id && relationship && ( + {me !== account.id && relationship && !isRedesignEnabled() && ( )} diff --git a/app/javascript/mastodon/features/account_timeline/components/badges.tsx b/app/javascript/mastodon/features/account_timeline/components/badges.tsx index 1c7dca1ef5..3b4b1ca2c6 100644 --- a/app/javascript/mastodon/features/account_timeline/components/badges.tsx +++ b/app/javascript/mastodon/features/account_timeline/components/badges.tsx @@ -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(); - } else if (account.group) { - badges.push(); - } - const domain = account.acct.includes('@') ? account.acct.split('@')[1] : localDomain; account.roles.forEach((role) => { - let icon: ReactNode = undefined; if (isAdminBadge(role)) { - icon = ( - + badges.push( + , + ); + } else { + badges.push( + , ); } - badges.push( - , - ); }); + if (account.bot) { + badges.push(); + } + if (account.group) { + badges.push(); + } + if (isRedesignEnabled() && relationship) { + if (relationship.blocking) { + badges.push( + , + ); + } else if (relationship.domain_blocking) { + badges.push( + + } + />, + ); + } else if (relationship.muting) { + badges.push( + , + ); + } else if ( + relationship.followed_by && + (relationship.following || relationship.requested) + ) { + badges.push( + + } + className={className} + />, + ); + } else if (relationship.followed_by) { + badges.push( + + } + className={className} + />, + ); + } else if (relationship.requested_by) { + badges.push( + + } + className={className} + />, + ); + } + } + if (!badges.length) { return null; } diff --git a/app/javascript/mastodon/features/account_timeline/components/redesign.module.scss b/app/javascript/mastodon/features/account_timeline/components/redesign.module.scss index f7a0bb8bbf..80195a7a82 100644 --- a/app/javascript/mastodon/features/account_timeline/components/redesign.module.scss +++ b/app/javascript/mastodon/features/account_timeline/components/redesign.module.scss @@ -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; } diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 49448c4f33..0ed0b7599c 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -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",