90 lines
3.4 KiB
TypeScript
90 lines
3.4 KiB
TypeScript
import { useEffect } from "react";
|
|
import { useMessageStore } from "../stores/message.ts";
|
|
|
|
interface PinnedMessagesProps {
|
|
channelId: string;
|
|
channelName: string;
|
|
onClose: () => void;
|
|
onJump?: (messageId: string) => void;
|
|
}
|
|
|
|
export function PinnedMessages({ channelId, channelName, onClose, onJump }: PinnedMessagesProps) {
|
|
const pinnedMessages = useMessageStore((s) => s.pinnedMessagesByChannel[channelId] || []);
|
|
const fetchPinnedMessages = useMessageStore((s) => s.fetchPinnedMessages);
|
|
const unpinMessage = useMessageStore((s) => s.unpinMessage);
|
|
|
|
useEffect(() => {
|
|
fetchPinnedMessages(channelId);
|
|
}, [channelId, fetchPinnedMessages]);
|
|
|
|
useEffect(() => {
|
|
const handler = (e: KeyboardEvent) => {
|
|
if (e.key === "Escape") {
|
|
onClose();
|
|
}
|
|
};
|
|
window.addEventListener("keydown", handler);
|
|
return () => window.removeEventListener("keydown", handler);
|
|
}, [onClose]);
|
|
|
|
return (
|
|
<div className="absolute inset-x-0 top-0 z-20 bg-gb-bg border-b border-gb-bg-t shadow-lg">
|
|
<div className="px-3 py-2 border-b border-gb-bg-t flex items-center justify-between">
|
|
<span className="text-xs text-gb-orange font-mono">PINNED IN #{channelName.toUpperCase()} ({pinnedMessages.length}/5)</span>
|
|
<button onClick={onClose} className="text-xs text-gb-red hover:text-gb-orange">[x]</button>
|
|
</div>
|
|
<div className="max-h-64 overflow-y-auto font-mono text-xs">
|
|
{pinnedMessages.length === 0 ? (
|
|
<div className="px-3 py-4 text-center text-gb-fg-f">[no pinned messages in this channel]</div>
|
|
) : (
|
|
pinnedMessages.map((msg) => (
|
|
<div
|
|
key={msg.id}
|
|
className="w-full text-left px-3 py-2 border-b border-gb-bg-t last:border-b-0 hover:bg-gb-bg-s/20 flex flex-col gap-1"
|
|
>
|
|
<div className="flex items-center justify-between text-gb-fg-f">
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-gb-aqua font-bold"><{msg.author_username}></span>
|
|
<span className="text-[10px] text-gb-fg-f">
|
|
{new Date(msg.created_at).toLocaleString()}
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
{onJump && (
|
|
<button
|
|
onClick={() => {
|
|
onJump(msg.id);
|
|
onClose();
|
|
}}
|
|
className="text-gb-aqua hover:text-gb-orange"
|
|
title="Jump to message"
|
|
>
|
|
[JUMP]
|
|
</button>
|
|
)}
|
|
<button
|
|
onClick={async () => {
|
|
if (confirm("Unpin this message?")) {
|
|
try {
|
|
await unpinMessage(channelId, msg.id);
|
|
} catch (err) {
|
|
alert(err instanceof Error ? err.message : "Failed to unpin");
|
|
}
|
|
}
|
|
}}
|
|
className="text-gb-red hover:text-gb-orange"
|
|
title="Unpin message"
|
|
>
|
|
[UNPIN]
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="text-gb-fg break-words whitespace-pre-wrap">{msg.content}</div>
|
|
</div>
|
|
))
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|