import "./stream-page.css"; import { NostrLink, NostrPrefix, TaggedNostrEvent, tryParseNostrLink, } from "@snort/system"; import { fetchNip05Pubkey } from "@snort/shared"; import { useLocation, useNavigate, useParams } from "react-router-dom"; import { Helmet } from "react-helmet"; import { LiveVideoPlayer } from "element/live-video-player"; import { createNostrLink, findTag, getEventFromLocationState, getHost, hexToBech32, } from "utils"; import { Profile, getName } from "element/profile"; import { LiveChat } from "element/live-chat"; import AsyncButton from "element/async-button"; import { useLogin } from "hooks/login"; import { useZapGoal } from "hooks/goals"; import { StreamState, System } from "index"; import { SendZapsDialog } from "element/send-zap"; import { NostrEvent } from "@snort/system"; import { useUserProfile } from "@snort/system-react"; import { NewStreamDialog } from "element/new-stream"; import { Tags } from "element/tags"; import { StatePill } from "element/state-pill"; import { StreamCards } from "element/stream-cards"; import { formatSats } from "number"; import { StreamTimer } from "element/stream-time"; import { ShareMenu } from "element/share-menu"; import { ContentWarningOverlay, isContentWarningAccepted, } from "element/content-warning"; import { useCurrentStreamFeed } from "hooks/current-stream-feed"; import { useEffect, useState } from "react"; function ProfileInfo({ ev, goal, }: { ev?: NostrEvent; goal?: TaggedNostrEvent; }) { const login = useLogin(); const navigate = useNavigate(); const host = getHost(ev); const profile = useUserProfile(host); const zapTarget = profile?.lud16 ?? profile?.lud06; const status = findTag(ev, "status") ?? ""; const isMine = ev?.pubkey === login?.pubkey; async function deleteStream() { const pub = login?.publisher(); if (pub && ev) { const evDelete = await pub.delete(ev.id); console.debug(evDelete); System.BroadcastEvent(evDelete); navigate("/"); } } const viewers = Number(findTag(ev, "current_participants") ?? "0"); return ( <>

{findTag(ev, "title")}

{findTag(ev, "summary")}

{viewers > 0 && ( {formatSats(viewers)} viewers )} {status === StreamState.Live && ( )} {ev && }
{isMine && (
{ev && } Delete
)}
{ev && ( <> {zapTarget && ( )} )}
); } export function StreamPageHandler() { const params = useParams(); const location = useLocation(); const evPreload = getEventFromLocationState(location.state); const [link, setLink] = useState(); useEffect(() => { if (params.id) { const parsedLink = tryParseNostrLink(params.id); if (parsedLink) { setLink(parsedLink); } else { const [handle, domain] = ( params.id.includes("@") ? params.id : `${params.id}@zap.stream` ).split("@"); fetchNip05Pubkey(handle, domain).then((d) => { if (d) { setLink({ id: d, type: NostrPrefix.PublicKey, encode: () => hexToBech32(NostrPrefix.PublicKey, d), } as NostrLink); } }); } } }, [params.id]); if (link) { return ; } } export function StreamPage({ link, evPreload, }: { evPreload?: NostrEvent; link: NostrLink; }) { const ev = useCurrentStreamFeed(link, true, evPreload); const host = getHost(ev); const goal = useZapGoal(host, createNostrLink(ev), true); const title = findTag(ev, "title"); const summary = findTag(ev, "summary"); const image = findTag(ev, "image"); const status = findTag(ev, "status"); const stream = status === StreamState.Live ? findTag(ev, "streaming") : findTag(ev, "recording"); const contentWarning = findTag(ev, "content-warning"); const tags = ev?.tags.filter((a) => a[0] === "t").map((a) => a[1]) ?? []; if (contentWarning && !isContentWarningAccepted()) { return ; } const descriptionContent = [ title, (summary?.length ?? 0) > 0 ? summary : "Nostr live streaming", ...tags, ].join(", "); return (
{`${title} - zap.stream`}
); }