diff --git a/packages/web/src/components/voice/VoiceUserRow.tsx b/packages/web/src/components/voice/VoiceUserRow.tsx new file mode 100644 index 00000000..3d91f8cc --- /dev/null +++ b/packages/web/src/components/voice/VoiceUserRow.tsx @@ -0,0 +1,129 @@ +import React from 'react'; +import { Avatar } from '../ui/Avatar'; + +export interface VoiceUserRowProps { + userId: string; + displayName: string; + avatar: string | null; + avatarColor?: string; + isMuted?: boolean; + isDeafened?: boolean; + isCameraOn?: boolean; + isScreenSharing?: boolean; + isServerMuted?: boolean; + isServerDeafened?: boolean; + isPermissionMuted?: boolean; + isLocallyMuted?: boolean; + isSpeaking?: boolean; + size?: 'compact' | 'default'; + className?: string; +} + +export function VoiceUserRow({ + userId, + displayName, + avatar, + avatarColor, + isMuted, + isDeafened, + isCameraOn, + isScreenSharing, + isServerMuted, + isServerDeafened, + isPermissionMuted, + isLocallyMuted, + isSpeaking, + size = 'default', + className = '', +}: VoiceUserRowProps) { + const avatarSize = size === 'compact' ? 20 : 24; + + // Mic icon priority: server muted/deafened/permission muted (amber) > self-muted (danger) + const showServerMicIcon = isServerMuted || isServerDeafened || isPermissionMuted; + const showSelfMicIcon = !showServerMicIcon && isMuted; + + // Deafen icon priority: server deafened (amber) > self-deafened (danger) + const showServerDeafIcon = isServerDeafened; + const showSelfDeafIcon = !isServerDeafened && isDeafened; + + return ( +