import { cn } from "@/lib/utils"; import type { LivestreamStore } from "@streamplace/core"; import { Globe, Loader2 } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { place } from "streamplace"; import { usePDSAgent } from "../../lib/store/hooks"; interface MultistreamTarget { uri: string; record: { [x: string]: unknown; name?: string; url?: string; active: boolean; createdAt: string; }; latestEvent?: { status: string; }; } function getTargetName(target: MultistreamTarget): string { if (target.record.name) return target.record.name; if (target.record.url) { try { return new URL(target.record.url).host; } catch { return "Untitled Target"; } } return "Untitled Target"; } function getTargetHostname(target: MultistreamTarget): string | null { if (!target.record.url) return null; try { return new URL(target.record.url).host.split(":")[0]; } catch { return null; } } function statusColor(target: MultistreamTarget): string { if (!target.record.active) return "text-(--color-fg-muted)"; switch (target.latestEvent?.status) { case "active": return "text-green-400"; case "error": return "text-red-400"; case "pending": return "text-amber-400 animate-pulse"; default: return "text-(--color-fg-muted)"; } } /** * Lists multistream targets with active/inactive toggles and connection * status. */ export function MultistreamStatusWidget({ store }: { store: LivestreamStore }) { const { t } = useTranslation("common"); const agent = usePDSAgent(); const [targets, setTargets] = useState([]); const [loading, setLoading] = useState(true); const [toggling, setToggling] = useState>(new Set()); const loadTargets = useCallback(async () => { if (!agent) return; try { setLoading(true); const response = await agent.client.call( place.stream.multistream.listTargets, { limit: 50, }, ); setTargets(response.targets as unknown as MultistreamTarget[]); } catch (error) { console.error("Failed to load multistream targets:", error); setTargets([]); } finally { setLoading(false); } }, [agent]); const toggleTarget = useCallback( async (target: MultistreamTarget, newActive: boolean) => { if (!agent) return; try { setToggling((prev) => new Set(prev).add(target.uri)); await agent.client.call(place.stream.multistream.putTarget, { multistreamTarget: { ...target.record, $type: "place.stream.multistream.target" as const, active: newActive, } as any, rkey: target.uri.split("/").pop() || "", }); await loadTargets(); } catch (error) { console.error("Failed to toggle multistream target:", error); } finally { setToggling((prev) => { const next = new Set(prev); next.delete(target.uri); return next; }); } }, [agent, loadTargets], ); useEffect(() => { loadTargets(); }, [loadTargets]); if (loading && targets.length === 0) { return (
{t("loading-multistream", { defaultValue: "Loading multistream…", })}
); } if (targets.length === 0) { return (
{t("no-multistream-targets", { defaultValue: "No multistream targets configured", })}
); } return (

{t("multistream", { defaultValue: "Multistream" })}

{loading && ( )}
{targets.map((target) => { const name = getTargetName(target); const hostname = getTargetHostname(target); const isToggling = toggling.has(target.uri); return (
{name} {target.record.name && hostname && ( {hostname} )}
{target.latestEvent && (
{target.latestEvent.status}
)}
); })}
); }