86 lines
3.1 KiB
TypeScript
86 lines
3.1 KiB
TypeScript
"use client";
|
|
|
|
import { useThread, useChannel, useSdk, notifyChannelChanged } from "@lynkd/messaging-inbox-sdk";
|
|
import { useUi } from "@/lib/ui-state";
|
|
import { MessageItem } from "./MessageItem";
|
|
import { Composer } from "./Composer";
|
|
import { X } from "lucide-react";
|
|
|
|
export function ThreadPanel() {
|
|
const { thread, closeThread, toast } = useUi();
|
|
const { client } = useSdk();
|
|
const channel = useChannel(thread?.channelId ?? null);
|
|
const { parent, replies, reply, react, pin, save, edit, remove } = useThread(
|
|
thread?.channelId ?? null,
|
|
thread?.parentId ?? null,
|
|
);
|
|
|
|
if (!thread) return null;
|
|
const channelLabel = channel ? (channel.kind === "channel" ? `#${channel.name}` : channel.name) : "channel";
|
|
|
|
return (
|
|
<aside
|
|
role="dialog"
|
|
aria-label="Thread"
|
|
className="fixed inset-0 z-40 flex w-full flex-col border-l border-border bg-surface md:static md:z-auto md:w-[400px] md:max-w-[400px] md:shrink-0 animate-fade-in"
|
|
>
|
|
<div className="flex h-[60px] items-center justify-between border-b border-border px-4">
|
|
<div>
|
|
<div className="text-[15px] font-bold">Thread</div>
|
|
<div className="text-[12px] text-muted">
|
|
{channel ? (channel.kind === "channel" ? `#${channel.name}` : channel.name) : ""}
|
|
</div>
|
|
</div>
|
|
<button onClick={closeThread} className="focus-ring grid h-8 w-8 place-items-center rounded-control text-muted hover:bg-hover hover:text-ink" aria-label="Close thread">
|
|
<X size={18} />
|
|
</button>
|
|
</div>
|
|
|
|
<div className="min-h-0 flex-1 overflow-y-auto pb-2">
|
|
{parent && (
|
|
<MessageItem
|
|
message={parent}
|
|
onReact={(e) => react(parent.id, e)}
|
|
onPin={() => pin(parent.id)}
|
|
onSave={() => save(parent.id)}
|
|
onEdit={(b) => edit(parent.id, b)}
|
|
onDelete={() => { remove(parent.id); closeThread(); }}
|
|
/>
|
|
)}
|
|
<div className="my-1 flex items-center gap-3 px-4">
|
|
<div className="h-px flex-1 bg-border" />
|
|
<span className="text-[11px] font-semibold text-muted">
|
|
{replies.length} {replies.length === 1 ? "reply" : "replies"}
|
|
</span>
|
|
<div className="h-px flex-1 bg-border" />
|
|
</div>
|
|
{replies.map((m) => (
|
|
<MessageItem
|
|
key={m.id}
|
|
message={m}
|
|
onReact={(e) => react(m.id, e)}
|
|
onPin={() => pin(m.id)}
|
|
onSave={() => save(m.id)}
|
|
onEdit={(b) => edit(m.id, b)}
|
|
onDelete={() => remove(m.id)}
|
|
/>
|
|
))}
|
|
</div>
|
|
|
|
<Composer
|
|
showAlsoSend
|
|
channelName={channelLabel}
|
|
placeholder="Reply…"
|
|
onSend={async (body, opts) => {
|
|
await reply(body, { attachments: opts?.attachments });
|
|
if (opts?.alsoSendToChannel && thread) {
|
|
await client.sendMessage(thread.channelId, body, { attachments: opts?.attachments, threadRootId: thread.parentId });
|
|
notifyChannelChanged(thread.channelId);
|
|
toast(`Also sent to ${channelLabel}`, "success");
|
|
}
|
|
}}
|
|
/>
|
|
</aside>
|
|
);
|
|
}
|