86 lines
3.4 KiB
TypeScript
86 lines
3.4 KiB
TypeScript
import { Clock } from "lucide-react"
|
|
import React from "react"
|
|
import { useShallow } from "zustand/react/shallow"
|
|
import type { Message } from "~/lib/api/types"
|
|
import { useUsersStore } from "~/stores/users-store"
|
|
import ChatMessageAttachment from "./chat-message-attachment"
|
|
import UserAvatar from "./user-avatar"
|
|
|
|
interface ChatMessageProps {
|
|
message: Message
|
|
}
|
|
|
|
export default function ChatMessage(
|
|
{ message }: ChatMessageProps
|
|
) {
|
|
const { user, fetchUsersIfNotPresent } = useUsersStore(useShallow(state => ({
|
|
user: state.users[message.authorId],
|
|
fetchUsersIfNotPresent: state.fetchUsersIfNotPresent
|
|
})))
|
|
|
|
React.useEffect(() => {
|
|
fetchUsersIfNotPresent([message.authorId])
|
|
}, [])
|
|
|
|
const formatMessageDate = (date: Date) => {
|
|
const now = new Date()
|
|
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
|
const yesterday = new Date(today)
|
|
yesterday.setDate(yesterday.getDate() - 1)
|
|
|
|
const messageDate = new Date(date.getFullYear(), date.getMonth(), date.getDate())
|
|
|
|
// Get localized time string
|
|
const timeString = date.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', hour12: false })
|
|
const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1)
|
|
|
|
if (messageDate.getTime() === today.getTime()) {
|
|
// Use Intl.RelativeTimeFormat for localized "Today"
|
|
const rtf = new Intl.RelativeTimeFormat(undefined, { numeric: 'auto' })
|
|
return `${(capitalize(rtf.format(0, 'day')))}, ${timeString}`
|
|
} else if (messageDate.getTime() === yesterday.getTime()) {
|
|
// Use Intl.RelativeTimeFormat for localized "Yesterday"
|
|
const rtf = new Intl.RelativeTimeFormat(undefined, { numeric: 'auto' })
|
|
return `${capitalize(rtf.format(-1, 'day'))}, ${timeString}`
|
|
} else {
|
|
return date.toLocaleDateString(undefined, {
|
|
month: 'short',
|
|
day: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
hour12: false
|
|
})
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="grid gap-x-2" style={{ gridTemplateColumns: "auto 1fr", gridTemplateRows: "auto 1fr" }}>
|
|
<div className="row-start-1 col-start-1 row-span-2 col-span-1">
|
|
<UserAvatar user={user} />
|
|
</div>
|
|
<div className="row-start-1 col-start-2 row-span-1 col-span-1 flex items-center gap-2">
|
|
<span className="font-medium text-sm">
|
|
{user?.displayName || user?.username}
|
|
</span>
|
|
<div className="flex items-center gap-0.5 text-xs text-muted-foreground whitespace-nowrap">
|
|
<Clock className="size-3" />
|
|
<span>
|
|
{formatMessageDate(message.createdAt)}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div className="row-start-2 col-start-2 row-span-1 col-span-1">
|
|
<div className="wrap-break-word contain-inline-size">
|
|
{message.content}
|
|
</div>
|
|
<div className="flex flex-col gap-2">
|
|
{
|
|
message.attachments.map((file, i) => (<div key={file.id}>
|
|
<ChatMessageAttachment file={file} />
|
|
</div>))
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
} |