Files
spsays-training/components/chat/reactions-drawer.tsx
T
2026-08-30 01:51:52 +05:30

84 lines
2.3 KiB
TypeScript

"use client";
/**
* Who reacted to a message, and with what.
*
* Fetched on open rather than shipped with every message: the thread renders
* from grouped counts, and sending the full membership of every reaction on
* every page would grow with the room.
*/
import { useEffect, useState } from "react";
import { UserAvatar } from "@/components/ui/user-avatar";
import { ResponsiveDialog } from "@/components/chat/responsive-dialog";
import { chat, type ReactionDetail } from "@/lib/chat";
export function ReactionsDrawer({
conversationId,
messageId,
onClose,
}: {
conversationId: string;
messageId: string;
onClose: () => void;
}) {
const [rows, setRows] = useState<ReactionDetail[] | null>(null);
useEffect(() => {
let cancelled = false;
chat
.reactionDetails(conversationId, messageId)
.then((r) => {
if (!cancelled) setRows(r);
})
.catch(() => {
if (!cancelled) setRows([]);
});
return () => {
cancelled = true;
};
}, [conversationId, messageId]);
return (
<ResponsiveDialog
title="Reactions"
description={
rows === null
? "Loading…"
: `${rows.length} ${rows.length === 1 ? "person" : "people"} reacted.`
}
onClose={onClose}
>
{rows === null ? (
<p className="p-4 text-sm text-muted-foreground">Loading…</p>
) : rows.length === 0 ? (
<p className="p-4 text-sm text-muted-foreground">No reactions.</p>
) : (
<ul className="divide-y divide-border">
{rows.map((r) => (
<li
key={r.user.id}
className="flex items-center gap-3 px-4 py-2.5"
>
<UserAvatar
src={r.user.avatar_url}
name={r.user.name}
className="size-9 shrink-0"
/>
<div className="min-w-0 flex-1">
<p className="truncate text-sm text-foreground">{r.user.name}</p>
<p className="truncate text-xs text-muted-foreground">
@{r.user.handle}
</p>
</div>
<span className="shrink-0 text-lg" aria-label={r.emoji}>
{r.emoji}
</span>
</li>
))}
</ul>
)}
</ResponsiveDialog>
);
}