import { commands } from "@/commands.gen"; import { decodeZapInvoice, formatCreatedAt } from "@/commons"; import { Note, RepostIcon, Spinner, User } from "@/components"; import { LumeEvent, LumeWindow, useEvent } from "@/system"; import { Kind, type NostrEvent } from "@/types"; import { Info } from "@phosphor-icons/react"; import * as ScrollArea from "@radix-ui/react-scroll-area"; import * as Tabs from "@radix-ui/react-tabs"; import { useQuery } from "@tanstack/react-query"; import { createLazyFileRoute } from "@tanstack/react-router"; import { getCurrentWindow } from "@tauri-apps/api/window"; import { nip19 } from "nostr-tools"; import { type ReactNode, useEffect, useMemo, useRef } from "react"; import { Virtualizer } from "virtua"; export const Route = createLazyFileRoute("/columns/_layout/notification/$id")({ component: Screen, }); function Screen() { const { id } = Route.useParams(); const { queryClient } = Route.useRouteContext(); const { isLoading, data } = useQuery({ queryKey: ["notification", id], queryFn: async () => { const res = await commands.getNotifications(id); if (res.status === "error") { throw new Error(res.error); } const data: NostrEvent[] = res.data.map((item) => JSON.parse(item)); const events = data.map((ev) => new LumeEvent(ev)); return events; }, select: (events) => { const zaps = new Map(); const reactions = new Map(); const hex = nip19.decode(id).data; const texts = events.filter( (ev) => ev.kind === Kind.Text && ev.pubkey !== hex, ); const zapEvents = events.filter((ev) => ev.kind === Kind.ZapReceipt); const reactEvents = events.filter( (ev) => ev.kind === Kind.Repost || ev.kind === Kind.Reaction, ); for (const event of reactEvents) { const rootId = event.tags.filter((tag) => tag[0] === "e")[0]?.[1]; if (rootId) { if (reactions.has(rootId)) { const ev = reactions.get(rootId); if (ev) { ev.push(event); } } else { reactions.set(rootId, [event]); } } } for (const event of zapEvents) { const rootId = event.tags.filter((tag) => tag[0] === "e")[0]?.[1]; if (rootId) { if (zaps.has(rootId)) { const ev = zaps.get(rootId); if (ev) { ev.push(event); } } else { zaps.set(rootId, [event]); } } } return { texts, zaps, reactions }; }, refetchOnWindowFocus: false, }); useEffect(() => { const unlisten = getCurrentWindow().listen("event", async (data) => { const event: LumeEvent = JSON.parse(data.payload as string); await queryClient.setQueryData( ["notification", id], (data: LumeEvent[]) => [event, ...data], ); }); return () => { unlisten.then((f) => f()); }; }, [id]); if (isLoading) { return (
); } return (
Replies Reactions Zaps {data.texts.map((event) => ( ))} {[...data.reactions.entries()].map(([root, events]) => (
{events.map((event) => (
{event.kind === Kind.Reaction ? ( event.content === "+" ? ( "👍" ) : ( event.content ) ) : ( )}
))}
))}
{[...data.zaps.entries()].map(([root, events]) => (
{events.map((event) => ( ))}
))}
); } function Tab({ value, children }: { value: string; children: ReactNode[] }) { const ref = useRef(null); return ( {children} ); } function RootNote({ id }: { id: string }) { const { isLoading, isError, data } = useEvent(id); if (isLoading) { return (
); } if (isError || !data) { return (

Event not found with your current relay set

); } return (
{data.content}
); } function TextNote({ event }: { event: LumeEvent }) { const pTags = event.tags .filter((tag) => tag[0] === "p") .map((tag) => tag[1]) .slice(0, 3); return ( ); } function ZapReceipt({ event }: { event: LumeEvent }) { const amount = useMemo( () => decodeZapInvoice(event.tags).bitcoinFormatted ?? "0", [event.id], ); const sender = useMemo( () => event.tags.find((tag) => tag[0] === "P")?.[1], [event.id], ); if (!sender) { return (
₿ {amount}
); } return (
₿ {amount}
); }