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:
@@ -52,6 +52,7 @@ import {
|
||||
MessageLayout,
|
||||
MessageSpacing,
|
||||
NoiseSuppressionMode,
|
||||
RingtoneId,
|
||||
Settings,
|
||||
settingsAtom,
|
||||
} from '../../../state/settings';
|
||||
@@ -78,6 +79,7 @@ import { SequenceCardStyle } from '../styles.css';
|
||||
import { useTauriUpdater } from '../../../hooks/useTauriUpdater';
|
||||
import { useDateFormatItems } from '../../../hooks/useDateFormat';
|
||||
import { playCallJoinSound } from '../../../utils/callSounds';
|
||||
import { previewRingtone, RINGTONE_OPTIONS } from '../../../utils/ringtones';
|
||||
import { DenoiseTester } from './DenoiseTester';
|
||||
|
||||
type ThemeSelectorProps = {
|
||||
@@ -1242,12 +1244,18 @@ function Calls() {
|
||||
'callJoinLeaveSound',
|
||||
);
|
||||
const [ringtoneVolume, setRingtoneVolume] = useSetting(settingsAtom, 'ringtoneVolume');
|
||||
const [ringtoneId, setRingtoneId] = useSetting(settingsAtom, 'ringtoneId');
|
||||
|
||||
const handleJoinLeaveSoundChange = (value: 'off' | 'chime' | 'soft' | 'retro') => {
|
||||
setCallJoinLeaveSound(value);
|
||||
if (value !== 'off') playCallJoinSound(value);
|
||||
};
|
||||
|
||||
const handleRingtoneChange = (value: RingtoneId) => {
|
||||
setRingtoneId(value);
|
||||
previewRingtone(value, Math.max(0, Math.min(1, ringtoneVolume / 100)));
|
||||
};
|
||||
|
||||
const pttBind = useKeyBind(setPttKey);
|
||||
const deafenBind = useKeyBind(setDeafenKey);
|
||||
|
||||
@@ -1573,6 +1581,19 @@ function Calls() {
|
||||
/>
|
||||
)}
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Ringtone"
|
||||
description="Sound played for incoming calls. Selecting an option plays a preview."
|
||||
after={
|
||||
<SettingsSelect
|
||||
value={ringtoneId}
|
||||
onChange={(v) => handleRingtoneChange(v as RingtoneId)}
|
||||
options={RINGTONE_OPTIONS}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Ringtone Volume"
|
||||
|
||||
Reference in New Issue
Block a user