blob: c4683f2e3b512b0972357fd9ba7cd3e176a844ca [file] [log] [blame]
import { Button } from "./components/ui/button";
import { AppNode, AppState, Message, nodeLabel, useMessages, useStateStore } from "./lib/state";
import { useCallback, useEffect, useState } from "react";
import { useNodes } from "@xyflow/react";
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "./components/ui/accordion";
import { Badge } from "./components/ui/badge";
export function Messages() {
const store = useStateStore();
const nodes = useNodes<AppNode>();
const [nodeMap, setNodeMap] = useState<Map<string, AppNode>>(new Map());
useEffect(() => {
setNodeMap(new Map(nodes.map((n) => [n.id, n])));
}, [nodes, setNodeMap]);
const onClick = useCallback(
(fn?: (state: AppState) => void) => {
return () => {
if (fn) {
fn(store);
}
};
},
[store],
);
const messages = useMessages();
const [grouped, setGrouped] = useState<Map<string, Message[]>>(new Map());
useEffect(() => {
const g = new Map<string, Message[]>();
messages.forEach((m) => {
const id = m.nodeId || "global";
const existing: Message[] = g.get(id) || [];
existing.push(m);
g.set(id, existing);
});
setGrouped(g);
}, [messages, setGrouped]);
const [open, setOpen] = useState<string[]>([...grouped.keys()]);
useEffect(() => {
// TODO(gio): do not reopen closed ones
setOpen([...grouped.keys()]);
}, [grouped, setOpen]);
return (
<Accordion type="multiple" value={open} onValueChange={(v) => setOpen(v)}>
{[...grouped.entries()].map(([id, messages]) => (
<AccordionItem key={id} value={id}>
<AccordionTrigger className="flex flex-row-reverse !gap-1 !justify-end !h-fit !py-0">
<Badge>{messages.length}</Badge>
<div>{id === "global" ? "Global" : nodeLabel(nodeMap.get(id)!)}</div>
</AccordionTrigger>
<AccordionContent>
<div className="flex flex-col !px-4">
{messages.map((m) => (
<Button
key={m.id}
variant="ghost"
className="justify-start !h-fit !py-0"
onMouseOver={onClick(m.onHighlight)}
onMouseLeave={onClick(m.onLooseHighlight)}
onClick={onClick(m.onClick)}
>
{m.message}
</Button>
))}
</div>
</AccordionContent>
</AccordionItem>
))}
</Accordion>
);
}