Display new badges on new cards

This commit is contained in:
Luis Rojas
2024-04-23 16:29:56 -04:00
parent 718c0aa039
commit 8aba2c014b
10 changed files with 154 additions and 204 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

+2 -2
View File
@@ -4,7 +4,7 @@ import { IconList, IconPaperclip } from '@tabler/icons-react';
import { AttachmentCard } from '~/components/Article/Detail/AttachmentCard';
import { TableOfContent } from '~/components/Article/Detail/TableOfContent';
import { CreatorCardV2 } from '~/components/CreatorCard/CreatorCard';
import { SmartCreatorCard } from '~/components/CreatorCard/CreatorCard';
import { useHeadingsData } from '~/hooks/useHeadingsData';
import { hideMobile } from '~/libs/sx-helpers';
import type { ArticleGetById } from '~/server/services/article.service';
@@ -78,7 +78,7 @@ export function Sidebar({ articleId, attachments, creator }: Props) {
</Stack>
</Accordion>
)}
<CreatorCardV2 user={creator} tipBuzzEntityId={articleId} tipBuzzEntityType="Article" />
<SmartCreatorCard user={creator} tipBuzzEntityId={articleId} tipBuzzEntityType="Article" />
</Stack>
</aside>
);
+2 -2
View File
@@ -9,7 +9,7 @@ import { showErrorNotification, showSuccessNotification } from '~/utils/notifica
import { BountyGetById } from '~/types/router';
import { useCurrentUser } from '~/hooks/useCurrentUser';
import { getBountyCurrency } from '~/components/Bounty/bounty.utils';
import { CreatorCardV2 } from '~/components/CreatorCard/CreatorCard';
import { SmartCreatorCard } from '~/components/CreatorCard/CreatorCard';
import { formatDate } from '~/utils/date-helpers';
import { useTrackEvent } from '../TrackView/track.utils';
@@ -172,7 +172,7 @@ export const AwardBountyAction = ({
<Text size="xs" color="dimmed">
Entry added on {formatDate(bountyEntry.createdAt)} by
</Text>
<CreatorCardV2 user={bountyEntry.user} />
<SmartCreatorCard user={bountyEntry.user} />
</Stack>
</Center>
)}
+52 -80
View File
@@ -22,7 +22,7 @@ import { formatDate } from '~/utils/date-helpers';
import { sortDomainLinks } from '~/utils/domain-link';
import { trpc } from '~/utils/trpc';
import { TipBuzzButton } from '../Buzz/TipBuzzButton';
import { UserStatBadges } from '../UserStatBadges/UserStatBadges';
import { UserStatBadges, UserStatBadgesV2 } from '../UserStatBadges/UserStatBadges';
import { getEdgeUrl } from '~/client-utils/cf-images-utils';
import {
BadgeCosmetic,
@@ -35,6 +35,7 @@ import { isDefined } from '~/utils/type-guards';
import { BadgeDisplay, Username } from '../User/Username';
import { createServerSideProps } from '~/server/utils/server-side-helpers';
import { UserPublicSettingsSchema } from '~/server/schema/user.schema';
import { useFeatureFlags } from '~/providers/FeatureFlagsProvider';
const useStyles = createStyles((theme) => ({
profileDetailsContainer: {
@@ -66,6 +67,7 @@ export function CreatorCard({
tipBuzzEntityType,
tipBuzzEntityId,
withActions = true,
...cardProps
}: Props) {
const { data } = trpc.user.getCreator.useQuery(
{ id: user.id },
@@ -90,86 +92,46 @@ export function CreatorCard({
if (!creator || user.id === -1) return null;
const backgroundImage = creator.cosmetics.find(({ cosmetic }) =>
cosmetic ? cosmetic.type === 'ProfileBackground' : undefined
)?.cosmetic as Omit<ProfileBackgroundCosmetic, 'description' | 'obtainedAt'>;
return (
<Card p="xs" withBorder>
<Card p="xs" withBorder {...cardProps}>
<Card.Section>
<BackgroundImage
sx={{
backgroundRepeat: 'no-repeat',
backgroundPosition: 'right',
backgroundSize: 'cover',
}}
src={
backgroundImage && backgroundImage.data.url
? getEdgeUrl(backgroundImage.data.url, {
width: 'original',
transcode: false,
})
: '/images/civitai-default-account-bg.png'
}
>
<Stack spacing="xs" p="xs">
<Group align="center" position="apart">
<UserAvatar
size="sm"
avatarProps={{ size: 32 }}
user={creator}
subText={creator.createdAt ? `Joined ${formatDate(creator.createdAt)}` : undefined}
withOverlay={!!backgroundImage}
withUsername
linkToProfile
/>
{withActions && (
<Group spacing={8} noWrap>
<TipBuzzButton
toUserId={creator.id}
size="xs"
entityId={tipBuzzEntityId}
label=""
entityType={tipBuzzEntityType}
styles={
backgroundImage ? applyCosmeticThemeColors(backgroundImage.data) : undefined
}
compact
/>
<ChatUserButton
user={creator}
size="xs"
label=""
styles={
backgroundImage ? applyCosmeticThemeColors(backgroundImage.data) : undefined
}
compact
/>
<FollowUserButton
userId={creator.id}
size="xs"
styles={
backgroundImage ? applyCosmeticThemeColors(backgroundImage.data) : undefined
}
compact
/>
</Group>
)}
</Group>
<Group spacing={8}>
<RankBadge size="md" rank={creator.rank} />
{stats && (
<UserStatBadges
uploads={uploads}
followers={stats.followerCountAllTime}
favorites={stats.thumbsUpCountAllTime}
downloads={stats.downloadCountAllTime}
colorOverrides={backgroundImage?.data}
<Stack spacing="xs" p="xs">
<Group align="center" position="apart">
<UserAvatar
size="sm"
avatarProps={{ size: 32 }}
user={creator}
subText={creator.createdAt ? `Joined ${formatDate(creator.createdAt)}` : undefined}
withUsername
linkToProfile
/>
{withActions && (
<Group spacing={8} noWrap>
<TipBuzzButton
toUserId={creator.id}
size="xs"
entityId={tipBuzzEntityId}
label=""
entityType={tipBuzzEntityType}
compact
/>
)}
</Group>
</Stack>
</BackgroundImage>
<ChatUserButton user={creator} size="xs" label="" compact />
<FollowUserButton userId={creator.id} size="xs" compact />
</Group>
)}
</Group>
<Group spacing={8}>
<RankBadge size="md" rank={creator.rank} />
{stats && (
<UserStatBadges
uploads={uploads}
followers={stats.followerCountAllTime}
favorites={stats.thumbsUpCountAllTime}
downloads={stats.downloadCountAllTime}
/>
)}
</Group>
</Stack>
</Card.Section>
{creator.links && creator.links.length > 0 ? (
<Card.Section
@@ -289,7 +251,7 @@ export const CreatorCardV2 = ({
<Group spacing={8}>
<RankBadge size="md" rank={creator.rank} />
{stats && displayStats.length > 0 && (
<UserStatBadges
<UserStatBadgesV2
uploads={displayStats.includes('uploads') ? uploads : null}
followers={
displayStats.includes('followers') ? stats.followerCountAllTime : null
@@ -414,7 +376,7 @@ type Props = {
tipBuzzEntityId?: number;
tipBuzzEntityType?: string;
withActions?: boolean;
};
} & Omit<CardProps, 'children'>;
type PropsV2 = {
user: UserWithCosmetics;
@@ -425,3 +387,13 @@ type PropsV2 = {
useEquippedCosmetics?: boolean;
startDisplayOverwrite?: string[];
} & Omit<CardProps, 'children'>;
export const SmartCreatorCard = (props: PropsV2) => {
const featureFlags = useFeatureFlags();
if (featureFlags.cosmeticShop) {
return <CreatorCardV2 {...props} />;
}
return <CreatorCard {...props} />;
};
+1 -1
View File
@@ -42,7 +42,7 @@ export function IconBadge({ icon, children, tooltip, href, ...props }: IconBadge
}
export type IconBadgeProps = {
icon: React.ReactNode;
icon?: React.ReactNode;
tooltip?: React.ReactNode;
onClick?: React.MouseEventHandler<any> | undefined; //eslint-disable-line
href?: string;
+2 -2
View File
@@ -56,7 +56,7 @@ import { containerQuery } from '~/utils/mantine-css-helpers';
import { ProfileBackgroundCosmetic } from '~/server/selectors/cosmetic.selector';
import { getEdgeUrl } from '~/client-utils/cf-images-utils';
import { applyCosmeticThemeColors } from '~/libs/sx-helpers';
import { CreatorCardV2 } from '~/components/CreatorCard/CreatorCard';
import { SmartCreatorCard } from '~/components/CreatorCard/CreatorCard';
export function ImageDetail() {
const { classes, cx, theme } = useStyles();
@@ -113,7 +113,7 @@ export function ImageDetail() {
</div>
<Card className={cx(classes.sidebar)}>
<Card.Section withBorder>
<CreatorCardV2 user={image.user} style={{ border: 0 }} />
<SmartCreatorCard user={image.user} style={{ border: 0 }} />
</Card.Section>
<Card.Section
py="xs"
@@ -44,7 +44,7 @@ import { AlertWithIcon } from '~/components/AlertWithIcon/AlertWithIcon';
import { useCivitaiLink } from '~/components/CivitaiLink/CivitaiLinkProvider';
import { CivitaiLinkManageButton } from '~/components/CivitaiLink/CivitaiLinkManageButton';
import { ContentClamp } from '~/components/ContentClamp/ContentClamp';
import { CreatorCard, CreatorCardV2 } from '~/components/CreatorCard/CreatorCard';
import { CreatorCard, SmartCreatorCard } from '~/components/CreatorCard/CreatorCard';
import {
DescriptionTable,
type Props as DescriptionTableProps,
@@ -1026,7 +1026,11 @@ export function ModelVersionDetails({
)}
</Accordion>
<CreatorCardV2 user={model.user} tipBuzzEntityType="Model" tipBuzzEntityId={model.id} />
<SmartCreatorCard
user={model.user}
tipBuzzEntityType="Model"
tipBuzzEntityId={model.id}
/>
{onSite && (
<Group
align="flex-start"
+86 -113
View File
@@ -1,10 +1,12 @@
import { Badge, Group, MantineSize, Text, useMantineTheme } from '@mantine/core';
import { Badge, BadgeProps, Box, Group, MantineSize, Text, useMantineTheme } from '@mantine/core';
import { IconUpload, IconUsers, IconDownload, IconChecks } from '@tabler/icons-react';
import { IconBadge } from '~/components/IconBadge/IconBadge';
import { abbreviateNumber } from '~/utils/number-helpers';
import { StatTooltip } from '~/components/Tooltips/StatTooltip';
import { ThumbsUpIcon } from '~/components/ThumbsIcon/ThumbsIcon';
import { EdgeMedia } from '~/components/EdgeMedia/EdgeMedia';
import Image from 'next/image';
export function UserStatBadges({
followers,
@@ -134,6 +136,53 @@ export function UserStatBadges({
);
}
const BadgedIcon = ({
label,
icon,
value,
size = 'md',
textSize = 'xs',
...props
}: {
label: string;
value: number | string;
icon: React.ReactNode;
textSize?: MantineSize;
} & Omit<BadgeProps, 'leftSection'>) => (
<Group spacing={0} noWrap sx={{ position: 'relative' }}>
<Box pos="relative" sx={{ zIndex: 2, overflow: 'hidden' }} h={32}>
<Image src="/images/base-badge.png" alt={`${label} - ${value}`} width={32} height={32} />
<Box
style={{
top: '50%',
left: '50%',
position: 'absolute',
transform: 'translate(-50%, -50%)',
}}
>
{icon}
</Box>
</Box>
<IconBadge
size={size}
color="gray"
// @ts-ignore
variant="light"
sx={{
paddingLeft: 14,
marginLeft: -14,
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
}}
{...props}
>
<Text size={textSize} inline>
{value}
</Text>
</IconBadge>
</Group>
);
export function UserStatBadgesV2({
followers,
favorites,
@@ -146,118 +195,42 @@ export function UserStatBadgesV2({
const theme = useMantineTheme();
return (
<Group spacing={8} position="apart">
<Badge
size="lg"
radius="xl"
px={8}
color="dark"
sx={
colorOverrides
? { backgroundColor: colorOverrides.backgroundColor ?? undefined }
: undefined
}
variant={theme.colorScheme === 'dark' ? 'filled' : 'light'}
>
<Group spacing="xs" noWrap>
{uploads != null ? (
<IconBadge
p={0}
tooltip={<StatTooltip label="Uploads" value={uploads} />}
icon={<IconUpload size={14} />}
sx={
colorOverrides
? { color: colorOverrides.textColor ?? theme.colors.gray[0] }
: undefined
}
size="lg"
// @ts-ignore: transparent variant does work
variant="transparent"
>
<Text size="xs" weight={600} inline>
{abbreviateNumber(uploads)}
</Text>
</IconBadge>
) : null}
{followers != null ? (
<IconBadge
p={0}
tooltip={<StatTooltip label="Followers" value={followers} />}
href={username ? `/user/${username}/followers` : undefined}
icon={<IconUsers size={14} />}
sx={
colorOverrides
? { color: colorOverrides.textColor ?? theme.colors.gray[0] }
: undefined
}
size="lg"
// @ts-ignore: transparent variant does work
variant="transparent"
>
<Text size="xs" weight={600} inline>
{abbreviateNumber(followers)}
</Text>
</IconBadge>
) : null}
{favorites != null ? (
<IconBadge
p={0}
tooltip={<StatTooltip label="Likes" value={favorites} />}
icon={<ThumbsUpIcon size={14} />}
sx={
colorOverrides
? { color: colorOverrides.textColor ?? theme.colors.gray[0] }
: undefined
}
// @ts-ignore: transparent variant does work
variant="transparent"
size="lg"
>
<Text size="xs" weight={600} inline>
{abbreviateNumber(favorites)}
</Text>
</IconBadge>
) : null}
{downloads != null ? (
<IconBadge
p={0}
tooltip={<StatTooltip label="Downloads" value={downloads} />}
icon={<IconDownload size={14} />}
sx={
colorOverrides
? { color: colorOverrides.textColor ?? theme.colors.gray[0] }
: undefined
}
// @ts-ignore: transparent variant does work
variant="transparent"
size="lg"
>
<Text size="xs" weight={600} inline>
{abbreviateNumber(downloads)}
</Text>
</IconBadge>
) : null}
{answers != null && answers > 0 ? (
<IconBadge
p={0}
tooltip={<StatTooltip label="Answers" value={answers} />}
icon={<IconChecks size={14} />}
sx={
colorOverrides
? { color: colorOverrides.textColor ?? theme.colors.gray[0] }
: undefined
}
// @ts-ignore: transparent variant does work
variant="transparent"
size="lg"
>
<Text size="xs" weight={600} inline>
{abbreviateNumber(answers)}
</Text>
</IconBadge>
) : null}
</Group>
</Badge>
<Group spacing="xs" noWrap>
{uploads != null ? (
<BadgedIcon
icon={<IconUpload size={18} color="white" />}
label="Uploads"
value={abbreviateNumber(uploads)}
/>
) : null}
{followers != null ? (
<BadgedIcon
icon={<IconUsers size={18} color="white" />}
label="Followers"
value={abbreviateNumber(followers)}
/>
) : null}
{favorites != null ? (
<BadgedIcon
icon={<ThumbsUpIcon size={18} color="white" />}
label="Likes"
value={abbreviateNumber(favorites)}
/>
) : null}
{downloads != null ? (
<BadgedIcon
icon={<IconDownload size={18} color="white" />}
label="Downloads"
value={abbreviateNumber(downloads)}
/>
) : null}
{answers != null && answers > 0 ? (
<BadgedIcon
icon={<IconChecks size={18} color="white" />}
label="Answers"
value={abbreviateNumber(answers)}
/>
) : null}
</Group>
);
}
@@ -64,7 +64,7 @@ import { IconDotsVertical } from '@tabler/icons-react';
import { ReportMenuItem } from '~/components/MenuItems/ReportMenuItem';
import { ReportEntity } from '~/server/schema/report.schema';
import { openContext } from '~/providers/CustomModalsProvider';
import { CreatorCardV2 } from '~/components/CreatorCard/CreatorCard';
import { SmartCreatorCard } from '~/components/CreatorCard/CreatorCard';
import { formatDate } from '~/utils/date-helpers';
import { TrackView } from '~/components/TrackView/TrackView';
import { RenderHtml } from '~/components/RenderHtml/RenderHtml';
@@ -262,7 +262,7 @@ export default function BountyEntryDetailsPage({
)}
</NavigateBack>
</Group>
<CreatorCardV2 user={user} />
<SmartCreatorCard user={user} />
</Stack>
</Card.Section>
)}
@@ -89,6 +89,7 @@ const featureFlags = createFeatureFlags({
vault: ['mod'],
draftMode: ['mod'],
membershipsV2: ['mod'],
cosmeticShop: ['mod'],
});
export const featureFlagKeys = Object.keys(featureFlags) as FeatureFlagKey[];