feat(voice): голосовой интерфейс на LiveKit (Фаза 3, клиент)
- `livekit-client` подключён как ленивый чанк: SDK грузится только при входе в комнату, стартовый бандл не вырос; - `src/lib/livekit.ts` — единственное место знания о SDK: подключение, дорожки микрофона/камеры/экрана, снимки участников (речь, флаги, качество связи), выбор устройств на лету, разбор exp токена; - `src/stores/voiceConnection.ts` — сессия голоса: статусы idle/connecting/connected/reconnecting/error, продление токена за минуту до истечения с переподключением и повторным применением флагов, серверный мьют и глушение, громкость участников, перемещение, подсказка «говорите с выключенным микрофоном» не чаще раза в 5 с; - UI: состав голосовой комнаты в сайдбаре (речь, флаги, качество), панель комнаты с плитками и модерацией, меню участника (личная громкость, мьют, перемещение), drag&drop между голосовыми комнатами, кнопки панели пользователя управляют реальной сессией, при выключенном голосе — понятная подсказка; - 368 тестов Vitest (было 330), check/lint/test/build зелёные.
This commit is contained in:
@@ -366,9 +366,10 @@ describe('голосовая сессия: синхронизация с сер
|
||||
s: 1,
|
||||
d: makeVoiceState({ user_id: 'user-1', channel_id: 'c-other' }),
|
||||
});
|
||||
await flush(24);
|
||||
await vi.waitFor(() => {
|
||||
expect(useVoiceConnectionStore.getState().channelId).toBe('c-other');
|
||||
});
|
||||
|
||||
expect(useVoiceConnectionStore.getState().channelId).toBe('c-other');
|
||||
// Старая комната закрыта, для новой создана своя сессия SDK.
|
||||
expect(first.disconnectCalls).toBe(1);
|
||||
expect(FakeRoom.instances).toHaveLength(2);
|
||||
@@ -382,9 +383,10 @@ describe('голосовая сессия: синхронизация с сер
|
||||
const fetchMock = await joinRoom();
|
||||
|
||||
FakeRoom.latest().emit(RoomEvent.Disconnected);
|
||||
await flush(24);
|
||||
await vi.waitFor(() => {
|
||||
expect(useVoiceConnectionStore.getState().status).toBe('idle');
|
||||
});
|
||||
|
||||
expect(useVoiceConnectionStore.getState().status).toBe('idle');
|
||||
expect(useVoiceConnectionStore.getState().participants).toEqual([]);
|
||||
expect(lastRequest(fetchMock, { url: '/voice/leave', method: 'POST' })).toBeDefined();
|
||||
});
|
||||
@@ -456,6 +458,8 @@ describe('голосовая сессия: продление токена', ()
|
||||
expect(state.status).toBe('error');
|
||||
expect(state.error).toEqual({ code: VOICE_SESSION_EXPIRED_CODE });
|
||||
expect(state.participants).toEqual([]);
|
||||
// Комната остаётся в состоянии ошибки: подсказка видна в интерфейсе.
|
||||
expect(state.channelId).toBe('c-voice');
|
||||
expect(lastRequest(fetchMock, { url: '/voice/leave', method: 'POST' })).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user