import "./index.css" import { useCallback, useContext, useEffect, useState } from "react" import { EventKind, type 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, { type 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(() => { if (!ev) return 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 ((options?.canSetStream ?? true) && (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?.(evNew) } } const startsTimestamp = Number(start ?? Date.now() / 1000) const startsDate = new Date(startsTimestamp * 1000) return ( <>

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

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