blob: e756047c6322b10811f85441f76ff74be50137b3 [file] [log] [blame]
gio5f2f1002025-03-20 18:38:48 +04001import { Button } from "./components/ui/button";
2import { AppNode, AppState, Message, nodeLabel, useMessages, useStateStore } from "./lib/state";
3import { useCallback, useEffect, useState } from "react";
4import { useNodes } from "@xyflow/react";
5import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "./components/ui/accordion";
6import { Badge } from "./components/ui/badge";
7
8export function Messages() {
giod0026612025-05-08 13:00:36 +00009 const store = useStateStore();
10 const nodes = useNodes<AppNode>();
11 const [nodeMap, setNodeMap] = useState<Map<string, AppNode>>(new Map());
12 useEffect(() => {
13 setNodeMap(new Map(nodes.map((n) => [n.id, n])));
14 }, [nodes, setNodeMap]);
15 const onClick = useCallback(
16 (fn?: (state: AppState) => void) => {
17 return () => {
18 if (fn) {
19 fn(store);
20 }
21 };
22 },
23 [store],
24 );
25 const messages = useMessages();
26 const [grouped, setGrouped] = useState<Map<string, Message[]>>(new Map());
27 useEffect(() => {
28 const g = new Map<string, Message[]>();
29 messages.forEach((m) => {
30 const id = m.nodeId || "global";
31 const existing: Message[] = g.get(id) || [];
32 existing.push(m);
33 g.set(id, existing);
34 });
35 setGrouped(g);
36 }, [messages, setGrouped]);
37 const [open, setOpen] = useState<string[]>([...grouped.keys()]);
38 useEffect(() => {
39 // TODO(gio): do not reopen closed ones
40 setOpen([...grouped.keys()]);
41 }, [grouped, setOpen]);
42 return (
43 <Accordion type="multiple" value={open} onValueChange={(v) => setOpen(v)}>
44 {[...grouped.entries()].map(([id, messages]) => (
45 <AccordionItem key={id} value={id}>
46 <AccordionTrigger className="flex flex-row-reverse !space-x-4 !justify-end">
47 <Badge>{messages.length}</Badge>
48 <div>{id === "global" ? "Global" : nodeLabel(nodeMap.get(id)!)}</div>
49 </AccordionTrigger>
50 <AccordionContent>
51 <div className="flex flex-col space-y-1">
52 {messages.map((m) => (
53 <Button
54 key={m.id}
55 variant="ghost"
56 style={{ justifyContent: "flex-start" }}
57 onMouseOver={onClick(m.onHighlight)}
58 onMouseLeave={onClick(m.onLooseHighlight)}
59 onClick={onClick(m.onClick)}
60 >
61 {m.message}
62 </Button>
63 ))}
64 </div>
65 </AccordionContent>
66 </AccordionItem>
67 ))}
68 </Accordion>
69 );
70}