Release 2025-05-22-GmO6

This commit is contained in:
pluja
2025-05-22 11:10:18 +00:00
parent ed86f863e3
commit a69c0aeed4
20 changed files with 316 additions and 147 deletions

View File

@@ -2,7 +2,7 @@
import { cn } from '../lib/cn'
import { formatDateShort } from '../lib/timeAgo'
import MyPicture from './MyPicture.astro'
import UserBadge from './UserBadge.astro'
import type { Prisma } from '@prisma/client'
import type { HTMLAttributes } from 'astro/types'
@@ -15,6 +15,7 @@ export type ChatMessage = {
select: {
id: true
name: true
displayName: true
picture: true
}
}>
@@ -71,18 +72,7 @@ const { messages, userId, class: className, ...htmlProps } = Astro.props
)}
>
{!isCurrentUser && !isNextFromSameUser && (
<p class="text-day-500 mb-0.5 text-xs">
{!!message.user.picture && (
<MyPicture
src={message.user.picture}
height={20}
width={20}
class="inline-block size-5 rounded-full align-[-0.5em]"
alt=""
/>
)}
{message.user.name}
</p>
<UserBadge user={message.user} size="sm" class="text-day-500 mb-0.5 text-xs" />
)}
<p
class={cn(

View File

@@ -4,6 +4,7 @@ import { Markdown } from 'astro-remote'
import { Schema } from 'astro-seo-schema'
import { actions } from 'astro:actions'
import { commentStatusById } from '../constants/commentStatus'
import { karmaUnlocksById } from '../constants/karmaUnlocks'
import { getServiceUserRoleInfo } from '../constants/serviceUserRoles'
import { cn } from '../lib/cn'
@@ -18,9 +19,9 @@ import { formatDateShort } from '../lib/timeAgo'
import BadgeSmall from './BadgeSmall.astro'
import CommentModeration from './CommentModeration.astro'
import CommentReply from './CommentReply.astro'
import MyPicture from './MyPicture.astro'
import TimeFormatted from './TimeFormatted.astro'
import Tooltip from './Tooltip.astro'
import UserBadge from './UserBadge.astro'
import type { HTMLAttributes } from 'astro/types'
@@ -156,27 +157,11 @@ const commentUrl = makeCommentUrl({ serviceSlug, commentId: comment.id, origin:
</label>
<span class="flex items-center gap-1">
{
comment.author.picture && (
<MyPicture
src={comment.author.picture}
alt={`Profile for ${comment.author.displayName ?? comment.author.name}`}
class="size-6 rounded-full bg-zinc-700 object-cover"
height={24}
width={24}
/>
)
}
<a
href={`/u/${comment.author.name}`}
class={cn([
'font-title text-day-300 font-medium hover:underline focus-visible:underline',
isAuthor && 'font-medium text-green-500',
])}
>
{comment.author.displayName ?? comment.author.name}
</a>
<UserBadge
user={comment.author}
size="md"
class={cn('text-day-300', isAuthor && 'font-medium text-green-500')}
/>
{
(comment.author.verified || comment.author.admin || comment.author.verifier) && (
@@ -307,20 +292,35 @@ const commentUrl = makeCommentUrl({ serviceSlug, commentId: comment.id, origin:
{
comment.status === 'VERIFIED' && (
<BadgeSmall icon="ri:check-double-fill" color="green" text="Verified" inlineIcon />
<BadgeSmall
icon={commentStatusById.VERIFIED.icon}
color={commentStatusById.VERIFIED.color}
text={commentStatusById.VERIFIED.label}
inlineIcon
/>
)
}
{
(comment.status === 'PENDING' || comment.status === 'HUMAN_PENDING') &&
(showPending || isHighlightParent || isAuthorOrPrivileged) && (
<BadgeSmall icon="ri:time-fill" color="yellow" text="Unmoderated" inlineIcon />
<BadgeSmall
icon={commentStatusById.PENDING.icon}
color={commentStatusById.PENDING.color}
text={commentStatusById.PENDING.label}
inlineIcon
/>
)
}
{
comment.status === 'REJECTED' && isAuthorOrPrivileged && (
<BadgeSmall icon="ri:close-circle-fill" color="red" text="Rejected" inlineIcon />
<BadgeSmall
icon={commentStatusById.REJECTED.icon}
color={commentStatusById.REJECTED.color}
text={commentStatusById.REJECTED.label}
inlineIcon
/>
)
}

View File

@@ -11,6 +11,7 @@ import InputHoneypotTrap from './InputHoneypotTrap.astro'
import InputRating from './InputRating.astro'
import InputText from './InputText.astro'
import InputWrapper from './InputWrapper.astro'
import UserBadge from './UserBadge.astro'
import type { Prisma } from '@prisma/client'
import type { HTMLAttributes } from 'astro/types'
@@ -67,7 +68,7 @@ const userCommentsDisabled = user ? user.karmaUnlocks.commentsDisabled : false
<div class="text-day-400 flex items-center gap-2 text-xs peer-checked/use-form-secret-token:hidden">
<Icon name="ri:user-line" class="size-3.5" />
<span>
Commenting as: <span class="font-title font-medium text-green-400">{user.name}</span>
Commenting as: <UserBadge user={user} size="sm" class="text-green-400" />
</span>
</div>

View File

@@ -12,6 +12,7 @@ import HeaderNotificationIndicator from './HeaderNotificationIndicator.astro'
import HeaderSplashTextScript from './HeaderSplashTextScript.astro'
import Logo from './Logo.astro'
import Tooltip from './Tooltip.astro'
import UserBadge from './UserBadge.astro'
const user = Astro.locals.user
const actualUser = Astro.locals.actualUser
@@ -131,9 +132,12 @@ const splashText = showSplashText ? sample(splashTexts) : null
user ? (
<>
{actualUser && (
<span class="text-sm text-white/40 hover:text-white" transition:name="header-actual-user-name">
({actualUser.name})
</span>
<UserBadge
user={actualUser}
size="sm"
class="text-white/40 hover:text-white"
transition:name="header-actual-user-name"
/>
)}
<HeaderNotificationIndicator
@@ -141,13 +145,17 @@ const splashText = showSplashText ? sample(splashTexts) : null
transition:name="header-notification-indicator"
/>
<a
<UserBadge
href="/account"
class="xs:px-3 2xs:px-2 last:xs:-mr-3 last:2xs:-mr-2 flex h-full items-center px-1 text-sm text-zinc-400 transition-colors last:-mr-1 hover:text-zinc-300"
user={user}
size="md"
class="xs:px-3 2xs:px-2 last:xs:-mr-3 last:2xs:-mr-2 h-full px-1 text-zinc-400 transition-colors last:-mr-1 hover:text-zinc-300"
classNames={{
image: 'max-2xs:hidden',
}}
transition:name="header-user-link"
>
{user.name}
</a>
/>
{actualUser ? (
<a
href={makeUnimpersonateUrl(Astro.url)}

View File

@@ -0,0 +1,87 @@
---
import { tv, type VariantProps } from 'tailwind-variants'
import { getSizePxFromTailwindClasses } from '../lib/tailwind'
import MyPicture from './MyPicture.astro'
import type { Prisma } from '@prisma/client'
import type { HTMLAttributes } from 'astro/types'
import type { O } from 'ts-toolbelt'
const userBadge = tv({
slots: {
base: 'group/user-badge font-title inline-flex max-w-full items-center gap-1 overflow-hidden font-medium',
image: 'inline-block rounded-full object-cover',
text: 'truncate',
},
variants: {
size: {
sm: {
base: 'gap-1 text-xs',
image: 'size-4',
},
md: {
base: 'gap-2 text-sm',
image: 'size-5',
},
lg: {
base: 'gap-2 text-base',
image: 'size-6',
},
},
noLink: {
true: {
text: 'cursor-default',
},
false: {
base: 'cursor-pointer',
text: 'group-hover/user-badge:underline',
},
},
},
defaultVariants: {
size: 'sm',
noLink: false,
},
})
type Props = O.Optional<HTMLAttributes<'a'>, 'href'> &
VariantProps<typeof userBadge> & {
user: Prisma.UserGetPayload<{
select: {
name: true
displayName: true
picture: true
}
}>
classNames?: {
image?: string
text?: string
}
children?: never
}
const { user, href, class: className, size = 'sm', classNames, noLink = false, ...htmlProps } = Astro.props
const { base, image, text } = userBadge({ size, noLink })
const imageClassName = image({ class: classNames?.image })
const imageSizePx = getSizePxFromTailwindClasses(imageClassName, 16)
const Tag = noLink ? 'span' : 'a'
---
<Tag
href={Tag === 'a' ? (href ?? `/u/${user.name}`) : undefined}
class={base({ class: className })}
{...htmlProps}
>
{
!!user.picture && (
<MyPicture src={user.picture} height={imageSizePx} width={imageSizePx} class={imageClassName} alt="" />
)
}
<span class={text({ class: classNames?.text })}>
{user.displayName ?? user.name}
</span>
</Tag>

View File

@@ -19,6 +19,11 @@ type Props = {
verificationSummary: true
listedAt: true
createdAt: true
verificationSteps: {
select: {
status: true
}
}
}
}>
}
@@ -67,3 +72,18 @@ const wasRecentlyAdded = isPast(listedDate) && differenceInDays(new Date(), list
</div>
) : null
}
{
service.verificationStatus !== 'VERIFICATION_FAILED' &&
service.verificationSteps.some((step) => step.status === 'FAILED') && (
<div class="mb-3 flex items-center gap-2 rounded-md bg-red-900/50 p-2 text-sm text-red-400">
<Icon
name={verificationStatusesByValue.VERIFICATION_FAILED.icon}
class={cn('size-5', verificationStatusesByValue.VERIFICATION_FAILED.classNames.icon)}
/>
<span>
This service has failed one or more verification steps. Review the verification details carefully.
</span>
</div>
)
}