4.6 KiB
Voice Session Domain
Tracks voice session metadata across client-side navigation and manages the voice workspace UI state (expanded, minimized, hidden). This domain does not touch WebRTC directly; actual connections live in voice-connection and infrastructure/realtime.
Module map
voice-session/
├── application/
│ ├── voice-session.facade.ts Tracks active voice session, drives floating controls
│ └── voice-workspace.service.ts Workspace mode (hidden/expanded/minimized), focused stream, mini-window position
│
├── domain/
│ ├── voice-session.logic.ts isViewingVoiceSessionServer, buildVoiceSessionRoom
│ └── voice-session.models.ts VoiceSessionInfo interface
│
├── infrastructure/
│ └── voice-settings.storage.ts Persists audio device IDs, volumes, bitrate, latency, noise reduction to localStorage
│
├── feature/
│ ├── voice-controls/ Full voice control panel (mic, deafen, devices, screen share, settings)
│ └── floating-voice-controls/ Minimal overlay when user navigates away from the voice server
│
└── index.ts Barrel exports
How the pieces connect
The facade manages session bookkeeping. The workspace service owns view state. Settings storage provides persistence for user preferences. Neither service opens any WebRTC connections.
graph TD
VSF[VoiceSessionFacade]
VWS[VoiceWorkspaceService]
VSS[voiceSettingsStorage]
Logic[voice-session.logic]
VC[VoiceControlsComponent]
FC[FloatingVoiceControlsComponent]
Store[NgRx Store]
VC --> VSF
VC --> VWS
VC --> VSS
FC --> VSF
FC --> VWS
VSF --> Logic
VSF --> Store
VWS --> VSF
click VSF "application/voice-session.facade.ts" "Tracks active voice session" _blank
click VWS "application/voice-workspace.service.ts" "Workspace mode and focused stream" _blank
click VSS "infrastructure/voice-settings.storage.ts" "localStorage persistence for audio settings" _blank
click Logic "domain/voice-session.logic.ts" "Pure helper functions" _blank
click VC "feature/voice-controls/" "Full voice control panel" _blank
click FC "feature/floating-voice-controls/" "Minimal floating overlay" _blank
Session lifecycle
stateDiagram-v2
[*] --> NoSession
NoSession --> Active: startSession(info)
Active --> Active: checkCurrentRoute(serverId)
Active --> NoSession: endSession()
state Active {
[*] --> ViewingServer
ViewingServer --> AwayFromServer: navigated to different server
AwayFromServer --> ViewingServer: navigated back / navigateToVoiceServer()
}
When a voice session is active and the user navigates away from the voice-connected server, showFloatingControls becomes true and the floating overlay appears. Clicking the overlay dispatches RoomsActions.viewServer to navigate back.
Workspace modes
VoiceWorkspaceService controls the voice workspace panel state. The workspace is only visible when the user is viewing the voice-connected server.
stateDiagram-v2
[*] --> Hidden
Hidden --> Expanded: open()
Expanded --> Minimized: minimize()
Expanded --> Hidden: close() / showChat()
Minimized --> Expanded: restore()
Minimized --> Hidden: close()
Expanded --> Hidden: voice session ends
Minimized --> Hidden: voice session ends
The minimized mode renders a draggable mini-window. Its position is tracked in miniWindowPosition and clamped to viewport bounds on resize. focusedStreamId controls which screen-share stream gets the widescreen treatment in expanded mode.
Voice settings
Settings are stored in localStorage under a single JSON key. All values are validated and clamped on load to defend against corrupt storage.
| Setting | Default | Range |
|---|---|---|
| inputDevice | "" |
device ID string |
| outputDevice | "" |
device ID string |
| inputVolume | 100 | 0 -- 100 |
| outputVolume | 100 | 0 -- 100 |
| audioBitrate | 96 kbps | 32 -- 256 |
| latencyProfile | "balanced" |
low / balanced / high |
| noiseReduction | true |
boolean |
| screenShareQuality | "balanced" |
low / balanced / high |
| askScreenShareQuality | true |
boolean |
| includeSystemAudio | false |
boolean |
loadVoiceSettingsFromStorage() and saveVoiceSettingsToStorage(patch) are the only entry points. The save function merges the patch with the current stored value so callers only need to pass changed fields.