import { NostrEvent, NostrLink } from "@snort/system"; import { FormattedMessage } from "react-intl"; import { Link } from "react-router-dom"; import { getName } from "../profile"; import { NIP5_DOMAIN, StreamState } from "@/const"; import useImgProxy from "@/hooks/img-proxy"; import { formatSats } from "@/number"; import { extractStreamInfo, getHost, profileLink } from "@/utils"; import { useUserProfile } from "@snort/system-react"; import classNames from "classnames"; import { useEffect, useState } from "react"; import { Avatar } from "../avatar"; import Logo from "../logo"; import { useContentWarning } from "../nsfw"; import PillOpaque from "../pill-opaque"; import { RelativeTime } from "../relative-time"; import { StatePill } from "../state-pill"; import { NostrJson } from "@snort/shared"; const nameCache = new Map(); async function fetchNostrAddresByPubkey(pubkey: string, domain: string, timeout = 2_000): Promise { if (!pubkey || !domain) { return undefined; } const cacheKey = `${pubkey}@${domain}`; if (nameCache.has(cacheKey)) { return nameCache.get(cacheKey); } try { const res = await fetch(`https://${domain}/.well-known/nostr.json?pubkey=${pubkey}`, { signal: AbortSignal.timeout(timeout), }); const ret = (await res.json()) as NostrJson; nameCache.set(cacheKey, ret); return ret; } catch { // ignored } return undefined; } export function StreamTile({ ev, showAuthor = true, showStatus = true, showAvatar = true, style, className, }: { ev: NostrEvent; showAuthor?: boolean; showStatus?: boolean; showAvatar?: boolean; style: "list" | "grid"; className?: string; }) { const { title, image, status, participants, contentWarning, recording, ends } = extractStreamInfo(ev); const host = getHost(ev); const link = NostrLink.fromEvent(ev); const hostProfile = useUserProfile(host); const isGrownUp = useContentWarning(); const { proxy } = useImgProxy(); const [videoLink, setVideoLink] = useState(`/${link.encode()}`) useEffect(() => { if (status === StreamState.Live) { fetchNostrAddresByPubkey(host, NIP5_DOMAIN).then((h) => { if (h) { const names = Object.entries(h.names); if (names.length > 0) { setVideoLink(`/${names[0][0]}`); } } }); } }, [status, videoLink]); const [hasImg, setHasImage] = useState((image?.length ?? 0) > 0 || (recording?.length ?? 0) > 0); return (
{hasImg ? ( { setHasImage(false); }} /> ) : ( )} {showStatus && } {participants && ( )}
{showAuthor && showAvatar && ( )}
{(title?.length ?? 0) > 50 ? `${title?.slice(0, 47)}...` : title} {showAuthor && ( {getName(host, hostProfile)} {ends && ( <> {" ยท "} )} )}
); }