import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { ActivityIndicator, Platform, Pressable, StyleSheet, Text, View, } from 'react-native'; import { joinRoom, type Room } from 'trystero'; type DemoRole = 'broadcaster' | 'receiver'; type MediaState = 'idle' | 'requesting' | 'ready' | 'waiting' | 'live' | 'error'; const APP_ID = 'run-cloud-live-camera-relay-v1'; const ROOM_PATTERN = /^[a-z0-9-]{8,80}$/i; function demoParameters(): { role: DemoRole; roomId: string; receiver: number } { if (typeof window === 'undefined') { return { role: 'receiver', roomId: 'run-cloud-preview', receiver: 1 }; } const params = new URLSearchParams(window.location.search); const room = params.get('room') ?? ''; const receiver = Number(params.get('receiver')); return { role: params.get('role') === 'broadcaster' ? 'broadcaster' : 'receiver', roomId: ROOM_PATTERN.test(room) ? room : 'run-cloud-preview', receiver: Number.isInteger(receiver) && receiver > 0 ? receiver : 1, }; } function VideoSurface({ stream, muted, label, }: { stream: MediaStream; muted: boolean; label: string; }) { const ref = useRef(null); useEffect(() => { const video = ref.current; if (!video) return; video.srcObject = stream; void video.play().catch(() => undefined); return () => { video.srcObject = null; }; }, [stream]); return