import "./index.css"; import { useCallback, useContext, useEffect, useState } from "react"; import { EventKind, NostrEvent } from "@snort/system"; import { unixNow } from "@snort/shared"; import { FormattedMessage, useIntl } from "react-intl"; import { SnortContext } from "@snort/system-react"; import { extractStreamInfo, findTag } from "@/utils"; import { useLogin } from "@/hooks/login"; import { GOAL, StreamState, defaultRelays } from "@/const"; import { DefaultButton } from "@/element/buttons"; import Pill from "@/element/pill"; import { StreamInput } from "./input"; import { GoalSelector } from "./goal-selector"; import GameDatabase, { GameInfo } from "@/service/game-database"; import CategoryInput from "./category-input"; import { FileUploader } from "@/element/file-uploader"; import AmountInput from "@/element/amount-input"; export interface StreamEditorProps { ev?: NostrEvent; onFinish?: (ev: NostrEvent) => void; options?: { canSetTitle?: boolean; canSetSummary?: boolean; canSetImage?: boolean; canSetStatus?: boolean; canSetStream?: boolean; canSetTags?: boolean; canSetContentWarning?: boolean; }; } export function StreamEditor({ ev, onFinish, options }: StreamEditorProps) { const [title, setTitle] = useState(""); const [summary, setSummary] = useState(""); const [image, setImage] = useState(""); const [stream, setStream] = useState(""); const [recording, setRecording] = useState(""); const [status, setStatus] = useState(""); const [start, setStart] = useState(); const [tags, setTags] = useState([]); const [contentWarning, setContentWarning] = useState(false); const [isValid, setIsValid] = useState(false); const [goal, setGoal] = useState(); const [goalName, setGoalName] = useState(""); const [goalAmount, setGoalMount] = useState(0); const [game, setGame] = useState(); const [gameId, setGameId] = useState(); const [error, setError] = useState(""); const login = useLogin(); const { formatMessage } = useIntl(); const system = useContext(SnortContext); useEffect(() => { const { gameInfo, gameId, title, summary, image, stream, status, starts, tags, contentWarning, goal, recording } = extractStreamInfo(ev); setTitle(title ?? ""); setSummary(summary ?? ""); setImage(image ?? ""); setStream(stream ?? ""); setStatus(status ?? StreamState.Live); setRecording(recording ?? ""); setStart(starts); setTags(tags ?? []); setContentWarning(contentWarning !== undefined); setGoal(goal); setGameId(gameId); if (gameInfo) { setGame(gameInfo); } else if (gameId) { new GameDatabase().getGame(gameId).then(setGame); } }, []); 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(ev !== undefined || validate()); }, [validate, title, summary, image, stream]); async function publishStream() { const pub = login?.publisher(); if (pub) { let thisGoal = goal; if (!goal && goalName && goalAmount) { const goalEvent = await pub.generic(eb => { return eb .kind(GOAL) .tag(["amount", String(goalAmount * 1000)]) .tag(["relays", ...Object.keys(defaultRelays)]) .content(goalName); }); await system.BroadcastEvent(goalEvent); thisGoal = goalEvent.id; } 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(EventKind.LiveEvent) .tag(["d", dTag]) .tag(["title", title]) .tag(["summary", summary]) .tag(["image", image]) .tag(["status", status]) .tag(["starts", starts]); if (status === StreamState.Live) { eb.tag(["streaming", stream]); } if (status === StreamState.Ended) { eb.tag(["ends", ends]); if (recording) { eb.tag(["recording", recording]); } } for (const tx of tags) { eb.tag(["t", tx.trim()]); } if (contentWarning) { eb.tag(["content-warning", "nsfw"]); } if (thisGoal && thisGoal.length > 0) { eb.tag(["goal", thisGoal]); } if (gameId) { eb.tag(["t", gameId]); } return eb; }); console.debug(evNew); onFinish && onFinish(evNew); } } const startsTimestamp = parseInt(start ?? (new Date() / 1000)); const startsDate = new Date(startsTimestamp * 1000); return ( <>

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

{(options?.canSetTitle ?? true) && ( }> setTitle(e.target.value)} /> )} {(options?.canSetSummary ?? true) && ( }>