Files
diplom-frontend/app/components/chat-message.tsx
2025-05-20 04:16:03 +03:00

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>
)
}