@focus8/settings-registry 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +74 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +410 -119
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +640 -78
package/dist/index.js
CHANGED
|
@@ -138,7 +138,7 @@ export const SETTINGS_GROUPS = [
|
|
|
138
138
|
id: 'calendarType',
|
|
139
139
|
labelKey: 'Settings.CalendarType',
|
|
140
140
|
icon: 'ArrowLeftRight',
|
|
141
|
-
keys: ['calendarView.type'
|
|
141
|
+
keys: ['calendarView.type'],
|
|
142
142
|
},
|
|
143
143
|
{
|
|
144
144
|
id: 'calendarView',
|
|
@@ -147,9 +147,9 @@ export const SETTINGS_GROUPS = [
|
|
|
147
147
|
keys: [
|
|
148
148
|
'calendarView.showCalendarNames',
|
|
149
149
|
'calendarView.splitView',
|
|
150
|
-
'calendarView.calendarColumns',
|
|
151
150
|
'calendarView.dayViewZoom',
|
|
152
151
|
'calendarView.weekViewZoom',
|
|
152
|
+
'calendarView.calendarColumns',
|
|
153
153
|
],
|
|
154
154
|
},
|
|
155
155
|
{
|
|
@@ -163,9 +163,9 @@ export const SETTINGS_GROUPS = [
|
|
|
163
163
|
labelKey: 'Settings.DateWeather',
|
|
164
164
|
icon: 'CloudSun',
|
|
165
165
|
keys: [
|
|
166
|
-
'calendarView.showWeatherOnEvents',
|
|
167
166
|
'calendarView.showWeatherOnTimeline',
|
|
168
167
|
'calendarView.weatherLocation',
|
|
168
|
+
'calendarView.showWeatherOnEvents',
|
|
169
169
|
],
|
|
170
170
|
},
|
|
171
171
|
{
|
|
@@ -175,13 +175,13 @@ export const SETTINGS_GROUPS = [
|
|
|
175
175
|
keys: [
|
|
176
176
|
'sound.reminderVolume',
|
|
177
177
|
'sound.mediaVolume',
|
|
178
|
-
'sound.alarmSound',
|
|
179
178
|
'sound.reminderAlarmSound',
|
|
180
179
|
'sound.reminderAlarmTimeout',
|
|
181
180
|
'sound.allowCustomReminderSounds',
|
|
181
|
+
'notification.',
|
|
182
182
|
'sound.ttsEnabled',
|
|
183
183
|
'sound.ttsRate',
|
|
184
|
-
'
|
|
184
|
+
'sound.alarmSound',
|
|
185
185
|
],
|
|
186
186
|
},
|
|
187
187
|
{
|
|
@@ -189,17 +189,20 @@ export const SETTINGS_GROUPS = [
|
|
|
189
189
|
labelKey: 'Settings.TimerTitle',
|
|
190
190
|
icon: 'Timer',
|
|
191
191
|
keys: [
|
|
192
|
-
'
|
|
192
|
+
'chronological.timer.',
|
|
193
|
+
'timer.',
|
|
193
194
|
'sound.timerAlarmSound',
|
|
194
195
|
'sound.timerAlarmTimeout',
|
|
195
|
-
'
|
|
196
|
+
'sound.timerVolume',
|
|
196
197
|
],
|
|
198
|
+
appMode: 'ENROLLED',
|
|
197
199
|
},
|
|
198
200
|
{
|
|
199
201
|
id: 'lockScreen',
|
|
200
202
|
labelKey: 'Settings.LockScreen',
|
|
201
203
|
icon: 'Lock',
|
|
202
204
|
keys: ['lockScreen.'],
|
|
205
|
+
appMode: 'ENROLLED',
|
|
203
206
|
},
|
|
204
207
|
{
|
|
205
208
|
id: 'touch',
|
|
@@ -239,6 +242,9 @@ export const EXCLUDED_DEVICE_SETTINGS = new Set([
|
|
|
239
242
|
'device.id',
|
|
240
243
|
'device.devMenuEnabled',
|
|
241
244
|
'device.authWarningDismissTtlDays',
|
|
245
|
+
'notification.enabled',
|
|
246
|
+
'notification.notifyAllCalendars',
|
|
247
|
+
'notification.enabledCalendarIds',
|
|
242
248
|
'notification.hasBeenPrompted',
|
|
243
249
|
]);
|
|
244
250
|
/**
|
|
@@ -464,6 +470,31 @@ export const SETTINGS_LABELS = {
|
|
|
464
470
|
function matchesGroup(key, group) {
|
|
465
471
|
return group.keys.some((matcher) => key === matcher || (matcher.endsWith('.') && key.startsWith(matcher)));
|
|
466
472
|
}
|
|
473
|
+
/** Return the index of the first matching key pattern in the group, for sorting. */
|
|
474
|
+
function groupKeyIndex(key, keys) {
|
|
475
|
+
for (let i = 0; i < keys.length; i++) {
|
|
476
|
+
const matcher = keys[i];
|
|
477
|
+
if (key === matcher || (matcher.endsWith('.') && key.startsWith(matcher))) {
|
|
478
|
+
return i;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return keys.length;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Lazy-initialized map from setting key → declaration index in buildEntries.
|
|
485
|
+
* Used as a tiebreaker when multiple keys share the same prefix group index.
|
|
486
|
+
*/
|
|
487
|
+
let _registryKeyOrder;
|
|
488
|
+
function registryKeyOrder() {
|
|
489
|
+
if (!_registryKeyOrder) {
|
|
490
|
+
_registryKeyOrder = new Map();
|
|
491
|
+
const keys = Object.keys(buildEntries(DEFAULT_REGISTRY_CONFIG));
|
|
492
|
+
for (let i = 0; i < keys.length; i++) {
|
|
493
|
+
_registryKeyOrder.set(keys[i], i);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return _registryKeyOrder;
|
|
497
|
+
}
|
|
467
498
|
/**
|
|
468
499
|
* Group parsed setting entries into user-facing groups matching the mobile
|
|
469
500
|
* app's settings menu structure.
|
|
@@ -472,9 +503,9 @@ function matchesGroup(key, group) {
|
|
|
472
503
|
* buckets them into the standard groups, filtering by calendar type and
|
|
473
504
|
* excluded keys.
|
|
474
505
|
*/
|
|
475
|
-
export function groupSettingsForDevice(allSettings, calendarType) {
|
|
476
|
-
// Filter excluded
|
|
477
|
-
const settings = allSettings.filter((s) => !EXCLUDED_DEVICE_SETTINGS.has(s.key));
|
|
506
|
+
export function groupSettingsForDevice(allSettings, calendarType, appMode) {
|
|
507
|
+
// Filter excluded and hidden settings
|
|
508
|
+
const settings = allSettings.filter((s) => !EXCLUDED_DEVICE_SETTINGS.has(s.key) && s.uiType !== 'HIDDEN');
|
|
478
509
|
// Calendar type filtering for event form keys
|
|
479
510
|
const isChronological = calendarType === 'chronological';
|
|
480
511
|
const filteredSettings = settings.filter((s) => {
|
|
@@ -484,6 +515,14 @@ export function groupSettingsForDevice(allSettings, calendarType) {
|
|
|
484
515
|
if (!isChronological && s.key.startsWith('chronologicalEventForm.')) {
|
|
485
516
|
return false;
|
|
486
517
|
}
|
|
518
|
+
// Per-setting calendar type restriction
|
|
519
|
+
if (s.calendarType && s.calendarType !== calendarType) {
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
// Per-setting app mode restriction
|
|
523
|
+
if (s.appMode && s.appMode !== appMode) {
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
487
526
|
return true;
|
|
488
527
|
});
|
|
489
528
|
const claimed = new Set();
|
|
@@ -493,10 +532,25 @@ export function groupSettingsForDevice(allSettings, calendarType) {
|
|
|
493
532
|
if (group.calendarType && group.calendarType !== calendarType) {
|
|
494
533
|
continue;
|
|
495
534
|
}
|
|
535
|
+
// Skip groups not relevant for current app mode
|
|
536
|
+
if (group.appMode && group.appMode !== appMode) {
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
496
539
|
const matched = filteredSettings.filter((s) => !claimed.has(s.key) && matchesGroup(s.key, group));
|
|
497
540
|
if (matched.length === 0) {
|
|
498
541
|
continue;
|
|
499
542
|
}
|
|
543
|
+
// Sort matched settings by the order defined in group.keys,
|
|
544
|
+
// with registry declaration order as tiebreaker for prefix matches
|
|
545
|
+
const order = registryKeyOrder();
|
|
546
|
+
matched.sort((a, b) => {
|
|
547
|
+
const aIdx = groupKeyIndex(a.key, group.keys);
|
|
548
|
+
const bIdx = groupKeyIndex(b.key, group.keys);
|
|
549
|
+
if (aIdx !== bIdx) {
|
|
550
|
+
return aIdx - bIdx;
|
|
551
|
+
}
|
|
552
|
+
return (order.get(a.key) ?? Infinity) - (order.get(b.key) ?? Infinity);
|
|
553
|
+
});
|
|
500
554
|
for (const s of matched) {
|
|
501
555
|
claimed.add(s.key);
|
|
502
556
|
}
|
|
@@ -516,10 +570,57 @@ export const DEFAULT_REGISTRY_CONFIG = {
|
|
|
516
570
|
defaultLocale: 'nb',
|
|
517
571
|
};
|
|
518
572
|
// ---------------------------------------------------------------------------
|
|
573
|
+
// Shared option arrays (reused across multiple settings)
|
|
574
|
+
// ---------------------------------------------------------------------------
|
|
575
|
+
const ALARM_SOUND_OPTIONS = [
|
|
576
|
+
{ value: 'none', labelKey: 'Settings.Option.AlarmSound.None' },
|
|
577
|
+
{ value: 'alarm1', labelKey: 'Settings.Option.AlarmSound.Alarm1' },
|
|
578
|
+
{ value: 'alarm2', labelKey: 'Settings.Option.AlarmSound.Alarm2' },
|
|
579
|
+
{ value: 'alarm3', labelKey: 'Settings.Option.AlarmSound.Alarm3' },
|
|
580
|
+
{ value: 'alarm4', labelKey: 'Settings.Option.AlarmSound.Alarm4' },
|
|
581
|
+
{ value: 'alarm5', labelKey: 'Settings.Option.AlarmSound.Alarm5' },
|
|
582
|
+
{ value: 'alarm6', labelKey: 'Settings.Option.AlarmSound.Alarm6' },
|
|
583
|
+
{ value: 'alarm7', labelKey: 'Settings.Option.AlarmSound.Alarm7' },
|
|
584
|
+
{ value: 'alarm8', labelKey: 'Settings.Option.AlarmSound.Alarm8' },
|
|
585
|
+
{ value: 'alarm9', labelKey: 'Settings.Option.AlarmSound.Alarm9' },
|
|
586
|
+
];
|
|
587
|
+
const ALARM_TIMEOUT_OPTIONS = [
|
|
588
|
+
{ value: 1, labelKey: 'Settings.Option.AlarmTimeout.1min' },
|
|
589
|
+
{ value: 2, labelKey: 'Settings.Option.AlarmTimeout.2min' },
|
|
590
|
+
{ value: 3, labelKey: 'Settings.Option.AlarmTimeout.3min' },
|
|
591
|
+
{ value: 5, labelKey: 'Settings.Option.AlarmTimeout.5min' },
|
|
592
|
+
{ value: 10, labelKey: 'Settings.Option.AlarmTimeout.10min' },
|
|
593
|
+
];
|
|
594
|
+
const DAY_VIEW_ZOOM_OPTIONS = [
|
|
595
|
+
{ value: 15, labelKey: 'Settings.Option.DayViewZoom.15min' },
|
|
596
|
+
{ value: 30, labelKey: 'Settings.Option.DayViewZoom.30min' },
|
|
597
|
+
{ value: 60, labelKey: 'Settings.Option.DayViewZoom.1hour' },
|
|
598
|
+
];
|
|
599
|
+
const TIME_OF_DAY_SLIDER = { min: 0, max: 23, step: 1 };
|
|
600
|
+
// ---------------------------------------------------------------------------
|
|
519
601
|
// Helper
|
|
520
602
|
// ---------------------------------------------------------------------------
|
|
521
|
-
function def(category, type, defaultValue,
|
|
522
|
-
|
|
603
|
+
function def(category, type, defaultValue, uiType, extra) {
|
|
604
|
+
const result = {
|
|
605
|
+
category,
|
|
606
|
+
type,
|
|
607
|
+
default: defaultValue,
|
|
608
|
+
sync: extra?.sync ?? true,
|
|
609
|
+
uiType,
|
|
610
|
+
};
|
|
611
|
+
if (extra?.options) {
|
|
612
|
+
result.options = extra.options;
|
|
613
|
+
}
|
|
614
|
+
if (extra?.sliderConfig) {
|
|
615
|
+
result.sliderConfig = extra.sliderConfig;
|
|
616
|
+
}
|
|
617
|
+
if (extra?.appMode) {
|
|
618
|
+
result.appMode = extra.appMode;
|
|
619
|
+
}
|
|
620
|
+
if (extra?.calendarType) {
|
|
621
|
+
result.calendarType = extra.calendarType;
|
|
622
|
+
}
|
|
623
|
+
return result;
|
|
523
624
|
}
|
|
524
625
|
// ---------------------------------------------------------------------------
|
|
525
626
|
// Entry builder (internal — used by SettingsRegistry class and factory)
|
|
@@ -529,159 +630,277 @@ function buildEntries(config) {
|
|
|
529
630
|
// ═══════════════════════════════════════════════════════════════════════
|
|
530
631
|
// Appearance
|
|
531
632
|
// ═══════════════════════════════════════════════════════════════════════
|
|
532
|
-
'appearance.theme': def('appearance', 'string', config.defaultTheme
|
|
533
|
-
|
|
534
|
-
|
|
633
|
+
'appearance.theme': def('appearance', 'string', config.defaultTheme, 'CUSTOM_THEME_PICKER', {
|
|
634
|
+
options: [
|
|
635
|
+
{ value: 'light', labelKey: 'Settings.Option.Theme.Light' },
|
|
636
|
+
{ value: 'dark', labelKey: 'Settings.Option.Theme.Dark' },
|
|
637
|
+
{ value: 'system', labelKey: 'Settings.Option.Theme.System' },
|
|
638
|
+
],
|
|
639
|
+
}),
|
|
640
|
+
'appearance.clockType': def('appearance', 'string', 'digital', 'CUSTOM_CLOCK_TYPE', {
|
|
641
|
+
options: [
|
|
642
|
+
{ value: 'digital', labelKey: 'Settings.Option.ClockType.Digital' },
|
|
643
|
+
{ value: 'analog', labelKey: 'Settings.Option.ClockType.Analog' },
|
|
644
|
+
],
|
|
645
|
+
appMode: 'ENROLLED',
|
|
646
|
+
}),
|
|
647
|
+
'appearance.enableDayColors': def('appearance', 'boolean', false, 'TOGGLE', { calendarType: 'chronological' }),
|
|
535
648
|
// ═══════════════════════════════════════════════════════════════════════
|
|
536
649
|
// Calendar view
|
|
537
650
|
// ═══════════════════════════════════════════════════════════════════════
|
|
538
|
-
'calendarView.type': def('calendarView', 'string', 'time-based'
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
651
|
+
'calendarView.type': def('calendarView', 'string', 'time-based', 'CUSTOM_CALENDAR_TYPE', {
|
|
652
|
+
options: [
|
|
653
|
+
{
|
|
654
|
+
value: 'chronological',
|
|
655
|
+
labelKey: 'Settings.Option.CalendarType.Chronological',
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
value: 'time-based',
|
|
659
|
+
labelKey: 'Settings.Option.CalendarType.TimeBased',
|
|
660
|
+
},
|
|
661
|
+
],
|
|
662
|
+
}),
|
|
663
|
+
'calendarView.view': def('calendarView', 'string', 'day', 'SELECT', {
|
|
664
|
+
options: [
|
|
665
|
+
{ value: 'day', labelKey: 'Settings.Option.CalendarView.Day' },
|
|
666
|
+
{
|
|
667
|
+
value: '3-days',
|
|
668
|
+
labelKey: 'Settings.Option.CalendarView.3Days',
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
value: '5-days',
|
|
672
|
+
labelKey: 'Settings.Option.CalendarView.5Days',
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
value: '7-days',
|
|
676
|
+
labelKey: 'Settings.Option.CalendarView.7Days',
|
|
677
|
+
},
|
|
678
|
+
{ value: 'week', labelKey: 'Settings.Option.CalendarView.Week' },
|
|
679
|
+
{
|
|
680
|
+
value: 'month',
|
|
681
|
+
labelKey: 'Settings.Option.CalendarView.Month',
|
|
682
|
+
},
|
|
683
|
+
{
|
|
684
|
+
value: 'overview',
|
|
685
|
+
labelKey: 'Settings.Option.CalendarView.Overview',
|
|
686
|
+
},
|
|
687
|
+
],
|
|
688
|
+
}),
|
|
689
|
+
'calendarView.dayViewZoom': def('calendarView', 'number', 60, 'SELECT', { options: DAY_VIEW_ZOOM_OPTIONS, calendarType: 'time-based' }),
|
|
690
|
+
'calendarView.weekViewZoom': def('calendarView', 'number', 60, 'SELECT', { options: DAY_VIEW_ZOOM_OPTIONS, calendarType: 'time-based' }),
|
|
691
|
+
'calendarView.splitView': def('calendarView', 'boolean', false, 'TOGGLE'),
|
|
692
|
+
'calendarView.showCalendarNames': def('calendarView', 'boolean', true, 'TOGGLE'),
|
|
693
|
+
'calendarView.calendarColumns': def('calendarView', 'json', [], 'CUSTOM_SPLIT_VIEW_CONFIG'),
|
|
694
|
+
'calendarView.autoReturnToTodayEnabled': def('calendarView', 'boolean', config.isEnrolled, 'TOGGLE'),
|
|
695
|
+
'calendarView.autoReturnToTodayTimeoutSeconds': def('calendarView', 'number', 300, 'SLIDER', { sliderConfig: { min: 30, max: 600, step: 30 } }),
|
|
696
|
+
'calendarView.showWeatherOnEvents': def('calendarView', 'boolean', false, 'TOGGLE'),
|
|
697
|
+
'calendarView.showWeatherOnTimeline': def('calendarView', 'boolean', false, 'TOGGLE'),
|
|
698
|
+
'calendarView.weatherLocation': def('calendarView', 'json', null, 'CUSTOM_WEATHER_LOCATION'),
|
|
550
699
|
// ═══════════════════════════════════════════════════════════════════════
|
|
551
700
|
// Event form field visibility (time-based)
|
|
552
701
|
// ═══════════════════════════════════════════════════════════════════════
|
|
553
|
-
'eventForm.recurrence': def('eventForm', 'boolean', true),
|
|
554
|
-
'eventForm.
|
|
555
|
-
'eventForm.
|
|
556
|
-
'eventForm.
|
|
557
|
-
'eventForm.
|
|
558
|
-
'eventForm.description': def('eventForm', 'boolean', true),
|
|
559
|
-
'eventForm.checklist': def('eventForm', 'boolean', true),
|
|
560
|
-
'eventForm.images': def('eventForm', 'boolean', false),
|
|
561
|
-
'eventForm.audioClips': def('eventForm', 'boolean', false),
|
|
562
|
-
'eventForm.notificationReceivers': def('eventForm', 'boolean', true),
|
|
563
|
-
'eventForm.visibility': def('eventForm', 'boolean', false),
|
|
702
|
+
'eventForm.recurrence': def('eventForm', 'boolean', true, 'TOGGLE'),
|
|
703
|
+
'eventForm.location': def('eventForm', 'boolean', true, 'TOGGLE'),
|
|
704
|
+
'eventForm.travelTime': def('eventForm', 'boolean', false, 'TOGGLE'),
|
|
705
|
+
'eventForm.reminders': def('eventForm', 'boolean', true, 'TOGGLE'),
|
|
706
|
+
'eventForm.emailReminders': def('eventForm', 'boolean', false, 'TOGGLE'),
|
|
707
|
+
'eventForm.description': def('eventForm', 'boolean', true, 'TOGGLE'),
|
|
708
|
+
'eventForm.checklist': def('eventForm', 'boolean', true, 'TOGGLE'),
|
|
709
|
+
'eventForm.images': def('eventForm', 'boolean', false, 'TOGGLE'),
|
|
710
|
+
'eventForm.audioClips': def('eventForm', 'boolean', false, 'TOGGLE'),
|
|
711
|
+
'eventForm.notificationReceivers': def('eventForm', 'boolean', true, 'TOGGLE'),
|
|
712
|
+
'eventForm.visibility': def('eventForm', 'boolean', false, 'TOGGLE'),
|
|
564
713
|
// ═══════════════════════════════════════════════════════════════════════
|
|
565
714
|
// Sound & alerts
|
|
566
715
|
// ═══════════════════════════════════════════════════════════════════════
|
|
567
|
-
'sound.timerVolume': def('sound', 'number', 0.5),
|
|
568
|
-
'sound.reminderVolume': def('sound', 'number', 0.5),
|
|
569
|
-
'sound.mediaVolume': def('sound', 'number', 0.5),
|
|
570
|
-
'sound.alarmSound': def('sound', 'string', 'alarm1'),
|
|
571
|
-
'sound.reminderAlarmSound': def('sound', 'string', 'alarm1'),
|
|
572
|
-
'sound.timerAlarmSound': def('sound', 'string', 'alarm1'),
|
|
573
|
-
'sound.timerAlarmTimeout': def('sound', 'number', 3),
|
|
574
|
-
'sound.reminderAlarmTimeout': def('sound', 'number', 3),
|
|
575
|
-
'sound.allowCustomReminderSounds': def('sound', 'boolean', false),
|
|
576
|
-
'sound.ttsEnabled': def('sound', 'boolean', config.isEnrolled),
|
|
577
|
-
'sound.ttsRate': def('sound', 'number', 1.0),
|
|
716
|
+
'sound.timerVolume': def('sound', 'number', 0.5, 'VOLUME_SLIDER'),
|
|
717
|
+
'sound.reminderVolume': def('sound', 'number', 0.5, 'VOLUME_SLIDER', { appMode: 'ENROLLED' }),
|
|
718
|
+
'sound.mediaVolume': def('sound', 'number', 0.5, 'VOLUME_SLIDER', { appMode: 'ENROLLED' }),
|
|
719
|
+
'sound.alarmSound': def('sound', 'string', 'alarm1', 'SELECT', { options: ALARM_SOUND_OPTIONS }),
|
|
720
|
+
'sound.reminderAlarmSound': def('sound', 'string', 'alarm1', 'SELECT', { options: ALARM_SOUND_OPTIONS }),
|
|
721
|
+
'sound.timerAlarmSound': def('sound', 'string', 'alarm1', 'SELECT', { options: ALARM_SOUND_OPTIONS }),
|
|
722
|
+
'sound.timerAlarmTimeout': def('sound', 'number', 3, 'SELECT', { options: ALARM_TIMEOUT_OPTIONS }),
|
|
723
|
+
'sound.reminderAlarmTimeout': def('sound', 'number', 3, 'SELECT', { options: ALARM_TIMEOUT_OPTIONS }),
|
|
724
|
+
'sound.allowCustomReminderSounds': def('sound', 'boolean', false, 'TOGGLE'),
|
|
725
|
+
'sound.ttsEnabled': def('sound', 'boolean', config.isEnrolled, 'TOGGLE', { appMode: 'ENROLLED' }),
|
|
726
|
+
'sound.ttsRate': def('sound', 'number', 1.0, 'SLIDER', { sliderConfig: { min: 0.5, max: 2, step: 0.1 }, appMode: 'ENROLLED' }),
|
|
578
727
|
// ═══════════════════════════════════════════════════════════════════════
|
|
579
728
|
// Timer
|
|
580
729
|
// ═══════════════════════════════════════════════════════════════════════
|
|
581
|
-
'timer.showTimeRemaining': def('timer', 'boolean', true),
|
|
582
|
-
'timer.showEndTime': def('timer', 'boolean', true),
|
|
583
|
-
'timer.showRestartButton': def('timer', 'boolean', true),
|
|
584
|
-
'timer.showPauseButton': def('timer', 'boolean', true),
|
|
730
|
+
'timer.showTimeRemaining': def('timer', 'boolean', true, 'TOGGLE'),
|
|
731
|
+
'timer.showEndTime': def('timer', 'boolean', true, 'TOGGLE'),
|
|
732
|
+
'timer.showRestartButton': def('timer', 'boolean', true, 'TOGGLE'),
|
|
733
|
+
'timer.showPauseButton': def('timer', 'boolean', true, 'TOGGLE'),
|
|
585
734
|
// ═══════════════════════════════════════════════════════════════════════
|
|
586
735
|
// Lock screen
|
|
587
736
|
// ═══════════════════════════════════════════════════════════════════════
|
|
588
|
-
'lockScreen.
|
|
589
|
-
'lockScreen.
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
737
|
+
'lockScreen.inactivityLockEnabled': def('lockScreen', 'boolean', false, 'TOGGLE'),
|
|
738
|
+
'lockScreen.inactivityTimeoutMinutes': def('lockScreen', 'number', 5, 'SELECT', {
|
|
739
|
+
options: [
|
|
740
|
+
{ value: 1, labelKey: 'Settings.Option.InactivityTimeout.1min' },
|
|
741
|
+
{ value: 5, labelKey: 'Settings.Option.InactivityTimeout.5min' },
|
|
742
|
+
{ value: 10, labelKey: 'Settings.Option.InactivityTimeout.10min' },
|
|
743
|
+
{ value: 15, labelKey: 'Settings.Option.InactivityTimeout.15min' },
|
|
744
|
+
{ value: 30, labelKey: 'Settings.Option.InactivityTimeout.30min' },
|
|
745
|
+
{ value: 45, labelKey: 'Settings.Option.InactivityTimeout.45min' },
|
|
746
|
+
],
|
|
747
|
+
}),
|
|
748
|
+
'lockScreen.pin': def('lockScreen', 'string', '', 'PIN_INPUT'),
|
|
749
|
+
'lockScreen.clockDisplay': def('lockScreen', 'string', 'digital', 'SELECT', {
|
|
750
|
+
options: [
|
|
751
|
+
{ value: 'none', labelKey: 'Settings.Option.ClockDisplay.None' },
|
|
752
|
+
{
|
|
753
|
+
value: 'digital',
|
|
754
|
+
labelKey: 'Settings.Option.ClockDisplay.Digital',
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
value: 'analog',
|
|
758
|
+
labelKey: 'Settings.Option.ClockDisplay.Analog',
|
|
759
|
+
},
|
|
760
|
+
],
|
|
761
|
+
}),
|
|
762
|
+
'lockScreen.showHourNumbers': def('lockScreen', 'boolean', true, 'TOGGLE'),
|
|
763
|
+
'lockScreen.showDate': def('lockScreen', 'boolean', true, 'TOGGLE'),
|
|
764
|
+
'lockScreen.imageMode': def('lockScreen', 'string', 'none', 'SELECT', {
|
|
765
|
+
options: [
|
|
766
|
+
{ value: 'none', labelKey: 'Settings.Option.ImageMode.None' },
|
|
767
|
+
{
|
|
768
|
+
value: 'background',
|
|
769
|
+
labelKey: 'Settings.Option.ImageMode.Background',
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
value: 'photoFrame',
|
|
773
|
+
labelKey: 'Settings.Option.ImageMode.PhotoFrame',
|
|
774
|
+
},
|
|
775
|
+
],
|
|
776
|
+
}),
|
|
777
|
+
'lockScreen.backgroundImage': def('lockScreen', 'json', null, 'CUSTOM_IMAGE'),
|
|
778
|
+
'lockScreen.photoFrameIntervalSeconds': def('lockScreen', 'number', 60, 'SELECT', {
|
|
779
|
+
options: [
|
|
780
|
+
{ value: 30, labelKey: 'Settings.Option.PhotoFrameInterval.30sec' },
|
|
781
|
+
{ value: 60, labelKey: 'Settings.Option.PhotoFrameInterval.1min' },
|
|
782
|
+
{ value: 120, labelKey: 'Settings.Option.PhotoFrameInterval.2min' },
|
|
783
|
+
{ value: 300, labelKey: 'Settings.Option.PhotoFrameInterval.5min' },
|
|
784
|
+
],
|
|
785
|
+
}),
|
|
786
|
+
'lockScreen.photoFrameImages': def('lockScreen', 'json', [], 'CUSTOM_IMAGE_ARRAY'),
|
|
598
787
|
// ═══════════════════════════════════════════════════════════════════════
|
|
599
788
|
// Touch / gestures
|
|
600
789
|
// ═══════════════════════════════════════════════════════════════════════
|
|
601
|
-
'touch.enableTapToCreate': def('touch', 'boolean', false),
|
|
602
|
-
'touch.enableDragDrop': def('touch', 'boolean', false),
|
|
790
|
+
'touch.enableTapToCreate': def('touch', 'boolean', false, 'TOGGLE'),
|
|
791
|
+
'touch.enableDragDrop': def('touch', 'boolean', false, 'TOGGLE'),
|
|
603
792
|
// ═══════════════════════════════════════════════════════════════════════
|
|
604
793
|
// Device (not synced unless noted)
|
|
605
794
|
// ═══════════════════════════════════════════════════════════════════════
|
|
606
|
-
'device.id': def('device', 'string', '', false),
|
|
607
|
-
'device.timePickerMode': def('device', 'string', 'dials'
|
|
608
|
-
|
|
609
|
-
|
|
795
|
+
'device.id': def('device', 'string', '', 'HIDDEN', { sync: false }),
|
|
796
|
+
'device.timePickerMode': def('device', 'string', 'dials', 'SELECT', {
|
|
797
|
+
options: [
|
|
798
|
+
{ value: 'dials', labelKey: 'Settings.Option.TimePickerMode.Dials' },
|
|
799
|
+
{
|
|
800
|
+
value: 'keypad',
|
|
801
|
+
labelKey: 'Settings.Option.TimePickerMode.Keypad',
|
|
802
|
+
},
|
|
803
|
+
],
|
|
804
|
+
}),
|
|
805
|
+
'device.devMenuEnabled': def('device', 'boolean', false, 'HIDDEN', { sync: false }),
|
|
806
|
+
'device.authWarningDismissTtlDays': def('device', 'number', 3, 'HIDDEN', { sync: false }),
|
|
610
807
|
// ═══════════════════════════════════════════════════════════════════════
|
|
611
808
|
// Language
|
|
612
809
|
// ═══════════════════════════════════════════════════════════════════════
|
|
613
|
-
'language.locale': def('language', 'string', config.defaultLocale
|
|
810
|
+
'language.locale': def('language', 'string', config.defaultLocale, 'CUSTOM_LANGUAGE_PICKER', {
|
|
811
|
+
options: [
|
|
812
|
+
{ value: 'nb', labelKey: 'Settings.Option.Language.Norwegian' },
|
|
813
|
+
{ value: 'en', labelKey: 'Settings.Option.Language.English' },
|
|
814
|
+
],
|
|
815
|
+
}),
|
|
614
816
|
// ═══════════════════════════════════════════════════════════════════════
|
|
615
817
|
// Notifications
|
|
616
818
|
// ═══════════════════════════════════════════════════════════════════════
|
|
617
|
-
'notification.enabled': def('notification', 'boolean', false),
|
|
618
|
-
'notification.notifyAllCalendars': def('notification', 'boolean', true),
|
|
619
|
-
'notification.enabledCalendarIds': def('notification', 'json', []),
|
|
620
|
-
'notification.hasBeenPrompted': def('notification', 'boolean', false, false),
|
|
819
|
+
'notification.enabled': def('notification', 'boolean', false, 'TOGGLE'),
|
|
820
|
+
'notification.notifyAllCalendars': def('notification', 'boolean', true, 'TOGGLE'),
|
|
821
|
+
'notification.enabledCalendarIds': def('notification', 'json', [], 'CUSTOM_CALENDAR_IDS'),
|
|
822
|
+
'notification.hasBeenPrompted': def('notification', 'boolean', false, 'HIDDEN', { sync: false }),
|
|
621
823
|
// ═══════════════════════════════════════════════════════════════════════
|
|
622
824
|
// Chronological features (header, footer, menu, quick settings, timer)
|
|
623
825
|
// ═══════════════════════════════════════════════════════════════════════
|
|
624
826
|
// Header
|
|
625
|
-
'chronological.header.showNavigationArrows': def('chronological', 'boolean', true),
|
|
626
|
-
'chronological.header.
|
|
627
|
-
'chronological.header.
|
|
628
|
-
'chronological.header.showTimeOfDay': def('chronological', 'boolean', false),
|
|
827
|
+
'chronological.header.showNavigationArrows': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
828
|
+
'chronological.header.showCurrentYearInDate': def('chronological', 'boolean', false, 'TOGGLE'),
|
|
829
|
+
'chronological.header.showClock': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
830
|
+
'chronological.header.showTimeOfDay': def('chronological', 'boolean', false, 'TOGGLE'),
|
|
629
831
|
// Footer
|
|
630
|
-
'chronological.footer.showMenuButton': def('chronological', 'boolean', true),
|
|
631
|
-
'chronological.footer.showViewSwitcherDay': def('chronological', 'boolean', true),
|
|
632
|
-
'chronological.footer.showViewSwitcherWeek': def('chronological', 'boolean', true),
|
|
633
|
-
'chronological.footer.showViewSwitcherMonth': def('chronological', 'boolean', true),
|
|
634
|
-
'chronological.footer.showTimerButton': def('chronological', 'boolean', true),
|
|
635
|
-
'chronological.footer.showNewEventButton': def('chronological', 'boolean', true),
|
|
636
|
-
'chronological.footer.showSettingsButton': def('chronological', 'boolean', true),
|
|
832
|
+
'chronological.footer.showMenuButton': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
833
|
+
'chronological.footer.showViewSwitcherDay': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
834
|
+
'chronological.footer.showViewSwitcherWeek': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
835
|
+
'chronological.footer.showViewSwitcherMonth': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
836
|
+
'chronological.footer.showTimerButton': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
837
|
+
'chronological.footer.showNewEventButton': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
838
|
+
'chronological.footer.showSettingsButton': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
637
839
|
// Timer features
|
|
638
|
-
'chronological.timer.showNewCountdown': def('chronological', 'boolean', true),
|
|
639
|
-
'chronological.timer.showFromTemplate': def('chronological', 'boolean', true),
|
|
640
|
-
'chronological.timer.
|
|
641
|
-
'chronological.timer.
|
|
642
|
-
'chronological.timer.
|
|
840
|
+
'chronological.timer.showNewCountdown': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
841
|
+
'chronological.timer.showFromTemplate': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
842
|
+
'chronological.timer.showAddTemplate': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
843
|
+
'chronological.timer.showEditTemplate': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
844
|
+
'chronological.timer.showDeleteTemplate': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
643
845
|
// Menu
|
|
644
|
-
'chronological.menu.showSettingsButton': def('chronological', 'boolean', true),
|
|
846
|
+
'chronological.menu.showSettingsButton': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
645
847
|
// Quick settings
|
|
646
|
-
'chronological.quickSettings.showTimerVolume': def('chronological', 'boolean', true),
|
|
647
|
-
'chronological.quickSettings.showReminderVolume': def('chronological', 'boolean', true),
|
|
648
|
-
'chronological.quickSettings.showMediaVolume': def('chronological', 'boolean', true),
|
|
649
|
-
'chronological.quickSettings.showBrightness': def('chronological', 'boolean', true),
|
|
650
|
-
'chronological.quickSettings.showLockScreen': def('chronological', 'boolean', true),
|
|
848
|
+
'chronological.quickSettings.showTimerVolume': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
849
|
+
'chronological.quickSettings.showReminderVolume': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
850
|
+
'chronological.quickSettings.showMediaVolume': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
851
|
+
'chronological.quickSettings.showBrightness': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
852
|
+
'chronological.quickSettings.showLockScreen': def('chronological', 'boolean', true, 'TOGGLE'),
|
|
651
853
|
// Time-of-day periods
|
|
652
|
-
'chronological.timeOfDay.morningStart': def('chronological', 'number', 6),
|
|
653
|
-
'chronological.timeOfDay.forenoonStart': def('chronological', 'number', 9),
|
|
654
|
-
'chronological.timeOfDay.afternoonStart': def('chronological', 'number', 12),
|
|
655
|
-
'chronological.timeOfDay.eveningStart': def('chronological', 'number', 18),
|
|
656
|
-
'chronological.timeOfDay.nightStart': def('chronological', 'number', 0),
|
|
854
|
+
'chronological.timeOfDay.morningStart': def('chronological', 'number', 6, 'SLIDER', { sliderConfig: TIME_OF_DAY_SLIDER }),
|
|
855
|
+
'chronological.timeOfDay.forenoonStart': def('chronological', 'number', 9, 'SLIDER', { sliderConfig: TIME_OF_DAY_SLIDER }),
|
|
856
|
+
'chronological.timeOfDay.afternoonStart': def('chronological', 'number', 12, 'SLIDER', { sliderConfig: TIME_OF_DAY_SLIDER }),
|
|
857
|
+
'chronological.timeOfDay.eveningStart': def('chronological', 'number', 18, 'SLIDER', { sliderConfig: TIME_OF_DAY_SLIDER }),
|
|
858
|
+
'chronological.timeOfDay.nightStart': def('chronological', 'number', 0, 'SLIDER', { sliderConfig: TIME_OF_DAY_SLIDER }),
|
|
657
859
|
// ═══════════════════════════════════════════════════════════════════════
|
|
658
860
|
// Chronological event form
|
|
659
861
|
// ═══════════════════════════════════════════════════════════════════════
|
|
660
|
-
// Toggleable fields
|
|
661
|
-
'chronologicalEventForm.field.description': def('chronologicalEventForm', 'boolean', true),
|
|
662
|
-
'chronologicalEventForm.field.recurrence': def('chronologicalEventForm', 'boolean', true),
|
|
663
|
-
'chronologicalEventForm.field.acknowledge': def('chronologicalEventForm', 'boolean', true),
|
|
664
|
-
'chronologicalEventForm.field.checklist': def('chronologicalEventForm', 'boolean', true),
|
|
665
|
-
'chronologicalEventForm.field.extraImages': def('chronologicalEventForm', 'boolean', true),
|
|
666
|
-
'chronologicalEventForm.field.reminders': def('chronologicalEventForm', 'boolean', true),
|
|
667
|
-
'chronologicalEventForm.field.audioClips': def('chronologicalEventForm', 'boolean', true),
|
|
668
862
|
// Fixed fields
|
|
669
|
-
'chronologicalEventForm.fixedField.allDay': def('chronologicalEventForm', 'boolean', true),
|
|
670
|
-
'chronologicalEventForm.fixedField.endTime': def('chronologicalEventForm', 'boolean', true),
|
|
671
|
-
'chronologicalEventForm.fixedField.visibility': def('chronologicalEventForm', 'boolean', true),
|
|
863
|
+
'chronologicalEventForm.fixedField.allDay': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
|
|
864
|
+
'chronologicalEventForm.fixedField.endTime': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
|
|
865
|
+
'chronologicalEventForm.fixedField.visibility': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
|
|
866
|
+
// Toggleable fields
|
|
867
|
+
'chronologicalEventForm.field.acknowledge': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
|
|
868
|
+
'chronologicalEventForm.field.description': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
|
|
869
|
+
'chronologicalEventForm.field.recurrence': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
|
|
870
|
+
'chronologicalEventForm.field.checklist': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
|
|
871
|
+
'chronologicalEventForm.field.extraImages': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
|
|
872
|
+
'chronologicalEventForm.field.reminders': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
|
|
873
|
+
'chronologicalEventForm.field.audioClips': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
|
|
672
874
|
// Suggest end time
|
|
673
|
-
'chronologicalEventForm.suggestEndTime.enabled': def('chronologicalEventForm', 'boolean', false),
|
|
674
|
-
'chronologicalEventForm.suggestEndTime.value': def('chronologicalEventForm', 'number', 30),
|
|
675
|
-
'chronologicalEventForm.suggestEndTime.unit': def('chronologicalEventForm', 'string', 'minutes'
|
|
875
|
+
'chronologicalEventForm.suggestEndTime.enabled': def('chronologicalEventForm', 'boolean', false, 'TOGGLE'),
|
|
876
|
+
'chronologicalEventForm.suggestEndTime.value': def('chronologicalEventForm', 'number', 30, 'SLIDER', { sliderConfig: { min: 5, max: 480, step: 5 } }),
|
|
877
|
+
'chronologicalEventForm.suggestEndTime.unit': def('chronologicalEventForm', 'string', 'minutes', 'SELECT', {
|
|
878
|
+
options: [
|
|
879
|
+
{
|
|
880
|
+
value: 'minutes',
|
|
881
|
+
labelKey: 'Settings.Option.SuggestEndTimeUnit.Minutes',
|
|
882
|
+
},
|
|
883
|
+
{
|
|
884
|
+
value: 'hours',
|
|
885
|
+
labelKey: 'Settings.Option.SuggestEndTimeUnit.Hours',
|
|
886
|
+
},
|
|
887
|
+
],
|
|
888
|
+
}),
|
|
676
889
|
// Default visibility
|
|
677
|
-
'chronologicalEventForm.defaultVisibility': def('chronologicalEventForm', 'string', 'Private'
|
|
890
|
+
'chronologicalEventForm.defaultVisibility': def('chronologicalEventForm', 'string', 'Private', 'SELECT', {
|
|
891
|
+
options: [
|
|
892
|
+
{ value: 'Public', labelKey: 'Settings.Option.Visibility.Public' },
|
|
893
|
+
{ value: 'Private', labelKey: 'Settings.Option.Visibility.Private' },
|
|
894
|
+
{ value: 'Custom', labelKey: 'Settings.Option.Visibility.Custom' },
|
|
895
|
+
],
|
|
896
|
+
}),
|
|
678
897
|
// Reminder presets
|
|
679
|
-
'chronologicalEventForm.reminderPresets.timed': def('chronologicalEventForm', 'json', [5, 15, 30, 60, 120, 1440]),
|
|
898
|
+
'chronologicalEventForm.reminderPresets.timed': def('chronologicalEventForm', 'json', [5, 15, 30, 60, 120, 1440], 'CUSTOM_REMINDER_PRESETS'),
|
|
680
899
|
'chronologicalEventForm.reminderPresets.allDay': def('chronologicalEventForm', 'json', [
|
|
681
900
|
{ daysBefore: 0, time: '09:00' },
|
|
682
901
|
{ daysBefore: 1, time: '18:00' },
|
|
683
902
|
{ daysBefore: 2, time: '09:00' },
|
|
684
|
-
]),
|
|
903
|
+
], 'CUSTOM_REMINDER_PRESETS'),
|
|
685
904
|
};
|
|
686
905
|
}
|
|
687
906
|
// ---------------------------------------------------------------------------
|
|
@@ -726,8 +945,22 @@ export class SettingsRegistry {
|
|
|
726
945
|
case 'number':
|
|
727
946
|
return String(value ?? 0);
|
|
728
947
|
case 'boolean':
|
|
948
|
+
if (typeof value === 'string') {
|
|
949
|
+
return value === 'true' ? 'true' : 'false';
|
|
950
|
+
}
|
|
729
951
|
return value ? 'true' : 'false';
|
|
730
952
|
case 'json':
|
|
953
|
+
// If already a serialized JSON string, return as-is to avoid
|
|
954
|
+
// double-wrapping (e.g. JSON.stringify("[]") → "\"[]\"")
|
|
955
|
+
if (typeof value === 'string') {
|
|
956
|
+
try {
|
|
957
|
+
JSON.parse(value);
|
|
958
|
+
return value;
|
|
959
|
+
}
|
|
960
|
+
catch {
|
|
961
|
+
return JSON.stringify(value);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
731
964
|
return JSON.stringify(value);
|
|
732
965
|
}
|
|
733
966
|
}
|
|
@@ -746,7 +979,18 @@ export class SettingsRegistry {
|
|
|
746
979
|
return (raw === 'true');
|
|
747
980
|
case 'json':
|
|
748
981
|
try {
|
|
749
|
-
|
|
982
|
+
let result = JSON.parse(raw);
|
|
983
|
+
// Unwrap multiply-escaped JSON strings caused by repeated
|
|
984
|
+
// double-serialization (each push/pull cycle added a layer)
|
|
985
|
+
while (typeof result === 'string') {
|
|
986
|
+
try {
|
|
987
|
+
result = JSON.parse(result);
|
|
988
|
+
}
|
|
989
|
+
catch {
|
|
990
|
+
break;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
return result;
|
|
750
994
|
}
|
|
751
995
|
catch {
|
|
752
996
|
return this.getDefault(key);
|
|
@@ -825,6 +1069,9 @@ export function parseSettingsSnapshot(json, calendarType, registry = defaultRegi
|
|
|
825
1069
|
groups.set(category, []);
|
|
826
1070
|
}
|
|
827
1071
|
const labelDef = SETTINGS_LABELS[key];
|
|
1072
|
+
const entryDef = key in registry.entries
|
|
1073
|
+
? registry.entries[key]
|
|
1074
|
+
: undefined;
|
|
828
1075
|
groups.get(category).push({
|
|
829
1076
|
key,
|
|
830
1077
|
name,
|
|
@@ -832,6 +1079,11 @@ export function parseSettingsSnapshot(json, calendarType, registry = defaultRegi
|
|
|
832
1079
|
labelKey: labelDef?.labelKey,
|
|
833
1080
|
descriptionKey: labelDef?.descriptionKey,
|
|
834
1081
|
value,
|
|
1082
|
+
uiType: entryDef?.uiType ?? 'TEXT_INPUT',
|
|
1083
|
+
...(entryDef?.options && { options: entryDef.options }),
|
|
1084
|
+
...(entryDef?.sliderConfig && { sliderConfig: entryDef.sliderConfig }),
|
|
1085
|
+
...(entryDef?.appMode && { appMode: entryDef.appMode }),
|
|
1086
|
+
...(entryDef?.calendarType && { calendarType: entryDef.calendarType }),
|
|
835
1087
|
});
|
|
836
1088
|
}
|
|
837
1089
|
// Determine which categories to show
|
|
@@ -871,6 +1123,45 @@ export function formatSettingValue(value) {
|
|
|
871
1123
|
}
|
|
872
1124
|
return String(value);
|
|
873
1125
|
}
|
|
1126
|
+
/**
|
|
1127
|
+
* Serialize a settings object (with mixed native types) into a
|
|
1128
|
+
* string-values-only snapshot suitable for pushing to a mobile device.
|
|
1129
|
+
*
|
|
1130
|
+
* Uses the registry's `serialize()` method for known keys so that
|
|
1131
|
+
* booleans become "true"/"false", numbers become digit strings, etc.
|
|
1132
|
+
* Unknown keys are converted with `String(value)`.
|
|
1133
|
+
*/
|
|
1134
|
+
export function serializeSettingsSnapshot(settings, registry) {
|
|
1135
|
+
const result = {};
|
|
1136
|
+
for (const [key, value] of Object.entries(settings)) {
|
|
1137
|
+
if (key in registry.entries) {
|
|
1138
|
+
result[key] = registry.serialize(key, value);
|
|
1139
|
+
}
|
|
1140
|
+
else {
|
|
1141
|
+
result[key] = String(value ?? '');
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
return result;
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* Deserialize a settings snapshot (string values from the server/DB) into
|
|
1148
|
+
* native-typed values using the registry.
|
|
1149
|
+
*
|
|
1150
|
+
* This ensures local state always contains native types (boolean, number, etc.)
|
|
1151
|
+
* so that subsequent `serializeSettingsSnapshot` calls produce correct results.
|
|
1152
|
+
*/
|
|
1153
|
+
export function deserializeSettingsSnapshot(snapshot, registry = defaultRegistry) {
|
|
1154
|
+
const result = {};
|
|
1155
|
+
for (const [key, value] of Object.entries(snapshot)) {
|
|
1156
|
+
if (typeof value === 'string' && key in registry.entries) {
|
|
1157
|
+
result[key] = registry.deserialize(key, value);
|
|
1158
|
+
}
|
|
1159
|
+
else {
|
|
1160
|
+
result[key] = value;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
return result;
|
|
1164
|
+
}
|
|
874
1165
|
// ---------------------------------------------------------------------------
|
|
875
1166
|
// Default registry instance (non-enrolled, system theme, nb locale)
|
|
876
1167
|
// ---------------------------------------------------------------------------
|