import { Day } from "@/Const"; import Icon from "@/Icons/Icon"; import { formatShort } from "@/Number"; import { orderAscending } from "@/SnortUtils"; import { unixNow, unwrap } from "@snort/shared"; import { TaggedNostrEvent, EventKind } from "@snort/system"; import classNames from "classnames"; import { useState, useMemo } from "react"; import { FormattedMessage } from "react-intl"; import { AsyncIcon } from "./AsyncIcon"; import Tabs, { Tab } from "./Tabs"; import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"; interface StatSlot { time: string; reactions: number; reposts: number; quotes: number; mentions: number; zaps: number; } const enum NotificationSummaryPeriod { Daily, Weekly, } const enum NotificationSummaryFilter { Reactions = 1, Reposts = 2, Mentions = 4, Zaps = 8, All = 255, } export default function NotificationSummary({ evs }: { evs: Array }) { const [period, setPeriod] = useState(NotificationSummaryPeriod.Daily); const [filter, setFilter] = useState(NotificationSummaryFilter.All); const periodTabs = [ { value: NotificationSummaryPeriod.Daily, text: , }, { value: NotificationSummaryPeriod.Weekly, text: , }, ] as Array; const hasFlag = (v: number, f: NotificationSummaryFilter) => { return (v & f) > 0; }; const getWeek = (d: Date) => { const onejan = new Date(d.getFullYear(), 0, 1); const today = new Date(d.getFullYear(), d.getMonth(), d.getDate()); const dayOfYear = (today.getTime() - onejan.getTime() + 86400000) / 86400000; return Math.ceil(dayOfYear / 7); }; const stats = useMemo(() => { return orderAscending(evs) .filter(a => (period === NotificationSummaryPeriod.Daily ? a.created_at > unixNow() - 14 * Day : true)) .reduce( (acc, v) => { const date = new Date(v.created_at * 1000); const key = period === NotificationSummaryPeriod.Daily ? `${date.getMonth() + 1}/${date.getDate()}` : getWeek(date).toString(); acc[key] ??= { time: key, reactions: 0, reposts: 0, quotes: 0, mentions: 0, zaps: 0, }; if (v.kind === EventKind.Reaction) { acc[key].reactions++; } else if (v.kind === EventKind.Repost) { acc[key].reposts++; } else if (v.kind === EventKind.ZapReceipt) { acc[key].zaps++; } if (v.kind === EventKind.TextNote) { acc[key].mentions++; } return acc; }, {} as Record, ); }, [evs, period]); if (evs.length === 0) return; const filterIcon = (f: NotificationSummaryFilter, icon: string, iconActiveClass: string) => { const active = hasFlag(filter, f); return ( setFilter(v => v ^ f)} name={""} iconName={icon} /> ); }; return (

{filterIcon(NotificationSummaryFilter.Reactions, "heart-solid", "text-heart")} {filterIcon(NotificationSummaryFilter.Zaps, "zap-solid", "text-zap")} {filterIcon(NotificationSummaryFilter.Reposts, "reverse-left", "text-repost")} {filterIcon(NotificationSummaryFilter.Mentions, "at-sign", "text-mention")}
a.value === period))} setTab={t => setPeriod(t.value)} />
{hasFlag(filter, NotificationSummaryFilter.Reactions) && ( )} {hasFlag(filter, NotificationSummaryFilter.Reposts) && ( )} {hasFlag(filter, NotificationSummaryFilter.Mentions) && ( )} {hasFlag(filter, NotificationSummaryFilter.Zaps) && } { if (active && payload && payload.length) { return (
{formatShort(payload.find(a => a.name === "reactions")?.value as number)}
{formatShort(payload.find(a => a.name === "zaps")?.value as number)}
{formatShort(payload.find(a => a.name === "reposts")?.value as number)}
{formatShort(payload.find(a => a.name === "mentions")?.value as number)}
); } return null; }} />
); }