Files
diplom-frontend/app/components/chat-message.tsx
2025-05-21 08:46:12 +03:00

113 lines
4.0 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";
import UserContextMenu from "./user-context-menu";
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">
<UserContextMenu userId={message.authorId}>
<UserAvatar user={user} />
</UserContextMenu>
</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">
<UserContextMenu userId={message.authorId}>
<div>{user?.displayName || user?.username}</div>
</UserContextMenu>
</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="grid gap-2 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 justify-start">
{message.attachments.map((file, i) => (
<div key={file.id}>
<ChatMessageAttachment file={file} />
</div>
))}
</div>
</div>
</div>
);
}