minimal streamplace frontend
8
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix offline stream status

Juliet 51ff5f3a f08d0ba7

+26 -33
+1 -1
src/components/LoginButton.tsx
··· 41 41 fallback={ 42 42 <> 43 43 <button 44 - class="bg-sp-accent text-sp-bg hover:bg-sp-accent/80 rounded-sm px-3 py-1.5 text-sm font-medium transition-colors" 44 + class="bg-sp-accent text-sp-bg hover:bg-sp-accent/80 rounded-sm border border-transparent px-3 py-1.5 text-sm font-medium transition-colors" 45 45 onClick={() => setShowLoginModal(true)} 46 46 > 47 47 Sign in
+25 -32
src/components/VideoPlayer.tsx
··· 1 1 import { Maximize, Volume2, VolumeOff } from "lucide-solid"; 2 2 import { createSignal, onCleanup, onMount, Show } from "solid-js"; 3 3 4 + import { fetchLiveUsers } from "../lib/api"; 4 5 import { connectWhep, type WhepConnection } from "../lib/whep"; 5 6 6 7 export interface VideoPlayerProps { ··· 12 13 let containerEl!: HTMLDivElement; 13 14 let connection: WhepConnection | undefined; 14 15 15 - const [status, setStatus] = createSignal<"connecting" | "live" | "error" | "idle">("idle"); 16 + const [status, setStatus] = createSignal<"connecting" | "live" | "offline">("connecting"); 16 17 const [muted, setMuted] = createSignal(true); 17 18 const [volume, setVolume] = createSignal(1); 18 - const [errorMsg, setErrorMsg] = createSignal(""); 19 19 const [showControls, setShowControls] = createSignal(false); 20 20 let controlsTimer: ReturnType<typeof setTimeout> | undefined; 21 + let pollTimer: ReturnType<typeof setInterval> | undefined; 21 22 22 23 const flashControls = () => { 23 24 setShowControls(true); ··· 26 27 }; 27 28 28 29 const connect = async () => { 29 - setStatus("connecting"); 30 - setErrorMsg(""); 31 - 32 30 try { 33 31 connection = await connectWhep(props.handle); 34 32 ··· 36 34 const state = connection!.pc.iceConnectionState; 37 35 if (state === "connected" || state === "completed") { 38 36 setStatus("live"); 39 - } else if (state === "failed" || state === "disconnected") { 40 - setStatus("error"); 41 - setErrorMsg("Connection lost"); 42 - } 43 - }; 44 - 45 - connection.pc.onconnectionstatechange = () => { 46 - if (connection!.pc.connectionState === "failed") { 47 - setStatus("error"); 48 - setErrorMsg("Connection failed"); 49 37 } 50 38 }; 51 39 52 40 videoEl.srcObject = connection.stream; 53 41 videoEl.play().catch(() => {}); 54 - } catch (err) { 55 - setStatus("error"); 56 - setErrorMsg(err instanceof Error ? err.message : "Failed to connect"); 42 + } catch { 43 + // poll will handle showing offline 57 44 } 58 45 }; 59 46 ··· 63 50 connection = undefined; 64 51 } 65 52 videoEl.srcObject = null; 66 - setStatus("idle"); 53 + }; 54 + 55 + const poll = async () => { 56 + try { 57 + const liveUsers = await fetchLiveUsers(); 58 + const isLive = liveUsers.some((u) => u.handle === props.handle); 59 + if (isLive && !connection) { 60 + setStatus("connecting"); 61 + connect(); 62 + } else if (!isLive && status() !== "offline") { 63 + disconnect(); 64 + setStatus("offline"); 65 + } 66 + } catch { 67 + // ignore, try again next poll 68 + } 67 69 }; 68 70 69 71 const toggleMute = () => { ··· 100 102 101 103 onMount(() => { 102 104 videoEl.muted = true; 103 - connect(); 104 105 videoEl.addEventListener("pause", resumeOnPause); 106 + poll(); 107 + pollTimer = setInterval(poll, 10_000); 105 108 }); 106 109 107 110 onCleanup(() => { 108 111 disconnect(); 112 + clearInterval(pollTimer); 109 113 clearTimeout(controlsTimer); 110 114 videoEl.removeEventListener("pause", resumeOnPause); 111 115 }); ··· 127 131 Connecting... 128 132 </div> 129 133 </Show> 130 - <Show when={status() === "error"}> 131 - <div class="text-center"> 132 - <div class="text-sp-red">{errorMsg() || "Error"}</div> 133 - <button 134 - class="bg-sp-surface text-sp-text hover:bg-sp-border mt-2 rounded-sm px-3 py-1.5 text-sm transition-colors" 135 - onClick={connect} 136 - > 137 - Retry 138 - </button> 139 - </div> 140 - </Show> 141 - <Show when={status() === "idle"}> 134 + <Show when={status() === "offline"}> 142 135 <div class="text-sp-dim">Stream offline</div> 143 136 </Show> 144 137 </div>