import "./new-stream.css"; import * as Dialog from "@radix-ui/react-dialog"; import { useEffect, useState, useCallback } from "react"; import { EventPublisher, NostrEvent } from "@snort/system"; import { unixNow } from "@snort/shared"; import AsyncButton from "./async-button"; import { StreamState, System } from "index"; import { Icon } from "element/icon"; import { findTag } from "utils"; export function NewStream({ ev, onFinish, }: { ev?: NostrEvent; onFinish?: (ev: NostrEvent) => void; }) { const [title, setTitle] = useState(findTag(ev, "title") ?? ""); const [summary, setSummary] = useState(findTag(ev, "summary") ?? ""); const [image, setImage] = useState(findTag(ev, "image") ?? ""); const [stream, setStream] = useState(findTag(ev, "streaming") ?? ""); const [status, setStatus] = useState( findTag(ev, "status") ?? StreamState.Live ); const [start, setStart] = useState(findTag(ev, "starts")); const [isValid, setIsValid] = useState(false); const validate = useCallback(() => { if (title.length < 2) { return false; } if (stream.length < 5 || !stream.match(/^https?:\/\/.*\.m3u8?$/i)) { return false; } if (image.length > 0 && !image.match(/^https?:\/\//i)) { return false; } return true; }, [title, image, stream]); useEffect(() => { setIsValid(validate()); }, [validate, title, summary, image, stream]); async function publishStream() { const pub = await EventPublisher.nip7(); if (pub) { const evNew = await pub.generic((eb) => { const now = unixNow(); const dTag = findTag(ev, "d") ?? now.toString(); const starts = start ?? now.toString(); const ends = findTag(ev, "ends") ?? now.toString(); eb.kind(30_311) .tag(["d", dTag]) .tag(["title", title]) .tag(["summary", summary]) .tag(["image", image]) .tag(["streaming", stream]) .tag(["status", status]) .tag(["starts", starts]); if (status === StreamState.Ended) { eb.tag(["ends", ends]); } return eb; }); console.debug(evNew); System.BroadcastEvent(evNew); onFinish && onFinish(evNew); } } function toDateTimeString(n: number) { return new Date(n * 1000).toISOString().substring(0, -1); } function fromDateTimeString(s: string) { return Math.floor(new Date(s).getTime() / 1000); } return (

{ev ? "Edit Stream" : "New Stream"}

Title

setTitle(e.target.value)} />

Summary

setSummary(e.target.value)} />

Cover image

setImage(e.target.value)} />

Stream Url

setStream(e.target.value)} />
Stream type should be HLS

Status

{[StreamState.Live, StreamState.Planned, StreamState.Ended].map( (v) => ( setStatus(v)} > {v} ) )}
{status === StreamState.Planned && (

Start Time

setStart(fromDateTimeString(e.target.value).toString()) } />
)}
{ev ? "Save" : "Start Stream"}
); } interface NewStreamDialogProps { text?: string; btnClassName?: string; ev?: NostrEvent; onFinish?: (e: NostrEvent) => void; } export function NewStreamDialog({ text, ev, onFinish, btnClassName = "btn", }: NewStreamDialogProps) { return ( ); }