import type { Component } from "solid-js"; import { Show, createEffect, onCleanup } from "solid-js"; import { MicOff, VolumeX, AlertTriangle } from "lucide-solid"; import Avatar from "../common/Avatar"; import { openProfileCard } from "../../stores/ui"; import type { VoiceMediaState } from "../../lib/types"; interface VoiceParticipantTileProps { peer_id: string; display_name: string; media_state: VoiceMediaState; stream?: MediaStream | null; is_local?: boolean; connectionState?: RTCPeerConnectionState; } const VoiceParticipantTile: Component = (props) => { let videoRef: HTMLVideoElement | undefined; // attach stream to video element when it changes createEffect(() => { const currentStream = props.stream; if (videoRef && currentStream) { videoRef.srcObject = currentStream; } }); // cleanup video element on unmount onCleanup(() => { if (videoRef) { videoRef.srcObject = null; } }); const hasVideo = () => { return ( props.stream && (props.media_state.video_enabled || props.media_state.screen_sharing) ); }; // per-peer connection state ring styling const connectionRingClass = () => { const state = props.connectionState; switch (state) { case "connected": return "ring-2 ring-green-500/70"; case "connecting": case "new": return "ring-2 ring-amber-400/70 animate-pulse"; case "failed": return "ring-2 ring-red-500/80"; case "disconnected": return "ring-2 ring-white/30"; case "closed": default: return ""; } }; return (
{ openProfileCard({ peerId: props.peer_id, displayName: props.display_name, anchorX: e.clientX, anchorY: e.clientY, }); }} > {props.display_name}
} >