fix(settings): validate seasonalThemeOverride; commit page zoom on blur; GIF picker opt-in
- seasonalThemeOverride is coerced to 'auto' when the persisted value is outside the union, like every other enum setting (tested). - PageZoomInput commits on blur as well as Enter. - New gifPickerEnabled setting (default off) with a disclosure that GIF searches go to Giphy; the picker doesn't mount and the composer button is hidden while off. Fixes #74 Fixes #75 Fixes #68 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -56,6 +56,18 @@ test('coerces unknown persisted denoise model / ringtone id back to defaults', (
|
||||
assert.equal(valid.ringtoneId, 'chime');
|
||||
});
|
||||
|
||||
test('coerces unknown persisted seasonalThemeOverride back to auto', () => {
|
||||
setStored(null);
|
||||
const defaults = getSettings();
|
||||
assert.equal(defaults.seasonalThemeOverride, 'auto');
|
||||
|
||||
setStored(JSON.stringify({ seasonalThemeOverride: 'retired-theme' }));
|
||||
assert.equal(getSettings().seasonalThemeOverride, 'auto');
|
||||
|
||||
setStored(JSON.stringify({ seasonalThemeOverride: 'earthday' }));
|
||||
assert.equal(getSettings().seasonalThemeOverride, 'earthday');
|
||||
});
|
||||
|
||||
test('merges stored values over defaults', () => {
|
||||
setStored(JSON.stringify({ callNoiseSuppression: 'off', someUnknownKey: 1 }));
|
||||
const s = getSettings();
|
||||
|
||||
+45
-14
@@ -25,6 +25,38 @@ export type DenoiseModelId = 'rnnoise' | 'speex' | 'dtln' | 'deepfilternet';
|
||||
// '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 SeasonalThemeOverride =
|
||||
| 'auto'
|
||||
| 'off'
|
||||
| 'halloween'
|
||||
| 'christmas'
|
||||
| 'newyear'
|
||||
| 'autumn'
|
||||
| 'aprilfools'
|
||||
| 'lunar'
|
||||
| 'valentines'
|
||||
| 'stpatricks'
|
||||
| 'earthday'
|
||||
| 'deepspace'
|
||||
| 'arcade';
|
||||
|
||||
// Allow-list used to validate persisted values below.
|
||||
const SEASONAL_THEME_OVERRIDES: SeasonalThemeOverride[] = [
|
||||
'auto',
|
||||
'off',
|
||||
'halloween',
|
||||
'christmas',
|
||||
'newyear',
|
||||
'autumn',
|
||||
'aprilfools',
|
||||
'lunar',
|
||||
'valentines',
|
||||
'stpatricks',
|
||||
'earthday',
|
||||
'deepspace',
|
||||
'arcade',
|
||||
];
|
||||
// [P5-31] Granular call quality caps. 'auto' = don't cap (the EC fork keeps its
|
||||
// default encoding). Numbers are kbps (audio/screenshare bitrate) or fps
|
||||
// (screenshare framerate); converted to the fork's bits/sec + fps payload in
|
||||
@@ -254,24 +286,15 @@ export interface Settings {
|
||||
soundboardEnabled: boolean;
|
||||
soundboardVolume: number; // 0–100
|
||||
|
||||
seasonalThemeOverride:
|
||||
| 'auto'
|
||||
| 'off'
|
||||
| 'halloween'
|
||||
| 'christmas'
|
||||
| 'newyear'
|
||||
| 'autumn'
|
||||
| 'aprilfools'
|
||||
| 'lunar'
|
||||
| 'valentines'
|
||||
| 'stpatricks'
|
||||
| 'earthday'
|
||||
| 'deepspace'
|
||||
| 'arcade';
|
||||
seasonalThemeOverride: SeasonalThemeOverride;
|
||||
|
||||
// On-device message translation
|
||||
translateTargetLang: string; // BCP-47 base code, default 'en'
|
||||
autoTranslate: boolean; // auto-translate incoming foreign messages (opt-in)
|
||||
|
||||
// GIF picker sends every search term (and the user's IP) directly to Giphy,
|
||||
// so it's opt-in and off by default.
|
||||
gifPickerEnabled: boolean;
|
||||
}
|
||||
|
||||
const defaultSettings: Settings = {
|
||||
@@ -374,6 +397,8 @@ const defaultSettings: Settings = {
|
||||
|
||||
translateTargetLang: 'en',
|
||||
autoTranslate: false,
|
||||
|
||||
gifPickerEnabled: false,
|
||||
};
|
||||
|
||||
export const getSettings = (): Settings => {
|
||||
@@ -422,6 +447,12 @@ export const getSettings = (): Settings => {
|
||||
typeof saved.autoTranslate === 'boolean'
|
||||
? saved.autoTranslate
|
||||
: defaultSettings.autoTranslate,
|
||||
// Coerce any unknown/retired persisted seasonal theme id back to 'auto'.
|
||||
seasonalThemeOverride: SEASONAL_THEME_OVERRIDES.includes(
|
||||
saved.seasonalThemeOverride as SeasonalThemeOverride,
|
||||
)
|
||||
? (saved.seasonalThemeOverride as SeasonalThemeOverride)
|
||||
: defaultSettings.seasonalThemeOverride,
|
||||
composerToolbarButtons: {
|
||||
...DEFAULT_COMPOSER_TOOLBAR,
|
||||
...(saved.composerToolbarButtons ?? {}),
|
||||
|
||||
Reference in New Issue
Block a user