import React, { useEffect, useState, useRef } from 'react'; import { useVoiceStore } from '../../stores/voiceStore'; import { useUIStore } from '../../stores/uiStore'; import { useAuthStore } from '../../stores/authStore'; import { useSpaceStore, getChannelOrigin, getMyUserIdForOrigin } from '../../stores/spaceStore'; import { ScreenShareSettingsPopover } from './ScreenShareSettingsPopover'; import { hasPermissionBit, PermissionBits } from '../../utils/permissions'; import { handleMuteAction, handleDeafenAction, handleCameraAction, handleScreenShareAction, handleDisconnectAction } from '../../utils/voiceActions'; const btnBase = 'w-10 h-10 flex items-center justify-center rounded-full transition-colors'; const btnDefault = `${btnBase} bg-surface-channel text-txt-secondary hover:bg-surface-elevated hover:text-txt-primary`; const btnActive = (color: string) => `${btnBase} bg-${color}/20 text-${color} hover:bg-${color}/30`; const btnGreen = `${btnBase} bg-surface-channel text-status-online hover:bg-surface-elevated`; export function VoiceControlBar() { const isMuted = useVoiceStore((s) => s.isMuted); const isDeafened = useVoiceStore((s) => s.isDeafened); const isCameraOn = useVoiceStore((s) => s.isCameraOn); const isScreenSharing = useVoiceStore((s) => s.isScreenSharing); const voiceChatOpen = useUIStore((s) => s.voiceChatOpen); const toggleVoiceChat = useUIStore((s) => s.toggleVoiceChat); const voiceFullscreen = useUIStore((s) => s.voiceFullscreen); const toggleVoiceFullscreen = useUIStore((s) => s.toggleVoiceFullscreen); const currentVoiceChannelId = useVoiceStore((s) => s.currentVoiceChannelId); const myUser = useAuthStore((s) => s.user); const spaceId = useSpaceStore((s) => currentVoiceChannelId ? s.channelToSpaceMap.get(currentVoiceChannelId) : null); const myOriginId = useSpaceStore((s) => currentVoiceChannelId ? getMyUserIdForOrigin(getChannelOrigin(currentVoiceChannelId)) : s.members.find(m => m.userId === myUser?.id)?.userId ?? myUser?.id); const spaceMutedUserIds = useVoiceStore((s) => s.spaceMutedUserIds); const spaceDeafenedUserIds = useVoiceStore((s) => s.spaceDeafenedUserIds); const isSpaceMuted = !!(myOriginId && spaceId && spaceMutedUserIds.has(`${spaceId}:${myOriginId}`)); const isSpaceDeafened = !!(myOriginId && spaceId && spaceDeafenedUserIds.has(`${spaceId}:${myOriginId}`)); const activeDmCall = useVoiceStore((s) => s.activeDmCall); const channelPerms = useSpaceStore((s) => currentVoiceChannelId ? s.channelPermissions.get(currentVoiceChannelId) : undefined); const isDmCall = !!activeDmCall; const canSpeak = isDmCall || hasPermissionBit(channelPerms, PermissionBits.SPEAK); const canStream = isDmCall || hasPermissionBit(channelPerms, PermissionBits.STREAM); const [qualityOpen, setQualityOpen] = useState(false); const qualityBtnRef = useRef(null); const handleMute = React.useCallback(() => { handleMuteAction(isSpaceMuted, isSpaceDeafened); }, [isSpaceMuted, isSpaceDeafened]); const handleDeafen = React.useCallback(() => { handleDeafenAction(isSpaceDeafened); }, [isSpaceDeafened]); const handleCamera = () => handleCameraAction(); const handleScreenShare = () => handleScreenShareAction(); const handleDisconnect = () => handleDisconnectAction(); const handleFullscreen = () => { toggleVoiceFullscreen(); }; useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape' && voiceFullscreen) { useUIStore.getState().setVoiceFullscreen(false); } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [voiceFullscreen]); return (
{/* Mute */} {/* Deafen */} {/* Camera */} {canSpeak && ( )} {/* Screen Share */} {canStream && ( )} {/* Video Quality */} setQualityOpen(false)} anchorRef={qualityBtnRef} /> {/* Separator */}
{/* Chat Toggle */} {/* Fullscreen Toggle */} {/* Separator */}
{/* Disconnect */}
); }