fix: notify displaced tab when user joins voice from another session

When a user joins a voice channel while already connected from another
tab, the server now sends a 'displaced' reason on voice_disconnected.
The client tears down LiveKit and shows an informational toast.
This commit is contained in:
Jannis Braun
2026-03-20 19:03:35 +01:00
parent 68378c7d83
commit 0dc3eb1884
3 changed files with 19 additions and 2 deletions
+8
View File
@@ -537,6 +537,14 @@ function handleVoiceJoin(event: Record<string, unknown>, userId: string): void {
const left = connectionManager.leaveCurrentRoom(userId);
if (left) {
broadcastRoomLeave(left.roomId, left.room, userId);
// Notify all of the user's tabs so the displaced tab tears down LiveKit
connectionManager.sendToUser(userId, {
type: 'voice_disconnected',
userId,
channelId: left.roomId,
reason: 'displaced',
});
}
// Cancel any ringing DM rooms where this user is the caller
+1 -1
View File
@@ -330,7 +330,7 @@ export type ServerEvent =
| { type: 'voice_space_deafened'; userId: string; channelId: string; spaceId: string; deafened: boolean }
| { type: 'voice_permission_muted'; userId: string; spaceId: string; muted: boolean }
| { type: 'voice_moved'; userId: string; oldChannelId: string; newChannelId: string }
| { type: 'voice_disconnected'; userId: string; channelId: string }
| { type: 'voice_disconnected'; userId: string; channelId: string; reason?: 'displaced' }
| { type: 'user_updated'; user: User }
| { type: 'member_banned'; spaceId: string; reason: string | null }
| { type: 'category_created'; category: ChannelCategory; spaceId: string }
+9
View File
@@ -9,6 +9,8 @@ import type { ServerEvent, ClientEvent, ActiveCallInfo } from '@backspace/shared
import { resolveAssetUrl, normalizeUserAssets, normalizeMessageAssets } from '../utils/assetUrls';
import { broadcastVoiceStatus, broadcastDeafenViaLiveKit } from '../utils/voice';
import { registerSelfId } from '../utils/identity';
import { getActiveRoom } from './useLiveKit';
import { useUIStore } from '../stores/uiStore';
// ─── Connection state ─────────────────────────────────────────────────────────
@@ -428,7 +430,14 @@ function handleEvent(origin: string, event: ServerEvent): void {
case 'voice_disconnected': {
const myDisconnectId = isHome ? useAuthStore.getState().user?.id : getMyUserIdForOrigin(origin);
if (event.userId === myDisconnectId) {
const { currentVoiceChannelId } = useVoiceStore.getState();
if (event.channelId === currentVoiceChannelId) {
useVoiceStore.getState().handleForceDisconnect();
getActiveRoom()?.disconnect();
if (event.reason === 'displaced') {
useUIStore.getState().addToast('Voice disconnected — joined from another session', 'info');
}
}
}
break;
}