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 ( +
+ + + {displayName} + + {/* Status badges */} +
+ {/* Server muted / space deafened / permission muted — amber mic with slash */} + {showServerMicIcon && ( + + + + + + + + )} + {/* Server deafened — amber headphone with slash */} + {showServerDeafIcon && ( + + + + + + + )} + {/* Self-muted — danger mic with slash */} + {showSelfMicIcon && ( + + + + + + )} + {/* Self-deafened — danger headphone with slash */} + {showSelfDeafIcon && ( + + + + + )} + {/* Camera active */} + {isCameraOn && ( + + + + )} + {/* Screen sharing LIVE badge */} + {isScreenSharing && ( + LIVE + )} + {/* Locally muted — volume X icon */} + {isLocallyMuted && ( + + + + + + + + )} +
+
+ ); +}