blob: ecf7777041fb6482d38f54164da88f74997ba6e5 [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() {
9 const store = useStateStore();
10 const nodes = useNodes<AppNode>();
gio6cf8c272025-05-08 09:01:38 +000011 const [nodeMap, setNodeMap] = useState<Map<string, AppNode>>(new Map());
gio5f2f1002025-03-20 18:38:48 +040012 useEffect(() => {
13 setNodeMap(new Map(nodes.map((n) => [n.id, n])));
14 }, [nodes, setNodeMap]);
15 const onClick = useCallback((fn?: (state: AppState) => void) => {
16 return () => {
17 if (fn) {
18 fn(store);
19 }
20 };
21 }, [store]);
22 const messages = useMessages();
23 const [grouped, setGrouped] = useState<Map<string, Message[]>>(new Map());
24 useEffect(() => {
25 const g = new Map<string, Message[]>();
26 messages.forEach((m) => {
27 const id = m.nodeId || "global";
28 const existing: Message[] = g.get(id) || [];
29 existing.push(m);
30 g.set(id, existing);
31 });
32 setGrouped(g);
33 }, [messages, setGrouped]);
34 const [open, setOpen] = useState<string[]>([...grouped.keys()]);
35 useEffect(() => {
36 // TODO(gio): do not reopen closed ones
37 setOpen([...grouped.keys()]);
38 }, [grouped, setOpen]);
39 return (
gio6cf8c272025-05-08 09:01:38 +000040 <Accordion type="multiple" value={open} onValueChange={(v) => setOpen(v)}>
41 {[...grouped.entries()].map(([id, messages]) => (
42 <AccordionItem key={id} value={id}>
43 <AccordionTrigger className="flex flex-row-reverse !space-x-4 !justify-end">
44 <Badge>{messages.length}</Badge>
45 <div>{id === "global" ? "Global" : nodeLabel(nodeMap.get(id)!)}</div>
46 </AccordionTrigger>
47 <AccordionContent>
48 <div className="flex flex-col space-y-1">
49 {messages.map((m) => (
50 <Button key={m.id} variant="ghost" style={{ justifyContent: "flex-start" }} onMouseOver={onClick(m.onHighlight)} onMouseLeave={onClick(m.onLooseHighlight)} onClick={onClick(m.onClick)}>{m.message}</Button>
51 ))}
52 </div>
53 </AccordionContent>
54 </AccordionItem>
55 ))}
56 </Accordion>
gio5f2f1002025-03-20 18:38:48 +040057 )
58}