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) && (
)}
{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}
}
);
}