feat: audio devices + fullscreen + banner cleanup (v0.7.0)
Audio device selection: - audioSettings: persisted inputDeviceId + outputDeviceId - CallContext: uses stored input deviceId on mic enable, new setAudioInputDevice / setAudioOutputDevice actions that hot-swap without reconnect. Output swap applies HTMLMediaElement.setSinkId to every attached remote-audio element (LiveKit's switchActiveDevice only tracks elements it attached itself) - SettingsPage: new "Mikrofon" + "Ausgabegerät" selects with enumerateDevices, devicechange listener, permission-probe button. setSinkId-unsupported fallback is messaged but non-blocking Fullscreen: - FullscreenCall was absolute inset-0 z-40 which trapped it inside the <main> pane — sidebar + chat-list stayed visible. Switched to fixed inset-0 z-[60] so the call overlays the whole window Discord-style - ScreenShareViewer fullscreen: CSS-only toggle (native Fullscreen API unreliable under Tauri WKWebView), portalled to document.body when active so no ancestor stacking context can clip it. Esc exits ActiveCallBanner: - cleanup effect returned early when presence was entirely empty, leaving the "1 im Raum" fallback stuck after both peers left. Now schedules dismissLastCall as soon as othersIn.length === 0, with a 3s grace window to absorb presence re-sync flicker Bump tauri version 0.6.0 -> 0.7.0
This commit is contained in:
@@ -9,10 +9,19 @@ export type AudioQuality = 'voice' | 'hifi';
|
||||
|
||||
export interface AudioSettings {
|
||||
quality: AudioQuality;
|
||||
// Preferred input deviceId from enumerateDevices. null = use browser default
|
||||
// (whatever the OS points at). Persisted across sessions, so "grandma's
|
||||
// mic is default" stays even after browser picks the wrong device.
|
||||
inputDeviceId: string | null;
|
||||
// Preferred output (speaker/headphone) deviceId. null = system default.
|
||||
// Applied to every <audio> element we attach via HTMLMediaElement.setSinkId.
|
||||
outputDeviceId: string | null;
|
||||
}
|
||||
|
||||
const DEFAULTS: AudioSettings = {
|
||||
quality: 'voice',
|
||||
inputDeviceId: null,
|
||||
outputDeviceId: null,
|
||||
};
|
||||
|
||||
export interface AudioQualityParams {
|
||||
@@ -73,6 +82,14 @@ function read(): AudioSettings {
|
||||
const parsed = JSON.parse(raw) as Partial<AudioSettings>;
|
||||
cached = {
|
||||
quality: isQuality(parsed.quality) ? parsed.quality : DEFAULTS.quality,
|
||||
inputDeviceId:
|
||||
typeof parsed.inputDeviceId === 'string' && parsed.inputDeviceId.length > 0
|
||||
? parsed.inputDeviceId
|
||||
: DEFAULTS.inputDeviceId,
|
||||
outputDeviceId:
|
||||
typeof parsed.outputDeviceId === 'string' && parsed.outputDeviceId.length > 0
|
||||
? parsed.outputDeviceId
|
||||
: DEFAULTS.outputDeviceId,
|
||||
};
|
||||
return cached;
|
||||
} catch {
|
||||
|
||||
Reference in New Issue
Block a user