refactor: rename "Server Mute/Deafen" to "Space Mute/Deafen" across entire stack
Aligns voice moderation terminology with Backspace's "Spaces" branding. Renames WS protocol strings, backend handlers, frontend store/hooks/utils, user-facing labels, and documentation — 15 files, zero functional changes.
This commit is contained in:
@@ -178,9 +178,9 @@ export class SpeakingDetector {
|
||||
if (spaceId) {
|
||||
const myOriginId = getMyUserIdForOrigin(getChannelOrigin(channelId));
|
||||
if (myOriginId) {
|
||||
const serverKey = `${spaceId}:${myOriginId}`;
|
||||
effectiveMuted = effectiveMuted || store.serverMutedUserIds.has(serverKey);
|
||||
effectiveDeafened = effectiveDeafened || store.serverDeafenedUserIds.has(serverKey);
|
||||
const spaceKey = `${spaceId}:${myOriginId}`;
|
||||
effectiveMuted = effectiveMuted || store.spaceMutedUserIds.has(spaceKey);
|
||||
effectiveDeafened = effectiveDeafened || store.spaceDeafenedUserIds.has(spaceKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,14 +36,14 @@ export function ChannelSidebar() {
|
||||
const toggleDeafen = useVoiceStore((s) => s.toggleDeafen);
|
||||
const spaceId = useSpaceStore((s) => currentVoiceChannelId ? s.channelToSpaceMap.get(currentVoiceChannelId) : null);
|
||||
const myOriginId = useSpaceStore((s) => currentVoiceChannelId ? getMyUserIdForOrigin(getChannelOrigin(currentVoiceChannelId)) : s.members.find(m => m.userId === user?.id)?.userId ?? user?.id);
|
||||
const serverMutedUserIds = useVoiceStore((s) => s.serverMutedUserIds);
|
||||
const serverDeafenedUserIds = useVoiceStore((s) => s.serverDeafenedUserIds);
|
||||
const spaceMutedUserIds = useVoiceStore((s) => s.spaceMutedUserIds);
|
||||
const spaceDeafenedUserIds = useVoiceStore((s) => s.spaceDeafenedUserIds);
|
||||
const permissionMutedUserIds = useVoiceStore((s) => s.permissionMutedUserIds);
|
||||
|
||||
// Drag-and-drop state for moving users between voice channels
|
||||
const [voiceDragState, setVoiceDragState] = useState<{ userId: string; fromChannelId: string } | null>(null);
|
||||
const isServerMuted = !!(myOriginId && spaceId && serverMutedUserIds.has(`${spaceId}:${myOriginId}`));
|
||||
const isServerDeafened = !!(myOriginId && spaceId && serverDeafenedUserIds.has(`${spaceId}:${myOriginId}`));
|
||||
const isSpaceMuted = !!(myOriginId && spaceId && spaceMutedUserIds.has(`${spaceId}:${myOriginId}`));
|
||||
const isSpaceDeafened = !!(myOriginId && spaceId && spaceDeafenedUserIds.has(`${spaceId}:${myOriginId}`));
|
||||
const isPermissionMuted = !!(myOriginId && spaceId && permissionMutedUserIds.has(`${spaceId}:${myOriginId}`));
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
@@ -64,7 +64,7 @@ export function ChannelSidebar() {
|
||||
}, [setFloatingPanelHeight]);
|
||||
|
||||
const handleMicToggle = async () => {
|
||||
if (isServerMuted || isServerDeafened || isPermissionMuted) return;
|
||||
if (isSpaceMuted || isSpaceDeafened || isPermissionMuted) return;
|
||||
const wasDeafened = useVoiceStore.getState().isDeafened;
|
||||
toggleMic();
|
||||
broadcastVoiceStatus();
|
||||
@@ -75,7 +75,7 @@ export function ChannelSidebar() {
|
||||
};
|
||||
|
||||
const handleDeafenToggle = async () => {
|
||||
if (isServerDeafened) return;
|
||||
if (isSpaceDeafened) return;
|
||||
toggleDeafen();
|
||||
broadcastVoiceStatus();
|
||||
broadcastDeafenViaLiveKit();
|
||||
@@ -133,8 +133,8 @@ export function ChannelSidebar() {
|
||||
user={user}
|
||||
isMuted={isMuted}
|
||||
isDeafened={isDeafened}
|
||||
isServerMuted={isServerMuted}
|
||||
isServerDeafened={isServerDeafened}
|
||||
isSpaceMuted={isSpaceMuted}
|
||||
isSpaceDeafened={isSpaceDeafened}
|
||||
isPermissionMuted={isPermissionMuted}
|
||||
onMicToggle={handleMicToggle}
|
||||
onDeafenToggle={handleDeafenToggle}
|
||||
@@ -496,8 +496,8 @@ function UserAreaPanel({
|
||||
user,
|
||||
isMuted,
|
||||
isDeafened,
|
||||
isServerMuted,
|
||||
isServerDeafened,
|
||||
isSpaceMuted,
|
||||
isSpaceDeafened,
|
||||
isPermissionMuted,
|
||||
onMicToggle,
|
||||
onDeafenToggle,
|
||||
@@ -506,8 +506,8 @@ function UserAreaPanel({
|
||||
user: any;
|
||||
isMuted: boolean;
|
||||
isDeafened: boolean;
|
||||
isServerMuted: boolean;
|
||||
isServerDeafened: boolean;
|
||||
isSpaceMuted: boolean;
|
||||
isSpaceDeafened: boolean;
|
||||
isPermissionMuted: boolean;
|
||||
onMicToggle: () => void;
|
||||
onDeafenToggle: () => void;
|
||||
@@ -823,15 +823,15 @@ function UserAreaPanel({
|
||||
<button
|
||||
onClick={onMicToggle}
|
||||
className={`w-8 h-8 flex items-center justify-center hover:bg-interactive-hover rounded-l-[4px] transition-colors ${
|
||||
(isServerMuted || isServerDeafened || isPermissionMuted) ? 'text-accent-amber cursor-not-allowed'
|
||||
(isSpaceMuted || isSpaceDeafened || isPermissionMuted) ? 'text-accent-amber cursor-not-allowed'
|
||||
: isMuted || isDeafened ? 'text-txt-danger' : 'text-txt-tertiary hover:text-txt-primary'
|
||||
}`}
|
||||
title={(isPermissionMuted) ? 'Muted (No Speak Permission)' : (isServerMuted || isServerDeafened) ? 'Server Muted' : isMuted ? 'Unmute' : 'Mute'}
|
||||
title={(isPermissionMuted) ? 'Muted (No Speak Permission)' : (isSpaceMuted || isSpaceDeafened) ? 'Space Muted' : isMuted ? 'Unmute' : 'Mute'}
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z" />
|
||||
<path d="M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z" />
|
||||
{(isMuted || isDeafened || isServerMuted || isServerDeafened || isPermissionMuted) && <line x1="3" y1="3" x2="21" y2="21" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" />}
|
||||
{(isMuted || isDeafened || isSpaceMuted || isSpaceDeafened || isPermissionMuted) && <line x1="3" y1="3" x2="21" y2="21" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" />}
|
||||
</svg>
|
||||
</button>
|
||||
{/* Input chevron */}
|
||||
@@ -851,14 +851,14 @@ function UserAreaPanel({
|
||||
<button
|
||||
onClick={onDeafenToggle}
|
||||
className={`w-8 h-8 flex items-center justify-center hover:bg-interactive-hover rounded-l-[4px] transition-colors ${
|
||||
isServerDeafened ? 'text-accent-amber cursor-not-allowed'
|
||||
isSpaceDeafened ? 'text-accent-amber cursor-not-allowed'
|
||||
: isDeafened ? 'text-txt-danger' : 'text-txt-tertiary hover:text-txt-primary'
|
||||
}`}
|
||||
title={isServerDeafened ? 'Server Deafened' : isDeafened ? 'Undeafen' : 'Deafen'}
|
||||
title={isSpaceDeafened ? 'Space Deafened' : isDeafened ? 'Undeafen' : 'Deafen'}
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 3c-4.97 0-9 4.03-9 9v7c0 1.1.9 2 2 2h2v-7H5v-2c0-3.87 3.13-7 7-7s7 3.13 7 7v2h-2v7h2c1.1 0 2-.9 2-2v-7c0-4.97-4.03-9-9-9z" />
|
||||
{(isDeafened || isServerDeafened) && <line x1="3" y1="3" x2="21" y2="21" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" />}
|
||||
{(isDeafened || isSpaceDeafened) && <line x1="3" y1="3" x2="21" y2="21" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" />}
|
||||
</svg>
|
||||
</button>
|
||||
{/* Output chevron */}
|
||||
|
||||
@@ -68,7 +68,7 @@ function AudioTrackElement({
|
||||
export function GlobalAudioRenderer() {
|
||||
const participants = useVoiceStore((s) => s.participants);
|
||||
const isDeafenedIntent = useVoiceStore((s) => s.isDeafened);
|
||||
const serverDeafenedUserIds = useVoiceStore((s) => s.serverDeafenedUserIds);
|
||||
const spaceDeafenedUserIds = useVoiceStore((s) => s.spaceDeafenedUserIds);
|
||||
const currentVoiceChannelId = useVoiceStore((s) => s.currentVoiceChannelId);
|
||||
const outputVolume = useVoiceStore((s) => s.outputVolume);
|
||||
const participantVolumes = useVoiceStore((s) => s.participantVolumes);
|
||||
@@ -83,8 +83,8 @@ export function GlobalAudioRenderer() {
|
||||
// Compute effective deafened: user intent || server enforcement
|
||||
const spaceId = useSpaceStore((s) => currentVoiceChannelId ? s.channelToSpaceMap.get(currentVoiceChannelId) : null);
|
||||
const myOriginId = currentVoiceChannelId ? getMyUserIdForOrigin(getChannelOrigin(currentVoiceChannelId)) : undefined;
|
||||
const serverKey = (spaceId && myOriginId) ? `${spaceId}:${myOriginId}` : '';
|
||||
const isDeafened = isDeafenedIntent || serverDeafenedUserIds.has(serverKey);
|
||||
const spaceKey = (spaceId && myOriginId) ? `${spaceId}:${myOriginId}` : '';
|
||||
const isDeafened = isDeafenedIntent || spaceDeafenedUserIds.has(spaceKey);
|
||||
|
||||
// Determine if someone is currently speaking (for stream attenuation)
|
||||
const someoneIsSpeaking = participants.some((p) => !p.isLocal && speakingParticipantIds.has(p.identity));
|
||||
|
||||
@@ -31,8 +31,8 @@ export function VoiceChannel({ channelId, channelName, onClick, locked, dragStat
|
||||
const localIsDeafened = useVoiceStore((s) => s.isDeafened);
|
||||
const localIsMuted = useVoiceStore((s) => s.isMuted);
|
||||
const voiceUserStates = useVoiceStore((s) => s.voiceUserStates);
|
||||
const serverMutedUserIds = useVoiceStore((s) => s.serverMutedUserIds);
|
||||
const serverDeafenedUserIds = useVoiceStore((s) => s.serverDeafenedUserIds);
|
||||
const spaceMutedUserIds = useVoiceStore((s) => s.spaceMutedUserIds);
|
||||
const spaceDeafenedUserIds = useVoiceStore((s) => s.spaceDeafenedUserIds);
|
||||
const permissionMutedUserIds = useVoiceStore((s) => s.permissionMutedUserIds);
|
||||
const participantMutes = useVoiceStore((s) => s.participantMutes);
|
||||
const unwatchedCameras = useVoiceStore((s) => s.unwatchedCameras);
|
||||
@@ -147,8 +147,8 @@ export function VoiceChannel({ channelId, channelName, onClick, locked, dragStat
|
||||
const hasCamera = participant?.isCameraOn ?? wsStatus?.isCameraOn ?? false;
|
||||
const isScreenSharing = participant?.isScreenSharing ?? wsStatus?.isScreenSharing ?? false;
|
||||
const spaceId = channelToSpaceMap.get(channelId);
|
||||
const isServerMuted = serverMutedUserIds.has(`${spaceId}:${userId}`);
|
||||
const isServerDeafened = serverDeafenedUserIds.has(`${spaceId}:${userId}`);
|
||||
const isSpaceMuted = spaceMutedUserIds.has(`${spaceId}:${userId}`);
|
||||
const isSpaceDeafened = spaceDeafenedUserIds.has(`${spaceId}:${userId}`);
|
||||
const isPermissionMuted = permissionMutedUserIds.has(`${spaceId}:${userId}`);
|
||||
|
||||
const isDraggable = canMoveMembers && userId !== myUser?.id;
|
||||
@@ -181,8 +181,8 @@ export function VoiceChannel({ channelId, channelName, onClick, locked, dragStat
|
||||
<span className="text-[13px] text-txt-secondary truncate flex-1 min-w-0">{displayName}</span>
|
||||
{/* Status badges */}
|
||||
<div className="flex items-center gap-1 flex-shrink-0">
|
||||
{(isServerMuted || isServerDeafened || isPermissionMuted) && (
|
||||
<span title={isPermissionMuted ? "Muted (No Speak Permission)" : isServerMuted ? "Server Muted" : "Muted (Server Deafened)"}>
|
||||
{(isSpaceMuted || isSpaceDeafened || isPermissionMuted) && (
|
||||
<span title={isPermissionMuted ? "Muted (No Speak Permission)" : isSpaceMuted ? "Space Muted" : "Muted (Space Deafened)"}>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" className="text-accent-amber">
|
||||
<path d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z" />
|
||||
<path d="M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z" />
|
||||
@@ -190,22 +190,22 @@ export function VoiceChannel({ channelId, channelName, onClick, locked, dragStat
|
||||
</svg>
|
||||
</span>
|
||||
)}
|
||||
{isServerDeafened && (
|
||||
<span title="Server Deafened">
|
||||
{isSpaceDeafened && (
|
||||
<span title="Space Deafened">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" className="text-accent-amber">
|
||||
<path d="M12 3c-4.97 0-9 4.03-9 9v7c0 1.1.9 2 2 2h2v-7H5v-2c0-3.87 3.13-7 7-7s7 3.13 7 7v2h-2v7h2c1.1 0 2-.9 2-2v-7c0-4.97-4.03-9-9-9z" />
|
||||
<line x1="3" y1="3" x2="21" y2="21" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" />
|
||||
</svg>
|
||||
</span>
|
||||
)}
|
||||
{!isServerMuted && !isServerDeafened && !isPermissionMuted && isMuted && (
|
||||
{!isSpaceMuted && !isSpaceDeafened && !isPermissionMuted && isMuted && (
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" className="text-txt-danger">
|
||||
<path d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z" />
|
||||
<path d="M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z" />
|
||||
<line x1="3" y1="3" x2="21" y2="21" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" />
|
||||
</svg>
|
||||
)}
|
||||
{!isServerDeafened && isParticipantDeafened && (
|
||||
{!isSpaceDeafened && isParticipantDeafened && (
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" className="text-txt-danger">
|
||||
<path d="M12 3c-4.97 0-9 4.03-9 9v7c0 1.1.9 2 2 2h2v-7H5v-2c0-3.87 3.13-7 7-7s7 3.13 7 7v2h-2v7h2c1.1 0 2-.9 2-2v-7c0-4.97-4.03-9-9-9z" />
|
||||
<line x1="3" y1="3" x2="21" y2="21" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" />
|
||||
|
||||
@@ -32,10 +32,10 @@ export function VoiceControlBar() {
|
||||
const spaceId = useSpaceStore((s) => currentVoiceChannelId ? s.channelToSpaceMap.get(currentVoiceChannelId) : null);
|
||||
const voiceOrigin = currentVoiceChannelId ? getChannelOrigin(currentVoiceChannelId) : '';
|
||||
const myOriginId = useSpaceStore((s) => currentVoiceChannelId ? getMyUserIdForOrigin(getChannelOrigin(currentVoiceChannelId)) : s.members.find(m => m.userId === myUser?.id)?.userId ?? myUser?.id);
|
||||
const serverMutedUserIds = useVoiceStore((s) => s.serverMutedUserIds);
|
||||
const serverDeafenedUserIds = useVoiceStore((s) => s.serverDeafenedUserIds);
|
||||
const isServerMuted = !!(myOriginId && spaceId && serverMutedUserIds.has(`${spaceId}:${myOriginId}`));
|
||||
const isServerDeafened = !!(myOriginId && spaceId && serverDeafenedUserIds.has(`${spaceId}:${myOriginId}`));
|
||||
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;
|
||||
@@ -45,7 +45,7 @@ export function VoiceControlBar() {
|
||||
const qualityBtnRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
const handleMute = React.useCallback(async () => {
|
||||
if (isServerMuted || isServerDeafened) return;
|
||||
if (isSpaceMuted || isSpaceDeafened) return;
|
||||
const wasDeafened = useVoiceStore.getState().isDeafened;
|
||||
toggleMic();
|
||||
broadcastVoiceStatus();
|
||||
@@ -53,14 +53,14 @@ export function VoiceControlBar() {
|
||||
if (wasDeafened && !useVoiceStore.getState().isDeafened) {
|
||||
broadcastDeafenViaLiveKit();
|
||||
}
|
||||
}, [isServerMuted, isServerDeafened, toggleMic]);
|
||||
}, [isSpaceMuted, isSpaceDeafened, toggleMic]);
|
||||
|
||||
const handleDeafen = React.useCallback(async () => {
|
||||
if (isServerDeafened) return;
|
||||
if (isSpaceDeafened) return;
|
||||
toggleDeafen();
|
||||
broadcastVoiceStatus();
|
||||
broadcastDeafenViaLiveKit();
|
||||
}, [isServerDeafened, toggleDeafen]);
|
||||
}, [isSpaceDeafened, toggleDeafen]);
|
||||
|
||||
const handleCamera = async () => {
|
||||
const room = getActiveRoom();
|
||||
@@ -147,35 +147,35 @@ export function VoiceControlBar() {
|
||||
{/* Mute */}
|
||||
<button
|
||||
onClick={handleMute}
|
||||
className={(isServerMuted || isServerDeafened)
|
||||
className={(isSpaceMuted || isSpaceDeafened)
|
||||
? `${btnBase} bg-accent-amber/20 text-accent-amber cursor-not-allowed`
|
||||
: isMuted || isDeafened
|
||||
? `${btnBase} bg-accent-rose/20 text-txt-danger hover:bg-accent-rose/30`
|
||||
: btnDefault
|
||||
}
|
||||
title={(isServerMuted || isServerDeafened) ? (isMuted ? 'Server Muted (self-muted)' : 'Server Muted') : isMuted ? 'Unmute (M)' : 'Mute (M)'}
|
||||
title={(isSpaceMuted || isSpaceDeafened) ? (isMuted ? 'Space Muted (self-muted)' : 'Space Muted') : isMuted ? 'Unmute (M)' : 'Mute (M)'}
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z" />
|
||||
<path d="M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z" />
|
||||
{(isMuted || isDeafened || isServerMuted || isServerDeafened) && <line x1="3" y1="3" x2="21" y2="21" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" />}
|
||||
{(isMuted || isDeafened || isSpaceMuted || isSpaceDeafened) && <line x1="3" y1="3" x2="21" y2="21" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" />}
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{/* Deafen */}
|
||||
<button
|
||||
onClick={handleDeafen}
|
||||
className={isServerDeafened
|
||||
className={isSpaceDeafened
|
||||
? `${btnBase} bg-accent-amber/20 text-accent-amber cursor-not-allowed`
|
||||
: isDeafened
|
||||
? `${btnBase} bg-accent-rose/20 text-txt-danger hover:bg-accent-rose/30`
|
||||
: btnDefault
|
||||
}
|
||||
title={isServerDeafened ? 'Server Deafened' : isDeafened ? 'Undeafen (D)' : 'Deafen (D)'}
|
||||
title={isSpaceDeafened ? 'Space Deafened' : isDeafened ? 'Undeafen (D)' : 'Deafen (D)'}
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 3c-4.97 0-9 4.03-9 9v7c0 1.1.9 2 2 2h2v-7H5v-2c0-3.87 3.13-7 7-7s7 3.13 7 7v2h-2v7h2c1.1 0 2-.9 2-2v-7c0-4.97-4.03-9-9-9z" />
|
||||
{(isDeafened || isServerDeafened) && <line x1="3" y1="3" x2="21" y2="21" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" />}
|
||||
{(isDeafened || isSpaceDeafened) && <line x1="3" y1="3" x2="21" y2="21" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" />}
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ export function VoiceUser({ tile, large }: VoiceUserProps) {
|
||||
const isDeafened = useVoiceStore((s) => s.isDeafened);
|
||||
const currentVoiceChannelId = useVoiceStore((s) => s.currentVoiceChannelId);
|
||||
const isSpeaking = useVoiceStore((s) => s.speakingParticipantIds.has(participant.identity));
|
||||
const serverMutedUserIds = useVoiceStore((s) => s.serverMutedUserIds);
|
||||
const serverDeafenedUserIds = useVoiceStore((s) => s.serverDeafenedUserIds);
|
||||
const spaceMutedUserIds = useVoiceStore((s) => s.spaceMutedUserIds);
|
||||
const spaceDeafenedUserIds = useVoiceStore((s) => s.spaceDeafenedUserIds);
|
||||
const permissionMutedUserIds = useVoiceStore((s) => s.permissionMutedUserIds);
|
||||
const unwatchedCameras = useVoiceStore((s) => s.unwatchedCameras);
|
||||
const participantMutes = useVoiceStore((s) => s.participantMutes);
|
||||
@@ -123,15 +123,15 @@ export function VoiceUser({ tile, large }: VoiceUserProps) {
|
||||
</div>
|
||||
<div className="flex items-center gap-1 flex-shrink-0">
|
||||
{(() => {
|
||||
const isServerMutedUser = spaceId ? serverMutedUserIds.has(`${spaceId}:${participant.userId}`) : false;
|
||||
const isServerDeafenedUser = spaceId ? serverDeafenedUserIds.has(`${spaceId}:${participant.userId}`) : false;
|
||||
const isSpaceMutedUser = spaceId ? spaceMutedUserIds.has(`${spaceId}:${participant.userId}`) : false;
|
||||
const isSpaceDeafenedUser = spaceId ? spaceDeafenedUserIds.has(`${spaceId}:${participant.userId}`) : false;
|
||||
const isPermissionMutedUser = spaceId ? permissionMutedUserIds.has(`${spaceId}:${participant.userId}`) : false;
|
||||
const effectivelyMuted = participant.isMuted || isServerMutedUser || isServerDeafenedUser || isPermissionMutedUser;
|
||||
const effectivelyDeafened = (isLocal ? isDeafened : participant.isDeafened) || isServerDeafenedUser;
|
||||
const effectivelyMuted = participant.isMuted || isSpaceMutedUser || isSpaceDeafenedUser || isPermissionMutedUser;
|
||||
const effectivelyDeafened = (isLocal ? isDeafened : participant.isDeafened) || isSpaceDeafenedUser;
|
||||
return (
|
||||
<>
|
||||
{effectivelyMuted && (
|
||||
<div className={`w-5 h-5 ${(isServerMutedUser || isServerDeafenedUser || isPermissionMutedUser) ? 'bg-accent-amber/90' : 'bg-accent-rose/90'} rounded-full flex items-center justify-center`}>
|
||||
<div className={`w-5 h-5 ${(isSpaceMutedUser || isSpaceDeafenedUser || isPermissionMutedUser) ? 'bg-accent-amber/90' : 'bg-accent-rose/90'} rounded-full flex items-center justify-center`}>
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="white">
|
||||
<path d="M12 2C10.9 2 10 2.9 10 4V12C10 13.1 10.9 14 12 14C13.1 14 14 13.1 14 12V4C14 2.9 13.1 2 12 2Z" />
|
||||
<line
|
||||
@@ -146,7 +146,7 @@ export function VoiceUser({ tile, large }: VoiceUserProps) {
|
||||
</div>
|
||||
)}
|
||||
{effectivelyDeafened && (
|
||||
<div className={`w-5 h-5 ${isServerDeafenedUser ? 'bg-accent-amber/90' : 'bg-accent-rose/90'} rounded-full flex items-center justify-center`}>
|
||||
<div className={`w-5 h-5 ${isSpaceDeafenedUser ? 'bg-accent-amber/90' : 'bg-accent-rose/90'} rounded-full flex items-center justify-center`}>
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="white">
|
||||
<path d="M12 3c-4.97 0-9 4.03-9 9v7c0 1.1.9 2 2 2h2v-7H5v-2c0-3.87 3.13-7 7-7s7 3.13 7 7v2h-2v7h2c1.1 0 2-.9 2-2v-7c0-4.97-4.03-9-9-9z" />
|
||||
<line
|
||||
|
||||
@@ -18,8 +18,8 @@ interface VoiceModMenuItemsProps {
|
||||
* Use inside any container — no portal or positioning logic.
|
||||
*/
|
||||
export function VoiceModMenuItems({ targetUserId, channelId, onAction }: VoiceModMenuItemsProps) {
|
||||
const serverMutedUserIds = useVoiceStore((s) => s.serverMutedUserIds);
|
||||
const serverDeafenedUserIds = useVoiceStore((s) => s.serverDeafenedUserIds);
|
||||
const spaceMutedUserIds = useVoiceStore((s) => s.spaceMutedUserIds);
|
||||
const spaceDeafenedUserIds = useVoiceStore((s) => s.spaceDeafenedUserIds);
|
||||
|
||||
const spacePermissions = useSpaceStore((s) => s.spacePermissions);
|
||||
const currentSpaceId = useSpaceStore((s) => s.currentSpaceId);
|
||||
@@ -38,18 +38,18 @@ export function VoiceModMenuItems({ targetUserId, channelId, onAction }: VoiceMo
|
||||
const voiceOrigin = getChannelOrigin(channelId);
|
||||
const spaceId = useSpaceStore((s) => s.channelToSpaceMap.get(channelId));
|
||||
|
||||
const isServerMuted = serverMutedUserIds.has(`${spaceId}:${targetUserId}`);
|
||||
const isServerDeafened = serverDeafenedUserIds.has(`${spaceId}:${targetUserId}`);
|
||||
const isSpaceMuted = spaceMutedUserIds.has(`${spaceId}:${targetUserId}`);
|
||||
const isSpaceDeafened = spaceDeafenedUserIds.has(`${spaceId}:${targetUserId}`);
|
||||
|
||||
if (!canMuteMembers && !canDeafenMembers && !canMoveMembers && !canDisconnectMembers) return null;
|
||||
|
||||
const handleServerMute = () => {
|
||||
wsSend({ type: 'voice_server_mute', userId: targetUserId, muted: !isServerMuted }, voiceOrigin);
|
||||
const handleSpaceMute = () => {
|
||||
wsSend({ type: 'voice_space_mute', userId: targetUserId, muted: !isSpaceMuted }, voiceOrigin);
|
||||
onAction();
|
||||
};
|
||||
|
||||
const handleServerDeafen = () => {
|
||||
wsSend({ type: 'voice_server_deafen', userId: targetUserId, deafened: !isServerDeafened }, voiceOrigin);
|
||||
const handleSpaceDeafen = () => {
|
||||
wsSend({ type: 'voice_space_deafen', userId: targetUserId, deafened: !isSpaceDeafened }, voiceOrigin);
|
||||
onAction();
|
||||
};
|
||||
|
||||
@@ -69,26 +69,26 @@ export function VoiceModMenuItems({ targetUserId, channelId, onAction }: VoiceMo
|
||||
return (
|
||||
<>
|
||||
{canMuteMembers && (
|
||||
<button onClick={handleServerMute} className={btnClass} style={btnStyle}>
|
||||
<button onClick={handleSpaceMute} className={btnClass} style={btnStyle}>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" className="flex-shrink-0">
|
||||
<path d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z" />
|
||||
<path d="M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z" />
|
||||
{isServerMuted && (
|
||||
{isSpaceMuted && (
|
||||
<line x1="3" y1="3" x2="21" y2="21" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" />
|
||||
)}
|
||||
</svg>
|
||||
{isServerMuted ? 'Server Unmute' : 'Server Mute'}
|
||||
{isSpaceMuted ? 'Space Unmute' : 'Space Mute'}
|
||||
</button>
|
||||
)}
|
||||
{canDeafenMembers && (
|
||||
<button onClick={handleServerDeafen} className={btnClass} style={btnStyle}>
|
||||
<button onClick={handleSpaceDeafen} className={btnClass} style={btnStyle}>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" className="flex-shrink-0">
|
||||
<path d="M12 3c-4.97 0-9 4.03-9 9v7c0 1.1.9 2 2 2h2v-7H5v-2c0-3.87 3.13-7 7-7s7 3.13 7 7v2h-2v7h2c1.1 0 2-.9 2-2v-7c0-4.97-4.03-9-9-9z" />
|
||||
{isServerDeafened && (
|
||||
{isSpaceDeafened && (
|
||||
<line x1="3" y1="3" x2="21" y2="21" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" />
|
||||
)}
|
||||
</svg>
|
||||
{isServerDeafened ? 'Server Undeafen' : 'Server Deafen'}
|
||||
{isSpaceDeafened ? 'Space Undeafen' : 'Space Deafen'}
|
||||
</button>
|
||||
)}
|
||||
{canDisconnectMembers && (
|
||||
|
||||
@@ -151,8 +151,8 @@ export function useLiveKit() {
|
||||
const isScreenSharing = useVoiceStore((s) => s.isScreenSharing);
|
||||
const screenShareConfig = useVoiceStore((s) => s.screenShareConfig);
|
||||
const voiceUserStates = useVoiceStore((s) => s.voiceUserStates);
|
||||
const serverMutedUserIds = useVoiceStore((s) => s.serverMutedUserIds);
|
||||
const serverDeafenedUserIds = useVoiceStore((s) => s.serverDeafenedUserIds);
|
||||
const spaceMutedUserIds = useVoiceStore((s) => s.spaceMutedUserIds);
|
||||
const spaceDeafenedUserIds = useVoiceStore((s) => s.spaceDeafenedUserIds);
|
||||
const permissionMutedUserIds = useVoiceStore((s) => s.permissionMutedUserIds);
|
||||
const inputVolume = useVoiceStore((s) => s.inputVolume);
|
||||
const inputDeviceId = useVoiceStore((s) => s.inputDeviceId);
|
||||
@@ -213,8 +213,8 @@ export function useLiveKit() {
|
||||
const localMyId = cvId ? getMyUserIdForOrigin(localOrigin) : undefined;
|
||||
const localSpaceId = cvId ? useSpaceStore.getState().channelToSpaceMap.get(cvId) : null;
|
||||
const localKey = (localSpaceId && localMyId) ? `${localSpaceId}:${localMyId}` : '';
|
||||
isPartMuted = vs.isMuted || vs.serverMutedUserIds.has(localKey) || vs.permissionMutedUserIds.has(localKey);
|
||||
isPartDeafened = vs.isDeafened || vs.serverDeafenedUserIds.has(localKey);
|
||||
isPartMuted = vs.isMuted || vs.spaceMutedUserIds.has(localKey) || vs.permissionMutedUserIds.has(localKey);
|
||||
isPartDeafened = vs.isDeafened || vs.spaceDeafenedUserIds.has(localKey);
|
||||
} else {
|
||||
isPartDeafened = userState?.isDeafened ?? useVoiceStore.getState().deafenedUserIds.has(userId);
|
||||
if (userState) isPartMuted = userState.isMuted;
|
||||
@@ -271,8 +271,8 @@ export function useLiveKit() {
|
||||
const effMyId = cvId ? getMyUserIdForOrigin(effOrigin) : undefined;
|
||||
const effSpaceId = cvId ? useSpaceStore.getState().channelToSpaceMap.get(cvId) : null;
|
||||
const effKey = (effSpaceId && effMyId) ? `${effSpaceId}:${effMyId}` : '';
|
||||
const effectiveMuted = isMuted || serverMutedUserIds.has(effKey) || permissionMutedUserIds.has(effKey);
|
||||
const effectiveDeafened = isDeafened || serverDeafenedUserIds.has(effKey);
|
||||
const effectiveMuted = isMuted || spaceMutedUserIds.has(effKey) || permissionMutedUserIds.has(effKey);
|
||||
const effectiveDeafened = isDeafened || spaceDeafenedUserIds.has(effKey);
|
||||
|
||||
const syncMic = async () => {
|
||||
try {
|
||||
@@ -338,7 +338,7 @@ export function useLiveKit() {
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, [isMuted, isDeafened, serverMutedUserIds, serverDeafenedUserIds, permissionMutedUserIds, inputDeviceId, inputVolume, isConnected, echoCancellation, noiseSuppression, autoGainControl, rnnoiseEnabled]);
|
||||
}, [isMuted, isDeafened, spaceMutedUserIds, spaceDeafenedUserIds, permissionMutedUserIds, inputDeviceId, inputVolume, isConnected, echoCancellation, noiseSuppression, autoGainControl, rnnoiseEnabled]);
|
||||
|
||||
const connect = useCallback(async (channelId: string, isDm?: boolean) => {
|
||||
const storedId = isDm ? `dm-${channelId}` : channelId;
|
||||
@@ -403,7 +403,7 @@ export function useLiveKit() {
|
||||
const connMyId = cvIdConn ? getMyUserIdForOrigin(connOrigin) : undefined;
|
||||
const connSpaceId = cvIdConn ? useSpaceStore.getState().channelToSpaceMap.get(cvIdConn) : null;
|
||||
const connKey = (connSpaceId && connMyId) ? `${connSpaceId}:${connMyId}` : '';
|
||||
const effDeaf = vsConn.isDeafened || vsConn.serverDeafenedUserIds.has(connKey);
|
||||
const effDeaf = vsConn.isDeafened || vsConn.spaceDeafenedUserIds.has(connKey);
|
||||
if (effDeaf) {
|
||||
const encoder = new TextEncoder();
|
||||
newRoom.localParticipant.publishData(
|
||||
@@ -595,7 +595,7 @@ export function useLiveKit() {
|
||||
|
||||
useEffect(() => {
|
||||
updateParticipants();
|
||||
}, [voiceUserStates, isMuted, isDeafened, serverMutedUserIds, serverDeafenedUserIds, permissionMutedUserIds, updateParticipants]);
|
||||
}, [voiceUserStates, isMuted, isDeafened, spaceMutedUserIds, spaceDeafenedUserIds, permissionMutedUserIds, updateParticipants]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!room) return;
|
||||
|
||||
@@ -182,34 +182,34 @@ function handleEvent(origin: string, event: ServerEvent): void {
|
||||
}
|
||||
}
|
||||
|
||||
const nextServerMuted = new Set(vsState.serverMutedUserIds);
|
||||
const nextServerDeafened = new Set(vsState.serverDeafenedUserIds);
|
||||
const nextSpaceMuted = new Set(vsState.spaceMutedUserIds);
|
||||
const nextSpaceDeafened = new Set(vsState.spaceDeafenedUserIds);
|
||||
const nextPermissionMuted = new Set(vsState.permissionMutedUserIds);
|
||||
|
||||
// Clear existing restrictions that belong to spaces on THIS origin
|
||||
// (If a space was deleted while offline, its orphaned restrictions remain, which is harmless)
|
||||
for (const key of nextServerMuted) {
|
||||
for (const key of nextSpaceMuted) {
|
||||
const spaceId = key.split(':')[0];
|
||||
if (spaceId && originSpaceIds.has(spaceId)) nextServerMuted.delete(key);
|
||||
if (spaceId && originSpaceIds.has(spaceId)) nextSpaceMuted.delete(key);
|
||||
}
|
||||
for (const key of nextServerDeafened) {
|
||||
for (const key of nextSpaceDeafened) {
|
||||
const spaceId = key.split(':')[0];
|
||||
if (spaceId && originSpaceIds.has(spaceId)) nextServerDeafened.delete(key);
|
||||
if (spaceId && originSpaceIds.has(spaceId)) nextSpaceDeafened.delete(key);
|
||||
}
|
||||
for (const key of nextPermissionMuted) {
|
||||
const spaceId = key.split(':')[0];
|
||||
if (spaceId && originSpaceIds.has(spaceId)) nextPermissionMuted.delete(key);
|
||||
}
|
||||
|
||||
if (event.serverVoiceStates) {
|
||||
for (const [uid, state] of Object.entries(event.serverVoiceStates as Record<string, { serverMuted: boolean; serverDeafened: boolean; permissionMuted?: boolean }>)) {
|
||||
if (state.serverMuted) nextServerMuted.add(uid);
|
||||
if (state.serverDeafened) nextServerDeafened.add(uid);
|
||||
if (event.spaceVoiceStates) {
|
||||
for (const [uid, state] of Object.entries(event.spaceVoiceStates as Record<string, { spaceMuted: boolean; spaceDeafened: boolean; permissionMuted?: boolean }>)) {
|
||||
if (state.spaceMuted) nextSpaceMuted.add(uid);
|
||||
if (state.spaceDeafened) nextSpaceDeafened.add(uid);
|
||||
if (state.permissionMuted) nextPermissionMuted.add(uid);
|
||||
}
|
||||
}
|
||||
// Single atomic update
|
||||
useVoiceStore.setState({ serverMutedUserIds: nextServerMuted, serverDeafenedUserIds: nextServerDeafened, permissionMutedUserIds: nextPermissionMuted });
|
||||
useVoiceStore.setState({ spaceMutedUserIds: nextSpaceMuted, spaceDeafenedUserIds: nextSpaceDeafened, permissionMutedUserIds: nextPermissionMuted });
|
||||
|
||||
// With decoupled state, user intent is never force-set by the server.
|
||||
// Effective state (intent || serverEnforcement) is computed reactively
|
||||
@@ -335,9 +335,9 @@ function handleEvent(origin: string, event: ServerEvent): void {
|
||||
setVoiceUserStatus(event.userId, event.isMuted, event.isDeafened, event.isCameraOn, event.isScreenSharing);
|
||||
break;
|
||||
|
||||
case 'voice_server_muted': {
|
||||
const { setServerMutedUser } = useVoiceStore.getState();
|
||||
setServerMutedUser(event.spaceId, event.userId, event.muted);
|
||||
case 'voice_space_muted': {
|
||||
const { setSpaceMutedUser } = useVoiceStore.getState();
|
||||
setSpaceMutedUser(event.spaceId, event.userId, event.muted);
|
||||
// Broadcast effective state if this targets the current user
|
||||
const myMuteId = isHome ? useAuthStore.getState().user?.id : getMyUserIdForOrigin(origin);
|
||||
if (event.userId === myMuteId) broadcastVoiceStatus();
|
||||
@@ -352,9 +352,9 @@ function handleEvent(origin: string, event: ServerEvent): void {
|
||||
break;
|
||||
}
|
||||
|
||||
case 'voice_server_deafened': {
|
||||
const { setServerDeafenedUser } = useVoiceStore.getState();
|
||||
setServerDeafenedUser(event.spaceId, event.userId, event.deafened);
|
||||
case 'voice_space_deafened': {
|
||||
const { setSpaceDeafenedUser } = useVoiceStore.getState();
|
||||
setSpaceDeafenedUser(event.spaceId, event.userId, event.deafened);
|
||||
// Broadcast effective state if this targets the current user
|
||||
const myDeafenId = isHome ? useAuthStore.getState().user?.id : getMyUserIdForOrigin(origin);
|
||||
if (event.userId === myDeafenId) {
|
||||
|
||||
@@ -93,12 +93,12 @@ interface VoiceState {
|
||||
voiceUserStates: Map<string, { isMuted: boolean; isDeafened: boolean; isCameraOn: boolean; isScreenSharing: boolean }>;
|
||||
setVoiceUserStatus: (userId: string, isMuted: boolean, isDeafened: boolean, isCameraOn: boolean, isScreenSharing: boolean) => void;
|
||||
clearVoiceUserStatus: (userId: string) => void;
|
||||
// Server mute/deafen state (moderator action)
|
||||
serverMutedUserIds: Set<string>; // Stores "spaceId:userId"
|
||||
serverDeafenedUserIds: Set<string>; // Stores "spaceId:userId"
|
||||
setServerMutedUser: (spaceId: string, userId: string, muted: boolean) => void;
|
||||
setServerDeafenedUser: (spaceId: string, userId: string, deafened: boolean) => void;
|
||||
clearServerVoiceStates: () => void;
|
||||
// Space mute/deafen state (moderator action)
|
||||
spaceMutedUserIds: Set<string>; // Stores "spaceId:userId"
|
||||
spaceDeafenedUserIds: Set<string>; // Stores "spaceId:userId"
|
||||
setSpaceMutedUser: (spaceId: string, userId: string, muted: boolean) => void;
|
||||
setSpaceDeafenedUser: (spaceId: string, userId: string, deafened: boolean) => void;
|
||||
clearSpaceVoiceStates: () => void;
|
||||
// Permission mute state (SPEAK permission revoked while in voice)
|
||||
permissionMutedUserIds: Set<string>; // Stores "spaceId:userId"
|
||||
setPermissionMutedUser: (spaceId: string, userId: string, muted: boolean) => void;
|
||||
@@ -332,25 +332,25 @@ export const useVoiceStore = create<VoiceState>()(
|
||||
});
|
||||
},
|
||||
|
||||
serverMutedUserIds: new Set(),
|
||||
serverDeafenedUserIds: new Set(),
|
||||
setServerMutedUser: (spaceId, userId, muted) => {
|
||||
spaceMutedUserIds: new Set(),
|
||||
spaceDeafenedUserIds: new Set(),
|
||||
setSpaceMutedUser: (spaceId, userId, muted) => {
|
||||
set((state) => {
|
||||
const newSet = new Set(state.serverMutedUserIds);
|
||||
const newSet = new Set(state.spaceMutedUserIds);
|
||||
const key = `${spaceId}:${userId}`;
|
||||
if (muted) newSet.add(key); else newSet.delete(key);
|
||||
return { serverMutedUserIds: newSet };
|
||||
return { spaceMutedUserIds: newSet };
|
||||
});
|
||||
},
|
||||
setServerDeafenedUser: (spaceId, userId, deafened) => {
|
||||
setSpaceDeafenedUser: (spaceId, userId, deafened) => {
|
||||
set((state) => {
|
||||
const newSet = new Set(state.serverDeafenedUserIds);
|
||||
const newSet = new Set(state.spaceDeafenedUserIds);
|
||||
const key = `${spaceId}:${userId}`;
|
||||
if (deafened) newSet.add(key); else newSet.delete(key);
|
||||
return { serverDeafenedUserIds: newSet };
|
||||
return { spaceDeafenedUserIds: newSet };
|
||||
});
|
||||
},
|
||||
clearServerVoiceStates: () => set({ serverMutedUserIds: new Set(), serverDeafenedUserIds: new Set(), permissionMutedUserIds: new Set() }),
|
||||
clearSpaceVoiceStates: () => set({ spaceMutedUserIds: new Set(), spaceDeafenedUserIds: new Set(), permissionMutedUserIds: new Set() }),
|
||||
|
||||
permissionMutedUserIds: new Set(),
|
||||
setPermissionMutedUser: (spaceId, userId, muted) => {
|
||||
@@ -392,9 +392,9 @@ export const useVoiceStore = create<VoiceState>()(
|
||||
streamMutes: new Map(),
|
||||
watchingStreams: new Set(),
|
||||
unwatchedCameras: new Set(),
|
||||
// Server-enforced restrictions
|
||||
serverMutedUserIds: new Set(),
|
||||
serverDeafenedUserIds: new Set(),
|
||||
// Space-enforced restrictions
|
||||
spaceMutedUserIds: new Set(),
|
||||
spaceDeafenedUserIds: new Set(),
|
||||
permissionMutedUserIds: new Set(),
|
||||
}),
|
||||
|
||||
@@ -502,8 +502,8 @@ export const useVoiceStore = create<VoiceState>()(
|
||||
streamMutes: new Map(),
|
||||
watchingStreams: new Set(),
|
||||
unwatchedCameras: new Set(),
|
||||
serverMutedUserIds: new Set(),
|
||||
serverDeafenedUserIds: new Set(),
|
||||
spaceMutedUserIds: new Set(),
|
||||
spaceDeafenedUserIds: new Set(),
|
||||
permissionMutedUserIds: new Set(),
|
||||
}),
|
||||
}),
|
||||
@@ -566,8 +566,8 @@ export const useVoiceStore = create<VoiceState>()(
|
||||
merge: (persistedState: any, currentState: VoiceState) => {
|
||||
const merged = { ...currentState, ...persistedState };
|
||||
// Reconstruct non-persisted Sets/Maps to their defaults
|
||||
merged.serverMutedUserIds = currentState.serverMutedUserIds;
|
||||
merged.serverDeafenedUserIds = currentState.serverDeafenedUserIds;
|
||||
merged.spaceMutedUserIds = currentState.spaceMutedUserIds;
|
||||
merged.spaceDeafenedUserIds = currentState.spaceDeafenedUserIds;
|
||||
merged.permissionMutedUserIds = currentState.permissionMutedUserIds;
|
||||
merged.voiceUsers = currentState.voiceUsers;
|
||||
merged.participants = currentState.participants;
|
||||
|
||||
@@ -15,16 +15,16 @@ import { wsSend } from '../hooks/useWebSocket';
|
||||
*/
|
||||
export function broadcastVoiceStatus(overrideOrigin?: string): void {
|
||||
const vs = useVoiceStore.getState();
|
||||
const { isMuted, isDeafened, isCameraOn, isScreenSharing, currentVoiceChannelId, serverMutedUserIds, serverDeafenedUserIds } = vs;
|
||||
const { isMuted, isDeafened, isCameraOn, isScreenSharing, currentVoiceChannelId, spaceMutedUserIds, spaceDeafenedUserIds } = vs;
|
||||
if (!currentVoiceChannelId) return;
|
||||
|
||||
const origin = overrideOrigin ?? getChannelOrigin(currentVoiceChannelId);
|
||||
const myId = getMyUserIdForOrigin(origin);
|
||||
const spaceId = useSpaceStore.getState().channelToSpaceMap.get(currentVoiceChannelId);
|
||||
const serverKey = (spaceId && myId) ? `${spaceId}:${myId}` : '';
|
||||
const spaceKey = (spaceId && myId) ? `${spaceId}:${myId}` : '';
|
||||
|
||||
const effectiveMuted = isMuted || serverMutedUserIds.has(serverKey);
|
||||
const effectiveDeafened = isDeafened || serverDeafenedUserIds.has(serverKey);
|
||||
const effectiveMuted = isMuted || spaceMutedUserIds.has(spaceKey);
|
||||
const effectiveDeafened = isDeafened || spaceDeafenedUserIds.has(spaceKey);
|
||||
|
||||
wsSend({ type: 'voice_status', isMuted: effectiveMuted, isDeafened: effectiveDeafened, isCameraOn, isScreenSharing }, origin);
|
||||
}
|
||||
@@ -35,14 +35,14 @@ export function broadcastVoiceStatus(overrideOrigin?: string): void {
|
||||
*/
|
||||
export function broadcastDeafenViaLiveKit(): void {
|
||||
const vs = useVoiceStore.getState();
|
||||
const { isDeafened, currentVoiceChannelId, serverDeafenedUserIds } = vs;
|
||||
const { isDeafened, currentVoiceChannelId, spaceDeafenedUserIds } = vs;
|
||||
if (!currentVoiceChannelId) return;
|
||||
|
||||
const origin = getChannelOrigin(currentVoiceChannelId);
|
||||
const myId = getMyUserIdForOrigin(origin);
|
||||
const spaceId = useSpaceStore.getState().channelToSpaceMap.get(currentVoiceChannelId);
|
||||
const serverKey = (spaceId && myId) ? `${spaceId}:${myId}` : '';
|
||||
const effectiveDeafened = isDeafened || serverDeafenedUserIds.has(serverKey);
|
||||
const spaceKey = (spaceId && myId) ? `${spaceId}:${myId}` : '';
|
||||
const effectiveDeafened = isDeafened || spaceDeafenedUserIds.has(spaceKey);
|
||||
|
||||
import('../hooks/useLiveKit').then(({ getActiveRoom }) => {
|
||||
const room = getActiveRoom();
|
||||
|
||||
Reference in New Issue
Block a user