Release 2025-05-22-GmO6
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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)}
|
||||
|
||||
87
web/src/components/UserBadge.astro
Normal file
87
web/src/components/UserBadge.astro
Normal 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>
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user