Files
kycnotme/web/src/components/ChatMessages.astro
2025-05-22 11:10:18 +00:00

99 lines
3.2 KiB
Plaintext

---
import { cn } from '../lib/cn'
import { formatDateShort } from '../lib/timeAgo'
import UserBadge from './UserBadge.astro'
import type { Prisma } from '@prisma/client'
import type { HTMLAttributes } from 'astro/types'
export type ChatMessage = {
id: number
content: string
createdAt: Date
user: Prisma.UserGetPayload<{
select: {
id: true
name: true
displayName: true
picture: true
}
}>
}
type Props = HTMLAttributes<'div'> & {
messages: ChatMessage[]
userId: number | null
}
const { messages, userId, class: className, ...htmlProps } = Astro.props
---
<div
class={cn(
'mb-1 flex max-h-[60dvh] flex-col-reverse overflow-y-auto mask-t-from-[calc(100%-var(--spacing)*16)] pt-16',
className
)}
{...htmlProps}
>
<p
class="sticky bottom-0 -z-1 flex min-h-7 w-full items-end justify-center text-center text-xs text-balance text-gray-500"
>
<span class="js:hidden">Refresh the page to see new messages</span>
<span class="no-js:hidden" data-refresh-in="10">Refreshing every 10s</span>
</p>
{
messages.length > 0 ? (
messages
.map((message) => ({
...message,
formattedCreatedAt: formatDateShort(message.createdAt, {
prefix: false,
hourPrecision: true,
caseType: 'sentence',
}),
}))
.map((message, index, messages) => {
const isCurrentUser = message.user.id === userId
const prev = messages[index - 1]
const next = messages[index + 1]
const isPrevFromSameUser = !!prev && prev.user.id === message.user.id
const isPrevSameDate = !!prev && prev.formattedCreatedAt === message.formattedCreatedAt
const isNextFromSameUser = !!next && next.user.id === message.user.id
const isNextSameDate = !!next && next.formattedCreatedAt === message.formattedCreatedAt
return (
<div
class={cn(
'flex flex-col',
isCurrentUser ? 'ml-8 items-end' : 'mr-8 items-start',
isNextFromSameUser ? 'mt-1' : 'mt-3'
)}
>
{!isCurrentUser && !isNextFromSameUser && (
<UserBadge user={message.user} size="sm" class="text-day-500 mb-0.5 text-xs" />
)}
<p
class={cn(
'rounded-xl p-3 text-sm wrap-anywhere whitespace-pre-wrap',
isCurrentUser ? 'bg-blue-900 text-white' : 'bg-night-500 text-day-300',
isCurrentUser ? 'rounded-br-xs' : 'rounded-bl-xs',
isCurrentUser && isNextFromSameUser && isNextSameDate && 'rounded-tr-xs',
!isCurrentUser && isNextFromSameUser && isNextSameDate && 'rounded-tl-xs'
)}
id={`message-${message.id.toString()}`}
set:text={message.content}
/>
{(!isPrevFromSameUser || !isPrevSameDate) && (
<p class="text-day-500 mt-0.5 mb-2 text-xs">{message.formattedCreatedAt}</p>
)}
</div>
)
})
) : (
<div class="text-day-500 my-16 text-center text-sm italic">No messages yet</div>
)
}
</div>