Merge branch 'mastodon-badges' into 'main'
Support role badges on Mastodon See merge request soapbox-pub/soapbox!2985
This commit is contained in:
commit
1518615767
4 changed files with 39 additions and 10 deletions
|
@ -1,18 +1,32 @@
|
|||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { hexToHsl } from 'soapbox/utils/theme';
|
||||
|
||||
interface IBadge {
|
||||
title: React.ReactNode;
|
||||
slug: string;
|
||||
color?: string;
|
||||
}
|
||||
/** Badge to display on a user's profile. */
|
||||
const Badge: React.FC<IBadge> = ({ title, slug }) => {
|
||||
const Badge: React.FC<IBadge> = ({ title, slug, color }) => {
|
||||
const fallback = !['patron', 'admin', 'moderator', 'opaque', 'badge:donor'].includes(slug);
|
||||
|
||||
const isDark = useMemo(() => {
|
||||
const hsl = hexToHsl(color!);
|
||||
|
||||
if (hsl && hsl.l > 50) return false;
|
||||
|
||||
return true;
|
||||
}, [color]);
|
||||
|
||||
return (
|
||||
<span
|
||||
data-testid='badge'
|
||||
className={clsx('inline-flex items-center rounded px-2 py-0.5 text-xs font-medium', {
|
||||
className={clsx('inline-flex items-center rounded px-2 py-0.5 text-xs font-medium', color ? {
|
||||
'bg-gray-100 text-gray-100': isDark,
|
||||
'bg-gray-800 text-gray-900': !isDark,
|
||||
} : {
|
||||
'bg-fuchsia-700 text-white': slug === 'patron',
|
||||
'bg-emerald-800 text-white': slug === 'badge:donor',
|
||||
'bg-black text-white': slug === 'admin',
|
||||
|
@ -20,6 +34,7 @@ const Badge: React.FC<IBadge> = ({ title, slug }) => {
|
|||
'bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100': fallback,
|
||||
'bg-white/75 text-gray-900': slug === 'opaque',
|
||||
})}
|
||||
style={color ? { background: color } : undefined}
|
||||
>
|
||||
{title}
|
||||
</span>
|
||||
|
|
|
@ -7,7 +7,6 @@ import Markup from 'soapbox/components/markup';
|
|||
import { dateFormatOptions } from 'soapbox/components/relative-timestamp';
|
||||
import { Icon, HStack, Stack, Text } from 'soapbox/components/ui';
|
||||
import { useAppSelector, useSoapboxConfig } from 'soapbox/hooks';
|
||||
import { badgeToTag, getBadges as getAccountBadges } from 'soapbox/utils/badges';
|
||||
import { capitalize } from 'soapbox/utils/strings';
|
||||
|
||||
import ProfileFamiliarFollowers from './profile-familiar-followers';
|
||||
|
@ -58,13 +57,14 @@ const ProfileInfoPanel: React.FC<IProfileInfoPanel> = ({ account, username }) =>
|
|||
};
|
||||
|
||||
const getCustomBadges = (): React.ReactNode[] => {
|
||||
const badges = account ? getAccountBadges(account) : [];
|
||||
const badges = account?.roles || [];
|
||||
|
||||
return badges.map(badge => (
|
||||
return badges.filter(badge => badge.highlighted).map(badge => (
|
||||
<Badge
|
||||
key={badge}
|
||||
slug={badge}
|
||||
title={capitalize(badgeToTag(badge))}
|
||||
key={badge.id || badge.name}
|
||||
slug={badge.name}
|
||||
title={capitalize(badge.name)}
|
||||
color={badge.color}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
|
|
@ -17,12 +17,21 @@ const headerMissing = require('soapbox/assets/images/header-missing.png');
|
|||
|
||||
const birthdaySchema = z.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
||||
|
||||
const hexSchema = z.string().regex(/^#[a-f0-9]{6}$/i);
|
||||
|
||||
const fieldSchema = z.object({
|
||||
name: z.string(),
|
||||
value: z.string(),
|
||||
verified_at: z.string().datetime().nullable().catch(null),
|
||||
});
|
||||
|
||||
const roleSchema = z.object({
|
||||
id: z.string().catch(''),
|
||||
name: z.string().catch(''),
|
||||
color: hexSchema.catch(''),
|
||||
highlighted: z.boolean().catch(true),
|
||||
});
|
||||
|
||||
const baseAccountSchema = z.object({
|
||||
acct: z.string().catch(''),
|
||||
avatar: z.string().catch(avatarMissing),
|
||||
|
@ -85,6 +94,7 @@ const baseAccountSchema = z.object({
|
|||
relationship: relationshipSchema.optional().catch(undefined),
|
||||
tags: z.array(z.string()).catch([]),
|
||||
}).optional().catch(undefined),
|
||||
roles: filteredArray(roleSchema),
|
||||
source: z.object({
|
||||
approved: z.boolean().catch(true),
|
||||
chats_onboarded: z.boolean().catch(true),
|
||||
|
@ -118,6 +128,9 @@ const getDomain = (url: string) => {
|
|||
}
|
||||
};
|
||||
|
||||
const filterBadges = (tags?: string[]) =>
|
||||
tags?.filter(tag => tag.startsWith('badge:')).map(tag => ({ id: tag, name: tag.replace(/^badge:/, '') }));
|
||||
|
||||
/** Add internal fields to the account. */
|
||||
const transformAccount = <T extends TransformableAccount>({ pleroma, other_settings, fields, ...account }: T) => {
|
||||
const customEmojiMap = makeCustomEmojiMap(account.emojis);
|
||||
|
@ -156,6 +169,7 @@ const transformAccount = <T extends TransformableAccount>({ pleroma, other_setti
|
|||
const { relationship, ...rest } = pleroma;
|
||||
return rest;
|
||||
})(),
|
||||
roles: account.roles || filterBadges(pleroma?.tags),
|
||||
relationship: pleroma?.relationship,
|
||||
staff: pleroma?.is_admin || pleroma?.is_moderator || false,
|
||||
suspended: account.suspended || pleroma?.deactivated || false,
|
||||
|
|
|
@ -117,7 +117,7 @@ export const generateThemeCss = (soapboxConfig: SoapboxConfig): string => {
|
|||
return colorsToCss(soapboxConfig.colors.toJS() as TailwindColorPalette);
|
||||
};
|
||||
|
||||
const hexToHsl = (hex: string): Hsl | null => {
|
||||
export const hexToHsl = (hex: string): Hsl | null => {
|
||||
const rgb = hexToRgb(hex);
|
||||
return rgb ? rgbToHsl(rgb) : null;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue