diff --git a/src/pages/summary.tsx b/src/pages/summary.tsx index ebe0275..6e06a38 100644 --- a/src/pages/summary.tsx +++ b/src/pages/summary.tsx @@ -23,6 +23,12 @@ export function StreamSummaryPage() { if (link) { return ; } + const location = useLocation(); + const evPreload = getEventFromLocationState(location.state); + const link = useStreamLink(); + if (link) { + return ; + } } interface StatSlot { @@ -30,6 +36,10 @@ interface StatSlot { zaps: number; messages: number; reactions: number; + time: number; + zaps: number; + messages: number; + reactions: number; } export function StreamSummary({ link, preload }: { link: NostrLink; preload?: NostrEvent }) { @@ -37,7 +47,25 @@ export function StreamSummary({ link, preload }: { link: NostrLink; preload?: No const thisLink = ev ? NostrLink.fromEvent(ev) : undefined; const data = useLiveChatFeed(thisLink, undefined, 5_000); const reactions = useEventReactions(thisLink ?? link, data.reactions); + const ev = useCurrentStreamFeed(link, true, preload); + const thisLink = ev ? NostrLink.fromEvent(ev) : undefined; + const data = useLiveChatFeed(thisLink, undefined, 5_000); + const reactions = useEventReactions(thisLink ?? link, data.reactions); + const chatSummary = useMemo(() => { + return Object.entries( + data.messages.reduce((acc, v) => { + acc[v.pubkey] ??= []; + acc[v.pubkey].push(v); + return acc; + }, {} as Record>) + ) + .map(([k, v]) => ({ + pubkey: k, + messages: v, + })) + .sort((a, b) => (a.messages.length > b.messages.length ? -1 : 1)); + }, [data.messages]); const chatSummary = useMemo(() => { return Object.entries( data.messages.reduce((acc, v) => { @@ -53,6 +81,22 @@ export function StreamSummary({ link, preload }: { link: NostrLink; preload?: No .sort((a, b) => (a.messages.length > b.messages.length ? -1 : 1)); }, [data.messages]); + const zapsSummary = useMemo(() => { + return Object.entries( + reactions.zaps.reduce((acc, v) => { + if (!v.sender) return acc; + acc[v.sender] ??= []; + acc[v.sender].push(v); + return acc; + }, {} as Record>) + ) + .map(([k, v]) => ({ + pubkey: k, + zaps: v, + total: v.reduce((acc, vv) => acc + vv.amount, 0), + })) + .sort((a, b) => (a.total > b.total ? -1 : 1)); + }, [reactions.zaps]); const zapsSummary = useMemo(() => { return Object.entries( reactions.zaps.reduce((acc, v) => { @@ -70,18 +114,47 @@ export function StreamSummary({ link, preload }: { link: NostrLink; preload?: No .sort((a, b) => (a.total > b.total ? -1 : 1)); }, [reactions.zaps]); + const title = findTag(ev, "title"); + const summary = findTag(ev, "summary"); + const status = findTag(ev, "status"); + const starts = findTag(ev, "starts"); const title = findTag(ev, "title"); const summary = findTag(ev, "summary"); const status = findTag(ev, "status"); const starts = findTag(ev, "starts"); + const Day = 60 * 60 * 24; + const startTime = starts ? Number(starts) : ev?.created_at ?? unixNow(); + const endTime = status === StreamState.Live ? unixNow() : ev?.created_at ?? unixNow(); const Day = 60 * 60 * 24; const startTime = starts ? Number(starts) : ev?.created_at ?? unixNow(); const endTime = status === StreamState.Live ? unixNow() : ev?.created_at ?? unixNow(); + const streamLength = endTime - startTime; + const windowSize = streamLength > Day ? Day : 60 * 10; const streamLength = endTime - startTime; const windowSize = streamLength > Day ? Day : 60 * 10; + const stats = useMemo(() => { + let min = unixNow(); + let max = 0; + const ret = [...data.messages, ...data.reactions] + .sort((a, b) => (a.created_at > b.created_at ? -1 : 1)) + .reduce((acc, v) => { + const time = Math.floor(v.created_at - (v.created_at % windowSize)); + if (time < min) { + min = time; + } + if (time > max) { + max = time; + } + const key = time.toString(); + acc[key] ??= { + time, + zaps: 0, + messages: 0, + reactions: 0, + }; const stats = useMemo(() => { let min = unixNow(); let max = 0; @@ -114,6 +187,17 @@ export function StreamSummary({ link, preload }: { link: NostrLink; preload?: No } return acc; }, {} as Record); + if (v.kind === LIVE_STREAM_CHAT) { + acc[key].messages++; + } else if (v.kind === EventKind.ZapReceipt) { + acc[key].zaps++; + } else if (v.kind === EventKind.Reaction) { + acc[key].reactions++; + } else { + console.debug("Uncounted stat", v); + } + return acc; + }, {} as Record); // fill empty time slots for (let x = min; x < max; x += windowSize) { @@ -126,6 +210,17 @@ export function StreamSummary({ link, preload }: { link: NostrLink; preload?: No } return ret; }, [data]); + // fill empty time slots + for (let x = min; x < max; x += windowSize) { + ret[x.toString()] ??= { + time: x, + zaps: 0, + messages: 0, + reactions: 0, + }; + } + return ret; + }, [data]); return (