import "./live-chat.css"; import { EventKind, NostrPrefix, NostrLink, ParsedZap, NostrEvent, parseZap, encodeTLV, } from "@snort/system"; import { useEffect, useMemo } from "react"; import { System } from "../index"; import { useLiveChatFeed } from "../hooks/live-chat"; import { Profile } from "./profile"; import { Icon } from "./icon"; import Spinner from "./spinner"; import { useLogin } from "../hooks/login"; import { formatSats } from "../number"; import useTopZappers from "../hooks/top-zappers"; import { LIVE_STREAM_CHAT } from "../const"; import { ChatMessage } from "./chat-message"; import { Goal } from "./goal"; import { NewGoalDialog } from "./new-goal"; import { WriteMessage } from "./write-message"; import { findTag, getHost } from "utils"; export interface LiveChatOptions { canWrite?: boolean; showHeader?: boolean; } function TopZappers({ zaps }: { zaps: ParsedZap[] }) { const zappers = useTopZappers(zaps); return ( <> {zappers.map(({ pubkey, total }, idx) => { return (
{pubkey === "anon" ? (

Anon

) : ( )}

{formatSats(total)}

); })} ); } export function LiveChat({ link, ev, goal, options, height, }: { link: NostrLink; ev?: NostrEvent; goal?: NostrEvent; options?: LiveChatOptions; height?: number; }) { const host = getHost(ev); const feed = useLiveChatFeed(link, goal ? [goal.id] : undefined); const login = useLogin(); useEffect(() => { const pubkeys = [ ...new Set(feed.zaps.flatMap((a) => [a.pubkey, findTag(a, "p")!])), ]; System.ProfileLoader.TrackMetadata(pubkeys); return () => System.ProfileLoader.UntrackMetadata(pubkeys); }, [feed.zaps]); const zaps = feed.zaps .map((ev) => parseZap(ev, System.ProfileLoader.Cache)) .filter((z) => z && z.valid); const goalZaps = feed.zaps .filter((ev) => (goal ? ev.created_at > goal.created_at && ev.tags.some(t => t[0] === "e" && t[1] === goal.id) : false)) .map((ev) => parseZap(ev, System.ProfileLoader.Cache)) .filter((z) => z && z.valid); const events = useMemo(() => { return [...feed.messages, ...feed.zaps].sort( (a, b) => b.created_at - a.created_at ); }, [feed.messages, feed.zaps]); const streamer = getHost(ev); const naddr = useMemo(() => { if (ev) { return encodeTLV( NostrPrefix.Address, findTag(ev, "d") ?? "", undefined, ev.kind, ev.pubkey ); } }, [ev]); return (
{(options?.showHeader ?? true) && (

Stream Chat

)} {zaps.length > 0 && (

Top zappers

{goal && } {login?.pubkey === streamer && }
)}
{events.map((a) => { switch (a.kind) { case LIVE_STREAM_CHAT: { return ( ); } case EventKind.ZapReceipt: { const zap = zaps.find( (b) => b.id === a.id && b.receiver === streamer ); if (zap) { return ; } } } return null; })} {feed.messages.length === 0 && }
{(options?.canWrite ?? true) && (
{login ? ( ) : (

Please login to write messages!

)}
)}
); } const BIG_ZAP_THRESHOLD = 100_000; function ChatZap({ zap }: { zap: ParsedZap }) { if (!zap.valid) { return null; } const isBig = zap.amount >= BIG_ZAP_THRESHOLD; return (
zapped {formatSats(zap.amount)} sats
{zap.content &&
{zap.content}
}
); }