feat(messages): show channel and thread location context in message cards

This commit is contained in:
MythEclipse
2026-06-03 20:34:06 +07:00
parent 79c2e31b17
commit 71801b7f77
3 changed files with 40 additions and 20 deletions
@@ -12,12 +12,19 @@ export interface MessageMetadata {
stickers?: Array<{ name?: string; url?: string }>; stickers?: Array<{ name?: string; url?: string }>;
attachments?: Array<{ name: string; url: string; contentType?: string }>; attachments?: Array<{ name: string; url: string; contentType?: string }>;
embeds?: Array<{ title?: string; image?: string; thumbnail?: string }>; embeds?: Array<{ title?: string; image?: string; thumbnail?: string }>;
channel?: {
channelId: string;
channelName?: string;
threadId?: string;
threadName?: string;
};
} }
export function parseMetadata(value: string | null): MessageMetadata { export function parseMetadata(value: string | null): MessageMetadata {
if (!value) return {}; if (!value) return {};
try { try {
return JSON.parse(value) as MessageMetadata; const parsed = JSON.parse(value) as MessageMetadata;
return parsed;
} catch { } catch {
return {}; return {};
} }
@@ -1,6 +1,7 @@
import { import {
AlertCircle, AlertCircle,
CheckCircle2, CheckCircle2,
Hash,
Image as ImageIcon, Image as ImageIcon,
Pencil, Pencil,
RotateCw, RotateCw,
@@ -350,6 +351,20 @@ function MessageRow({
export function MessageCard({ messages, onReanalyze }: MessageCardProps) { export function MessageCard({ messages, onReanalyze }: MessageCardProps) {
const firstMsg = messages[0]; const firstMsg = messages[0];
const hasMultiple = messages.length > 1; const hasMultiple = messages.length > 1;
const meta = useMemo(
() => parseMetadata(firstMsg.metadata),
[firstMsg.metadata],
);
const channelMeta = meta.channel;
const locationLabel = useMemo(() => {
if (channelMeta?.threadName) {
return `# ${channelMeta.channelName || "unknown"} ${channelMeta.threadName}`;
}
if (channelMeta?.channelName) {
return `# ${channelMeta.channelName}`;
}
return null;
}, [channelMeta]);
return ( return (
<article <article
@@ -369,11 +384,17 @@ export function MessageCard({ messages, onReanalyze }: MessageCardProps) {
/> />
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
{/* Group header: username + timestamp of first message */} {/* Group header: username + location + timestamp */}
<div className="flex items-baseline gap-2 mb-2"> <div className="flex items-baseline gap-2 mb-2">
<span className="font-semibold text-sm text-foreground"> <span className="font-semibold text-sm text-foreground">
{firstMsg.username || firstMsg.user_id} {firstMsg.username || firstMsg.user_id}
</span> </span>
{locationLabel && (
<span className="flex items-center gap-1 text-[11px] text-muted-foreground/50 bg-muted/50 px-1.5 py-0.5 rounded-full">
<Hash className="h-2.5 w-2.5" />
{locationLabel}
</span>
)}
<span <span
className="text-[11px] text-muted-foreground/60" className="text-[11px] text-muted-foreground/60"
title={new Date(firstMsg.created_at).toLocaleString()} title={new Date(firstMsg.created_at).toLocaleString()}
@@ -12,10 +12,6 @@ import {
CardTitle, CardTitle,
Input, Input,
Select, Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
Tabs, Tabs,
TabsContent, TabsContent,
TabsList, TabsList,
@@ -134,20 +130,16 @@ export function MessagesPanel({
<Hash className="h-4 w-4 text-muted-foreground shrink-0" /> <Hash className="h-4 w-4 text-muted-foreground shrink-0" />
<Select <Select
value={selectedChannel || ""} value={selectedChannel || ""}
onValueChange={(val) => onChannelChange?.(val)} onChange={(e) => onChannelChange?.(e.target.value === "__all" ? "" : e.target.value)}
> placeholder="All channels"
<SelectTrigger className="w-full max-w-xs h-9 text-sm"> options={[
<SelectValue placeholder="All channels" /> { value: "__all", label: "All channels" },
</SelectTrigger> ...textChannels.map((ch) => ({
<SelectContent> value: ch.id,
<SelectItem value="__all">All channels</SelectItem> label: `# ${ch.name}`,
{textChannels.map((ch) => ( })),
<SelectItem key={ch.id} value={ch.id}> ]}
# {ch.name} />
</SelectItem>
))}
</SelectContent>
</Select>
</div> </div>
)} )}
</CardContent> </CardContent>