feat(calls): selectable incoming-call ringtone (#4)
Adds a ringtoneId setting (classic | chime | soft | retro | none) so the incoming-call ring is no longer hardcoded to call.ogg. The three synth styles are generated in-browser via a new utils/ringtones.ts module (mirroring the existing callSounds.ts WebAudio pattern), so no new binary assets are bundled; 'classic' keeps the existing call.ogg clip and 'none' is a silent, visual-only incoming-call UI. - ringtones.ts: startRingtone() loops until stopped; previewRingtone() plays a single non-looping preview and auto-cancels the prior preview. - IncomingCall: ring driven by the setting; <audio> element removed. - Settings > Calls: Ringtone selector with on-select preview, beside the existing Ringtone Volume slider. - settings.ts: persisted value whitelisted back to a known id. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,10 @@ export type NoiseSuppressionMode = 'off' | 'browser' | 'ml';
|
||||
// CDN. Its wasm is single-threaded, so no COOP/COEP cross-origin isolation is
|
||||
// required (see LOTUS_DENOISE_ENGINEERING_REVIEW.md).
|
||||
export type DenoiseModelId = 'rnnoise' | 'speex' | 'dtln' | 'deepfilternet';
|
||||
// Incoming-call ringtone. 'classic' is the bundled call.ogg clip; 'chime' /
|
||||
// 'soft' / 'retro' are synthesized in-browser (see utils/ringtones.ts);
|
||||
// 'none' is silent (visual-only incoming-call UI).
|
||||
export type RingtoneId = 'classic' | 'chime' | 'soft' | 'retro' | 'none';
|
||||
export type ChatBackground =
|
||||
| 'none'
|
||||
| 'blueprint'
|
||||
@@ -148,6 +152,7 @@ export interface Settings {
|
||||
afkTimeoutMinutes: number;
|
||||
|
||||
callJoinLeaveSound: 'off' | 'chime' | 'soft' | 'retro';
|
||||
ringtoneId: RingtoneId;
|
||||
ringtoneVolume: number; // 0–100
|
||||
|
||||
seasonalThemeOverride:
|
||||
@@ -243,6 +248,7 @@ const defaultSettings: Settings = {
|
||||
afkTimeoutMinutes: 10,
|
||||
|
||||
callJoinLeaveSound: 'chime',
|
||||
ringtoneId: 'classic',
|
||||
ringtoneVolume: 70,
|
||||
|
||||
seasonalThemeOverride: 'auto',
|
||||
@@ -273,6 +279,15 @@ export const getSettings = (): Settings => {
|
||||
saved.callDenoiseModel === 'deepfilternet'
|
||||
? saved.callDenoiseModel
|
||||
: defaultSettings.callDenoiseModel,
|
||||
// Coerce any unknown persisted ringtone id back to the default.
|
||||
ringtoneId:
|
||||
saved.ringtoneId === 'classic' ||
|
||||
saved.ringtoneId === 'chime' ||
|
||||
saved.ringtoneId === 'soft' ||
|
||||
saved.ringtoneId === 'retro' ||
|
||||
saved.ringtoneId === 'none'
|
||||
? saved.ringtoneId
|
||||
: defaultSettings.ringtoneId,
|
||||
composerToolbarButtons: {
|
||||
...DEFAULT_COMPOSER_TOOLBAR,
|
||||
...(saved.composerToolbarButtons ?? {}),
|
||||
|
||||
Reference in New Issue
Block a user