@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/src/index.ts
CHANGED
|
@@ -77,6 +77,47 @@ export type AllDayPreset = {
|
|
|
77
77
|
time: string;
|
|
78
78
|
};
|
|
79
79
|
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// UI type discriminator — determines which component renders each setting
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
export type SettingUiType =
|
|
85
|
+
| 'TOGGLE'
|
|
86
|
+
| 'SELECT'
|
|
87
|
+
| 'VOLUME_SLIDER'
|
|
88
|
+
| 'SLIDER'
|
|
89
|
+
| 'NUMBER_INPUT'
|
|
90
|
+
| 'TEXT_INPUT'
|
|
91
|
+
| 'PIN_INPUT'
|
|
92
|
+
| 'CUSTOM_THEME_PICKER'
|
|
93
|
+
| 'CUSTOM_LANGUAGE_PICKER'
|
|
94
|
+
| 'CUSTOM_CALENDAR_TYPE'
|
|
95
|
+
| 'CUSTOM_CLOCK_TYPE'
|
|
96
|
+
| 'CUSTOM_WEATHER_LOCATION'
|
|
97
|
+
| 'CUSTOM_IMAGE'
|
|
98
|
+
| 'CUSTOM_IMAGE_ARRAY'
|
|
99
|
+
| 'CUSTOM_CALENDAR_IDS'
|
|
100
|
+
| 'CUSTOM_REMINDER_PRESETS'
|
|
101
|
+
| 'CUSTOM_EVENT_FORM'
|
|
102
|
+
| 'CUSTOM_CHRONOLOGICAL_EVENT_FORM'
|
|
103
|
+
| 'CUSTOM_DISPLAY_DENSITY'
|
|
104
|
+
| 'CUSTOM_BRIGHTNESS'
|
|
105
|
+
| 'CUSTOM_SPLIT_VIEW_CONFIG'
|
|
106
|
+
| 'HIDDEN';
|
|
107
|
+
|
|
108
|
+
export type SettingOption<T = unknown> = {
|
|
109
|
+
/** The option value */
|
|
110
|
+
value: T;
|
|
111
|
+
/** i18n message key for the option label */
|
|
112
|
+
labelKey: string;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export type SliderConfig = {
|
|
116
|
+
min: number;
|
|
117
|
+
max: number;
|
|
118
|
+
step: number;
|
|
119
|
+
};
|
|
120
|
+
|
|
80
121
|
// ---------------------------------------------------------------------------
|
|
81
122
|
// Setting categories — used for UI grouping and profile organization
|
|
82
123
|
// ---------------------------------------------------------------------------
|
|
@@ -219,6 +260,8 @@ export type SettingsGroupDef = {
|
|
|
219
260
|
keys: string[];
|
|
220
261
|
/** Only show for this calendar type (undefined = always) */
|
|
221
262
|
calendarType?: CalendarType;
|
|
263
|
+
/** Only show for this app mode (undefined = always) */
|
|
264
|
+
appMode?: 'ENROLLED';
|
|
222
265
|
};
|
|
223
266
|
|
|
224
267
|
/**
|
|
@@ -242,7 +285,7 @@ export const SETTINGS_GROUPS: readonly SettingsGroupDef[] = [
|
|
|
242
285
|
id: 'calendarType',
|
|
243
286
|
labelKey: 'Settings.CalendarType',
|
|
244
287
|
icon: 'ArrowLeftRight',
|
|
245
|
-
keys: ['calendarView.type'
|
|
288
|
+
keys: ['calendarView.type'],
|
|
246
289
|
},
|
|
247
290
|
{
|
|
248
291
|
id: 'calendarView',
|
|
@@ -251,9 +294,9 @@ export const SETTINGS_GROUPS: readonly SettingsGroupDef[] = [
|
|
|
251
294
|
keys: [
|
|
252
295
|
'calendarView.showCalendarNames',
|
|
253
296
|
'calendarView.splitView',
|
|
254
|
-
'calendarView.calendarColumns',
|
|
255
297
|
'calendarView.dayViewZoom',
|
|
256
298
|
'calendarView.weekViewZoom',
|
|
299
|
+
'calendarView.calendarColumns',
|
|
257
300
|
],
|
|
258
301
|
},
|
|
259
302
|
{
|
|
@@ -267,9 +310,9 @@ export const SETTINGS_GROUPS: readonly SettingsGroupDef[] = [
|
|
|
267
310
|
labelKey: 'Settings.DateWeather',
|
|
268
311
|
icon: 'CloudSun',
|
|
269
312
|
keys: [
|
|
270
|
-
'calendarView.showWeatherOnEvents',
|
|
271
313
|
'calendarView.showWeatherOnTimeline',
|
|
272
314
|
'calendarView.weatherLocation',
|
|
315
|
+
'calendarView.showWeatherOnEvents',
|
|
273
316
|
],
|
|
274
317
|
},
|
|
275
318
|
{
|
|
@@ -279,13 +322,13 @@ export const SETTINGS_GROUPS: readonly SettingsGroupDef[] = [
|
|
|
279
322
|
keys: [
|
|
280
323
|
'sound.reminderVolume',
|
|
281
324
|
'sound.mediaVolume',
|
|
282
|
-
'sound.alarmSound',
|
|
283
325
|
'sound.reminderAlarmSound',
|
|
284
326
|
'sound.reminderAlarmTimeout',
|
|
285
327
|
'sound.allowCustomReminderSounds',
|
|
328
|
+
'notification.',
|
|
286
329
|
'sound.ttsEnabled',
|
|
287
330
|
'sound.ttsRate',
|
|
288
|
-
'
|
|
331
|
+
'sound.alarmSound',
|
|
289
332
|
],
|
|
290
333
|
},
|
|
291
334
|
{
|
|
@@ -293,17 +336,20 @@ export const SETTINGS_GROUPS: readonly SettingsGroupDef[] = [
|
|
|
293
336
|
labelKey: 'Settings.TimerTitle',
|
|
294
337
|
icon: 'Timer',
|
|
295
338
|
keys: [
|
|
296
|
-
'
|
|
339
|
+
'chronological.timer.',
|
|
340
|
+
'timer.',
|
|
297
341
|
'sound.timerAlarmSound',
|
|
298
342
|
'sound.timerAlarmTimeout',
|
|
299
|
-
'
|
|
343
|
+
'sound.timerVolume',
|
|
300
344
|
],
|
|
345
|
+
appMode: 'ENROLLED',
|
|
301
346
|
},
|
|
302
347
|
{
|
|
303
348
|
id: 'lockScreen',
|
|
304
349
|
labelKey: 'Settings.LockScreen',
|
|
305
350
|
icon: 'Lock',
|
|
306
351
|
keys: ['lockScreen.'],
|
|
352
|
+
appMode: 'ENROLLED',
|
|
307
353
|
},
|
|
308
354
|
{
|
|
309
355
|
id: 'touch',
|
|
@@ -344,6 +390,9 @@ export const EXCLUDED_DEVICE_SETTINGS: ReadonlySet<string> = new Set([
|
|
|
344
390
|
'device.id',
|
|
345
391
|
'device.devMenuEnabled',
|
|
346
392
|
'device.authWarningDismissTtlDays',
|
|
393
|
+
'notification.enabled',
|
|
394
|
+
'notification.notifyAllCalendars',
|
|
395
|
+
'notification.enabledCalendarIds',
|
|
347
396
|
'notification.hasBeenPrompted',
|
|
348
397
|
]);
|
|
349
398
|
|
|
@@ -602,6 +651,33 @@ function matchesGroup(key: string, group: SettingsGroupDef): boolean {
|
|
|
602
651
|
);
|
|
603
652
|
}
|
|
604
653
|
|
|
654
|
+
/** Return the index of the first matching key pattern in the group, for sorting. */
|
|
655
|
+
function groupKeyIndex(key: string, keys: readonly string[]): number {
|
|
656
|
+
for (let i = 0; i < keys.length; i++) {
|
|
657
|
+
const matcher = keys[i];
|
|
658
|
+
if (key === matcher || (matcher.endsWith('.') && key.startsWith(matcher))) {
|
|
659
|
+
return i;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
return keys.length;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Lazy-initialized map from setting key → declaration index in buildEntries.
|
|
667
|
+
* Used as a tiebreaker when multiple keys share the same prefix group index.
|
|
668
|
+
*/
|
|
669
|
+
let _registryKeyOrder: Map<string, number> | undefined;
|
|
670
|
+
function registryKeyOrder(): Map<string, number> {
|
|
671
|
+
if (!_registryKeyOrder) {
|
|
672
|
+
_registryKeyOrder = new Map();
|
|
673
|
+
const keys = Object.keys(buildEntries(DEFAULT_REGISTRY_CONFIG));
|
|
674
|
+
for (let i = 0; i < keys.length; i++) {
|
|
675
|
+
_registryKeyOrder.set(keys[i], i);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return _registryKeyOrder;
|
|
679
|
+
}
|
|
680
|
+
|
|
605
681
|
/**
|
|
606
682
|
* Group parsed setting entries into user-facing groups matching the mobile
|
|
607
683
|
* app's settings menu structure.
|
|
@@ -613,10 +689,11 @@ function matchesGroup(key: string, group: SettingsGroupDef): boolean {
|
|
|
613
689
|
export function groupSettingsForDevice(
|
|
614
690
|
allSettings: ParsedSettingEntry[],
|
|
615
691
|
calendarType: CalendarType,
|
|
692
|
+
appMode?: 'ENROLLED',
|
|
616
693
|
): { id: SettingsGroupId; labelKey: string; icon: string; settings: ParsedSettingEntry[] }[] {
|
|
617
|
-
// Filter excluded
|
|
694
|
+
// Filter excluded and hidden settings
|
|
618
695
|
const settings = allSettings.filter(
|
|
619
|
-
(s) => !EXCLUDED_DEVICE_SETTINGS.has(s.key),
|
|
696
|
+
(s) => !EXCLUDED_DEVICE_SETTINGS.has(s.key) && s.uiType !== 'HIDDEN',
|
|
620
697
|
);
|
|
621
698
|
|
|
622
699
|
// Calendar type filtering for event form keys
|
|
@@ -628,6 +705,14 @@ export function groupSettingsForDevice(
|
|
|
628
705
|
if (!isChronological && s.key.startsWith('chronologicalEventForm.')) {
|
|
629
706
|
return false;
|
|
630
707
|
}
|
|
708
|
+
// Per-setting calendar type restriction
|
|
709
|
+
if (s.calendarType && s.calendarType !== calendarType) {
|
|
710
|
+
return false;
|
|
711
|
+
}
|
|
712
|
+
// Per-setting app mode restriction
|
|
713
|
+
if (s.appMode && s.appMode !== appMode) {
|
|
714
|
+
return false;
|
|
715
|
+
}
|
|
631
716
|
return true;
|
|
632
717
|
});
|
|
633
718
|
|
|
@@ -645,6 +730,11 @@ export function groupSettingsForDevice(
|
|
|
645
730
|
continue;
|
|
646
731
|
}
|
|
647
732
|
|
|
733
|
+
// Skip groups not relevant for current app mode
|
|
734
|
+
if (group.appMode && group.appMode !== appMode) {
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
|
|
648
738
|
const matched = filteredSettings.filter(
|
|
649
739
|
(s) => !claimed.has(s.key) && matchesGroup(s.key, group),
|
|
650
740
|
);
|
|
@@ -652,6 +742,18 @@ export function groupSettingsForDevice(
|
|
|
652
742
|
continue;
|
|
653
743
|
}
|
|
654
744
|
|
|
745
|
+
// Sort matched settings by the order defined in group.keys,
|
|
746
|
+
// with registry declaration order as tiebreaker for prefix matches
|
|
747
|
+
const order = registryKeyOrder();
|
|
748
|
+
matched.sort((a, b) => {
|
|
749
|
+
const aIdx = groupKeyIndex(a.key, group.keys);
|
|
750
|
+
const bIdx = groupKeyIndex(b.key, group.keys);
|
|
751
|
+
if (aIdx !== bIdx) {
|
|
752
|
+
return aIdx - bIdx;
|
|
753
|
+
}
|
|
754
|
+
return (order.get(a.key) ?? Infinity) - (order.get(b.key) ?? Infinity);
|
|
755
|
+
});
|
|
756
|
+
|
|
655
757
|
for (const s of matched) {
|
|
656
758
|
claimed.add(s.key);
|
|
657
759
|
}
|
|
@@ -683,6 +785,16 @@ export type SettingDef<T = unknown> = {
|
|
|
683
785
|
default: T;
|
|
684
786
|
/** Whether this setting should be synced to the server. Default true. */
|
|
685
787
|
sync: boolean;
|
|
788
|
+
/** UI component type for rendering this setting */
|
|
789
|
+
uiType: SettingUiType;
|
|
790
|
+
/** Available options for SELECT-type settings */
|
|
791
|
+
options?: readonly SettingOption<T>[];
|
|
792
|
+
/** Slider configuration for SLIDER-type settings */
|
|
793
|
+
sliderConfig?: SliderConfig;
|
|
794
|
+
/** Only show this setting for enrolled/kiosk devices (undefined = always) */
|
|
795
|
+
appMode?: 'ENROLLED';
|
|
796
|
+
/** Only show this setting for a specific calendar type (undefined = always) */
|
|
797
|
+
calendarType?: CalendarType;
|
|
686
798
|
};
|
|
687
799
|
|
|
688
800
|
// ---------------------------------------------------------------------------
|
|
@@ -705,6 +817,39 @@ export const DEFAULT_REGISTRY_CONFIG: RegistryConfig = {
|
|
|
705
817
|
defaultLocale: 'nb',
|
|
706
818
|
};
|
|
707
819
|
|
|
820
|
+
// ---------------------------------------------------------------------------
|
|
821
|
+
// Shared option arrays (reused across multiple settings)
|
|
822
|
+
// ---------------------------------------------------------------------------
|
|
823
|
+
|
|
824
|
+
const ALARM_SOUND_OPTIONS: readonly SettingOption<AlarmSound>[] = [
|
|
825
|
+
{ value: 'none', labelKey: 'Settings.Option.AlarmSound.None' },
|
|
826
|
+
{ value: 'alarm1', labelKey: 'Settings.Option.AlarmSound.Alarm1' },
|
|
827
|
+
{ value: 'alarm2', labelKey: 'Settings.Option.AlarmSound.Alarm2' },
|
|
828
|
+
{ value: 'alarm3', labelKey: 'Settings.Option.AlarmSound.Alarm3' },
|
|
829
|
+
{ value: 'alarm4', labelKey: 'Settings.Option.AlarmSound.Alarm4' },
|
|
830
|
+
{ value: 'alarm5', labelKey: 'Settings.Option.AlarmSound.Alarm5' },
|
|
831
|
+
{ value: 'alarm6', labelKey: 'Settings.Option.AlarmSound.Alarm6' },
|
|
832
|
+
{ value: 'alarm7', labelKey: 'Settings.Option.AlarmSound.Alarm7' },
|
|
833
|
+
{ value: 'alarm8', labelKey: 'Settings.Option.AlarmSound.Alarm8' },
|
|
834
|
+
{ value: 'alarm9', labelKey: 'Settings.Option.AlarmSound.Alarm9' },
|
|
835
|
+
];
|
|
836
|
+
|
|
837
|
+
const ALARM_TIMEOUT_OPTIONS: readonly SettingOption<AlarmTimeout>[] = [
|
|
838
|
+
{ value: 1, labelKey: 'Settings.Option.AlarmTimeout.1min' },
|
|
839
|
+
{ value: 2, labelKey: 'Settings.Option.AlarmTimeout.2min' },
|
|
840
|
+
{ value: 3, labelKey: 'Settings.Option.AlarmTimeout.3min' },
|
|
841
|
+
{ value: 5, labelKey: 'Settings.Option.AlarmTimeout.5min' },
|
|
842
|
+
{ value: 10, labelKey: 'Settings.Option.AlarmTimeout.10min' },
|
|
843
|
+
];
|
|
844
|
+
|
|
845
|
+
const DAY_VIEW_ZOOM_OPTIONS: readonly SettingOption<CalendarDayViewCellZoom>[] = [
|
|
846
|
+
{ value: 15, labelKey: 'Settings.Option.DayViewZoom.15min' },
|
|
847
|
+
{ value: 30, labelKey: 'Settings.Option.DayViewZoom.30min' },
|
|
848
|
+
{ value: 60, labelKey: 'Settings.Option.DayViewZoom.1hour' },
|
|
849
|
+
];
|
|
850
|
+
|
|
851
|
+
const TIME_OF_DAY_SLIDER: SliderConfig = { min: 0, max: 23, step: 1 };
|
|
852
|
+
|
|
708
853
|
// ---------------------------------------------------------------------------
|
|
709
854
|
// Helper
|
|
710
855
|
// ---------------------------------------------------------------------------
|
|
@@ -713,9 +858,35 @@ function def<T>(
|
|
|
713
858
|
category: SettingsCategory,
|
|
714
859
|
type: SettingsDataType,
|
|
715
860
|
defaultValue: T,
|
|
716
|
-
|
|
861
|
+
uiType: SettingUiType,
|
|
862
|
+
extra?: {
|
|
863
|
+
sync?: boolean;
|
|
864
|
+
options?: readonly SettingOption<T>[];
|
|
865
|
+
sliderConfig?: SliderConfig;
|
|
866
|
+
appMode?: 'ENROLLED';
|
|
867
|
+
calendarType?: CalendarType;
|
|
868
|
+
},
|
|
717
869
|
): SettingDef<T> {
|
|
718
|
-
|
|
870
|
+
const result: SettingDef<T> = {
|
|
871
|
+
category,
|
|
872
|
+
type,
|
|
873
|
+
default: defaultValue,
|
|
874
|
+
sync: extra?.sync ?? true,
|
|
875
|
+
uiType,
|
|
876
|
+
};
|
|
877
|
+
if (extra?.options) {
|
|
878
|
+
result.options = extra.options;
|
|
879
|
+
}
|
|
880
|
+
if (extra?.sliderConfig) {
|
|
881
|
+
result.sliderConfig = extra.sliderConfig;
|
|
882
|
+
}
|
|
883
|
+
if (extra?.appMode) {
|
|
884
|
+
result.appMode = extra.appMode;
|
|
885
|
+
}
|
|
886
|
+
if (extra?.calendarType) {
|
|
887
|
+
result.calendarType = extra.calendarType;
|
|
888
|
+
}
|
|
889
|
+
return result;
|
|
719
890
|
}
|
|
720
891
|
|
|
721
892
|
// ---------------------------------------------------------------------------
|
|
@@ -731,9 +902,35 @@ function buildEntries(config: RegistryConfig) {
|
|
|
731
902
|
'appearance',
|
|
732
903
|
'string',
|
|
733
904
|
config.defaultTheme,
|
|
905
|
+
'CUSTOM_THEME_PICKER',
|
|
906
|
+
{
|
|
907
|
+
options: [
|
|
908
|
+
{ value: 'light', labelKey: 'Settings.Option.Theme.Light' },
|
|
909
|
+
{ value: 'dark', labelKey: 'Settings.Option.Theme.Dark' },
|
|
910
|
+
{ value: 'system', labelKey: 'Settings.Option.Theme.System' },
|
|
911
|
+
],
|
|
912
|
+
},
|
|
913
|
+
),
|
|
914
|
+
'appearance.clockType': def<ClockType>(
|
|
915
|
+
'appearance',
|
|
916
|
+
'string',
|
|
917
|
+
'digital',
|
|
918
|
+
'CUSTOM_CLOCK_TYPE',
|
|
919
|
+
{
|
|
920
|
+
options: [
|
|
921
|
+
{ value: 'digital', labelKey: 'Settings.Option.ClockType.Digital' },
|
|
922
|
+
{ value: 'analog', labelKey: 'Settings.Option.ClockType.Analog' },
|
|
923
|
+
],
|
|
924
|
+
appMode: 'ENROLLED',
|
|
925
|
+
},
|
|
926
|
+
),
|
|
927
|
+
'appearance.enableDayColors': def<boolean>(
|
|
928
|
+
'appearance',
|
|
929
|
+
'boolean',
|
|
930
|
+
false,
|
|
931
|
+
'TOGGLE',
|
|
932
|
+
{ calendarType: 'chronological' },
|
|
734
933
|
),
|
|
735
|
-
'appearance.clockType': def<ClockType>('appearance', 'string', 'digital'),
|
|
736
|
-
'appearance.enableDayColors': def<boolean>('appearance', 'boolean', false),
|
|
737
934
|
|
|
738
935
|
// ═══════════════════════════════════════════════════════════════════════
|
|
739
936
|
// Calendar view
|
|
@@ -742,152 +939,348 @@ function buildEntries(config: RegistryConfig) {
|
|
|
742
939
|
'calendarView',
|
|
743
940
|
'string',
|
|
744
941
|
'time-based',
|
|
942
|
+
'CUSTOM_CALENDAR_TYPE',
|
|
943
|
+
{
|
|
944
|
+
options: [
|
|
945
|
+
{
|
|
946
|
+
value: 'chronological',
|
|
947
|
+
labelKey: 'Settings.Option.CalendarType.Chronological',
|
|
948
|
+
},
|
|
949
|
+
{
|
|
950
|
+
value: 'time-based',
|
|
951
|
+
labelKey: 'Settings.Option.CalendarType.TimeBased',
|
|
952
|
+
},
|
|
953
|
+
],
|
|
954
|
+
},
|
|
955
|
+
),
|
|
956
|
+
'calendarView.view': def<CalendarViewMode>(
|
|
957
|
+
'calendarView',
|
|
958
|
+
'string',
|
|
959
|
+
'day',
|
|
960
|
+
'SELECT',
|
|
961
|
+
{
|
|
962
|
+
options: [
|
|
963
|
+
{ value: 'day', labelKey: 'Settings.Option.CalendarView.Day' },
|
|
964
|
+
{
|
|
965
|
+
value: '3-days',
|
|
966
|
+
labelKey: 'Settings.Option.CalendarView.3Days',
|
|
967
|
+
},
|
|
968
|
+
{
|
|
969
|
+
value: '5-days',
|
|
970
|
+
labelKey: 'Settings.Option.CalendarView.5Days',
|
|
971
|
+
},
|
|
972
|
+
{
|
|
973
|
+
value: '7-days',
|
|
974
|
+
labelKey: 'Settings.Option.CalendarView.7Days',
|
|
975
|
+
},
|
|
976
|
+
{ value: 'week', labelKey: 'Settings.Option.CalendarView.Week' },
|
|
977
|
+
{
|
|
978
|
+
value: 'month',
|
|
979
|
+
labelKey: 'Settings.Option.CalendarView.Month',
|
|
980
|
+
},
|
|
981
|
+
{
|
|
982
|
+
value: 'overview',
|
|
983
|
+
labelKey: 'Settings.Option.CalendarView.Overview',
|
|
984
|
+
},
|
|
985
|
+
],
|
|
986
|
+
},
|
|
745
987
|
),
|
|
746
|
-
'calendarView.view': def<CalendarViewMode>('calendarView', 'string', 'day'),
|
|
747
988
|
'calendarView.dayViewZoom': def<CalendarDayViewCellZoom>(
|
|
748
989
|
'calendarView',
|
|
749
990
|
'number',
|
|
750
991
|
60,
|
|
992
|
+
'SELECT',
|
|
993
|
+
{ options: DAY_VIEW_ZOOM_OPTIONS, calendarType: 'time-based' },
|
|
751
994
|
),
|
|
752
995
|
'calendarView.weekViewZoom': def<CalendarDayViewCellZoom>(
|
|
753
996
|
'calendarView',
|
|
754
997
|
'number',
|
|
755
998
|
60,
|
|
999
|
+
'SELECT',
|
|
1000
|
+
{ options: DAY_VIEW_ZOOM_OPTIONS, calendarType: 'time-based' },
|
|
1001
|
+
),
|
|
1002
|
+
'calendarView.splitView': def<boolean>(
|
|
1003
|
+
'calendarView',
|
|
1004
|
+
'boolean',
|
|
1005
|
+
false,
|
|
1006
|
+
'TOGGLE',
|
|
756
1007
|
),
|
|
757
|
-
'calendarView.splitView': def<boolean>('calendarView', 'boolean', false),
|
|
758
1008
|
'calendarView.showCalendarNames': def<boolean>(
|
|
759
1009
|
'calendarView',
|
|
760
1010
|
'boolean',
|
|
761
1011
|
true,
|
|
1012
|
+
'TOGGLE',
|
|
1013
|
+
),
|
|
1014
|
+
'calendarView.calendarColumns': def<unknown[]>(
|
|
1015
|
+
'calendarView',
|
|
1016
|
+
'json',
|
|
1017
|
+
[],
|
|
1018
|
+
'CUSTOM_SPLIT_VIEW_CONFIG',
|
|
762
1019
|
),
|
|
763
|
-
'calendarView.calendarColumns': def<unknown[]>('calendarView', 'json', []),
|
|
764
1020
|
'calendarView.autoReturnToTodayEnabled': def<boolean>(
|
|
765
1021
|
'calendarView',
|
|
766
1022
|
'boolean',
|
|
767
1023
|
config.isEnrolled,
|
|
1024
|
+
'TOGGLE',
|
|
768
1025
|
),
|
|
769
1026
|
'calendarView.autoReturnToTodayTimeoutSeconds': def<number>(
|
|
770
1027
|
'calendarView',
|
|
771
1028
|
'number',
|
|
772
1029
|
300,
|
|
1030
|
+
'SLIDER',
|
|
1031
|
+
{ sliderConfig: { min: 30, max: 600, step: 30 } },
|
|
773
1032
|
),
|
|
774
1033
|
'calendarView.showWeatherOnEvents': def<boolean>(
|
|
775
1034
|
'calendarView',
|
|
776
1035
|
'boolean',
|
|
777
1036
|
false,
|
|
1037
|
+
'TOGGLE',
|
|
778
1038
|
),
|
|
779
1039
|
'calendarView.showWeatherOnTimeline': def<boolean>(
|
|
780
1040
|
'calendarView',
|
|
781
1041
|
'boolean',
|
|
782
1042
|
false,
|
|
1043
|
+
'TOGGLE',
|
|
783
1044
|
),
|
|
784
1045
|
'calendarView.weatherLocation': def<WeatherLocation | null>(
|
|
785
1046
|
'calendarView',
|
|
786
1047
|
'json',
|
|
787
1048
|
null,
|
|
1049
|
+
'CUSTOM_WEATHER_LOCATION',
|
|
788
1050
|
),
|
|
789
1051
|
|
|
790
1052
|
// ═══════════════════════════════════════════════════════════════════════
|
|
791
1053
|
// Event form field visibility (time-based)
|
|
792
1054
|
// ═══════════════════════════════════════════════════════════════════════
|
|
793
|
-
'eventForm.recurrence': def<boolean>('eventForm', 'boolean', true),
|
|
794
|
-
'eventForm.
|
|
795
|
-
'eventForm.
|
|
796
|
-
'eventForm.
|
|
797
|
-
'eventForm.
|
|
798
|
-
'eventForm.description': def<boolean>('eventForm', 'boolean', true),
|
|
799
|
-
'eventForm.checklist': def<boolean>('eventForm', 'boolean', true),
|
|
800
|
-
'eventForm.images': def<boolean>('eventForm', 'boolean', false),
|
|
801
|
-
'eventForm.audioClips': def<boolean>('eventForm', 'boolean', false),
|
|
1055
|
+
'eventForm.recurrence': def<boolean>('eventForm', 'boolean', true, 'TOGGLE'),
|
|
1056
|
+
'eventForm.location': def<boolean>('eventForm', 'boolean', true, 'TOGGLE'),
|
|
1057
|
+
'eventForm.travelTime': def<boolean>('eventForm', 'boolean', false, 'TOGGLE'),
|
|
1058
|
+
'eventForm.reminders': def<boolean>('eventForm', 'boolean', true, 'TOGGLE'),
|
|
1059
|
+
'eventForm.emailReminders': def<boolean>('eventForm', 'boolean', false, 'TOGGLE'),
|
|
1060
|
+
'eventForm.description': def<boolean>('eventForm', 'boolean', true, 'TOGGLE'),
|
|
1061
|
+
'eventForm.checklist': def<boolean>('eventForm', 'boolean', true, 'TOGGLE'),
|
|
1062
|
+
'eventForm.images': def<boolean>('eventForm', 'boolean', false, 'TOGGLE'),
|
|
1063
|
+
'eventForm.audioClips': def<boolean>('eventForm', 'boolean', false, 'TOGGLE'),
|
|
802
1064
|
'eventForm.notificationReceivers': def<boolean>(
|
|
803
1065
|
'eventForm',
|
|
804
1066
|
'boolean',
|
|
805
1067
|
true,
|
|
1068
|
+
'TOGGLE',
|
|
806
1069
|
),
|
|
807
|
-
'eventForm.visibility': def<boolean>('eventForm', 'boolean', false),
|
|
1070
|
+
'eventForm.visibility': def<boolean>('eventForm', 'boolean', false, 'TOGGLE'),
|
|
808
1071
|
|
|
809
1072
|
// ═══════════════════════════════════════════════════════════════════════
|
|
810
1073
|
// Sound & alerts
|
|
811
1074
|
// ═══════════════════════════════════════════════════════════════════════
|
|
812
|
-
'sound.timerVolume': def<number>('sound', 'number', 0.5),
|
|
813
|
-
'sound.reminderVolume': def<number>('sound', 'number', 0.5),
|
|
814
|
-
'sound.mediaVolume': def<number>('sound', 'number', 0.5),
|
|
815
|
-
'sound.alarmSound': def<AlarmSound>(
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
'sound.
|
|
1075
|
+
'sound.timerVolume': def<number>('sound', 'number', 0.5, 'VOLUME_SLIDER'),
|
|
1076
|
+
'sound.reminderVolume': def<number>('sound', 'number', 0.5, 'VOLUME_SLIDER', { appMode: 'ENROLLED' }),
|
|
1077
|
+
'sound.mediaVolume': def<number>('sound', 'number', 0.5, 'VOLUME_SLIDER', { appMode: 'ENROLLED' }),
|
|
1078
|
+
'sound.alarmSound': def<AlarmSound>(
|
|
1079
|
+
'sound',
|
|
1080
|
+
'string',
|
|
1081
|
+
'alarm1',
|
|
1082
|
+
'SELECT',
|
|
1083
|
+
{ options: ALARM_SOUND_OPTIONS },
|
|
1084
|
+
),
|
|
1085
|
+
'sound.reminderAlarmSound': def<AlarmSound>(
|
|
1086
|
+
'sound',
|
|
1087
|
+
'string',
|
|
1088
|
+
'alarm1',
|
|
1089
|
+
'SELECT',
|
|
1090
|
+
{ options: ALARM_SOUND_OPTIONS },
|
|
1091
|
+
),
|
|
1092
|
+
'sound.timerAlarmSound': def<AlarmSound>(
|
|
1093
|
+
'sound',
|
|
1094
|
+
'string',
|
|
1095
|
+
'alarm1',
|
|
1096
|
+
'SELECT',
|
|
1097
|
+
{ options: ALARM_SOUND_OPTIONS },
|
|
1098
|
+
),
|
|
1099
|
+
'sound.timerAlarmTimeout': def<AlarmTimeout>(
|
|
1100
|
+
'sound',
|
|
1101
|
+
'number',
|
|
1102
|
+
3,
|
|
1103
|
+
'SELECT',
|
|
1104
|
+
{ options: ALARM_TIMEOUT_OPTIONS },
|
|
1105
|
+
),
|
|
1106
|
+
'sound.reminderAlarmTimeout': def<AlarmTimeout>(
|
|
1107
|
+
'sound',
|
|
1108
|
+
'number',
|
|
1109
|
+
3,
|
|
1110
|
+
'SELECT',
|
|
1111
|
+
{ options: ALARM_TIMEOUT_OPTIONS },
|
|
1112
|
+
),
|
|
1113
|
+
'sound.allowCustomReminderSounds': def<boolean>(
|
|
1114
|
+
'sound',
|
|
1115
|
+
'boolean',
|
|
1116
|
+
false,
|
|
1117
|
+
'TOGGLE',
|
|
1118
|
+
),
|
|
1119
|
+
'sound.ttsEnabled': def<boolean>(
|
|
1120
|
+
'sound',
|
|
1121
|
+
'boolean',
|
|
1122
|
+
config.isEnrolled,
|
|
1123
|
+
'TOGGLE',
|
|
1124
|
+
{ appMode: 'ENROLLED' },
|
|
1125
|
+
),
|
|
1126
|
+
'sound.ttsRate': def<number>(
|
|
1127
|
+
'sound',
|
|
1128
|
+
'number',
|
|
1129
|
+
1.0,
|
|
1130
|
+
'SLIDER',
|
|
1131
|
+
{ sliderConfig: { min: 0.5, max: 2, step: 0.1 }, appMode: 'ENROLLED' },
|
|
1132
|
+
),
|
|
823
1133
|
|
|
824
1134
|
// ═══════════════════════════════════════════════════════════════════════
|
|
825
1135
|
// Timer
|
|
826
1136
|
// ═══════════════════════════════════════════════════════════════════════
|
|
827
|
-
'timer.showTimeRemaining': def<boolean>('timer', 'boolean', true),
|
|
828
|
-
'timer.showEndTime': def<boolean>('timer', 'boolean', true),
|
|
829
|
-
'timer.showRestartButton': def<boolean>('timer', 'boolean', true),
|
|
830
|
-
'timer.showPauseButton': def<boolean>('timer', 'boolean', true),
|
|
1137
|
+
'timer.showTimeRemaining': def<boolean>('timer', 'boolean', true, 'TOGGLE'),
|
|
1138
|
+
'timer.showEndTime': def<boolean>('timer', 'boolean', true, 'TOGGLE'),
|
|
1139
|
+
'timer.showRestartButton': def<boolean>('timer', 'boolean', true, 'TOGGLE'),
|
|
1140
|
+
'timer.showPauseButton': def<boolean>('timer', 'boolean', true, 'TOGGLE'),
|
|
831
1141
|
|
|
832
1142
|
// ═══════════════════════════════════════════════════════════════════════
|
|
833
1143
|
// Lock screen
|
|
834
1144
|
// ═══════════════════════════════════════════════════════════════════════
|
|
835
|
-
'lockScreen.pin': def<string>('lockScreen', 'string', ''),
|
|
836
1145
|
'lockScreen.inactivityLockEnabled': def<boolean>(
|
|
837
1146
|
'lockScreen',
|
|
838
1147
|
'boolean',
|
|
839
1148
|
false,
|
|
1149
|
+
'TOGGLE',
|
|
840
1150
|
),
|
|
841
1151
|
'lockScreen.inactivityTimeoutMinutes': def<InactivityTimeoutMinutes>(
|
|
842
1152
|
'lockScreen',
|
|
843
1153
|
'number',
|
|
844
1154
|
5,
|
|
845
|
-
|
|
1155
|
+
'SELECT',
|
|
1156
|
+
{
|
|
1157
|
+
options: [
|
|
1158
|
+
{ value: 1, labelKey: 'Settings.Option.InactivityTimeout.1min' },
|
|
1159
|
+
{ value: 5, labelKey: 'Settings.Option.InactivityTimeout.5min' },
|
|
1160
|
+
{ value: 10, labelKey: 'Settings.Option.InactivityTimeout.10min' },
|
|
1161
|
+
{ value: 15, labelKey: 'Settings.Option.InactivityTimeout.15min' },
|
|
1162
|
+
{ value: 30, labelKey: 'Settings.Option.InactivityTimeout.30min' },
|
|
1163
|
+
{ value: 45, labelKey: 'Settings.Option.InactivityTimeout.45min' },
|
|
1164
|
+
],
|
|
1165
|
+
},
|
|
1166
|
+
),
|
|
1167
|
+
'lockScreen.pin': def<string>('lockScreen', 'string', '', 'PIN_INPUT'),
|
|
846
1168
|
'lockScreen.clockDisplay': def<LockScreenClockDisplay>(
|
|
847
1169
|
'lockScreen',
|
|
848
1170
|
'string',
|
|
849
1171
|
'digital',
|
|
1172
|
+
'SELECT',
|
|
1173
|
+
{
|
|
1174
|
+
options: [
|
|
1175
|
+
{ value: 'none', labelKey: 'Settings.Option.ClockDisplay.None' },
|
|
1176
|
+
{
|
|
1177
|
+
value: 'digital',
|
|
1178
|
+
labelKey: 'Settings.Option.ClockDisplay.Digital',
|
|
1179
|
+
},
|
|
1180
|
+
{
|
|
1181
|
+
value: 'analog',
|
|
1182
|
+
labelKey: 'Settings.Option.ClockDisplay.Analog',
|
|
1183
|
+
},
|
|
1184
|
+
],
|
|
1185
|
+
},
|
|
1186
|
+
),
|
|
1187
|
+
'lockScreen.showHourNumbers': def<boolean>(
|
|
1188
|
+
'lockScreen',
|
|
1189
|
+
'boolean',
|
|
1190
|
+
true,
|
|
1191
|
+
'TOGGLE',
|
|
1192
|
+
),
|
|
1193
|
+
'lockScreen.showDate': def<boolean>(
|
|
1194
|
+
'lockScreen',
|
|
1195
|
+
'boolean',
|
|
1196
|
+
true,
|
|
1197
|
+
'TOGGLE',
|
|
850
1198
|
),
|
|
851
|
-
'lockScreen.showDate': def<boolean>('lockScreen', 'boolean', true),
|
|
852
|
-
'lockScreen.showHourNumbers': def<boolean>('lockScreen', 'boolean', true),
|
|
853
1199
|
'lockScreen.imageMode': def<LockScreenImageMode>(
|
|
854
1200
|
'lockScreen',
|
|
855
1201
|
'string',
|
|
856
1202
|
'none',
|
|
1203
|
+
'SELECT',
|
|
1204
|
+
{
|
|
1205
|
+
options: [
|
|
1206
|
+
{ value: 'none', labelKey: 'Settings.Option.ImageMode.None' },
|
|
1207
|
+
{
|
|
1208
|
+
value: 'background',
|
|
1209
|
+
labelKey: 'Settings.Option.ImageMode.Background',
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
value: 'photoFrame',
|
|
1213
|
+
labelKey: 'Settings.Option.ImageMode.PhotoFrame',
|
|
1214
|
+
},
|
|
1215
|
+
],
|
|
1216
|
+
},
|
|
857
1217
|
),
|
|
858
1218
|
'lockScreen.backgroundImage': def<string | null>(
|
|
859
1219
|
'lockScreen',
|
|
860
1220
|
'json',
|
|
861
1221
|
null,
|
|
862
|
-
|
|
863
|
-
'lockScreen.photoFrameImages': def<LockScreenImage[]>(
|
|
864
|
-
'lockScreen',
|
|
865
|
-
'json',
|
|
866
|
-
[],
|
|
1222
|
+
'CUSTOM_IMAGE',
|
|
867
1223
|
),
|
|
868
1224
|
'lockScreen.photoFrameIntervalSeconds': def<PhotoFrameIntervalSeconds>(
|
|
869
1225
|
'lockScreen',
|
|
870
1226
|
'number',
|
|
871
1227
|
60,
|
|
1228
|
+
'SELECT',
|
|
1229
|
+
{
|
|
1230
|
+
options: [
|
|
1231
|
+
{ value: 30, labelKey: 'Settings.Option.PhotoFrameInterval.30sec' },
|
|
1232
|
+
{ value: 60, labelKey: 'Settings.Option.PhotoFrameInterval.1min' },
|
|
1233
|
+
{ value: 120, labelKey: 'Settings.Option.PhotoFrameInterval.2min' },
|
|
1234
|
+
{ value: 300, labelKey: 'Settings.Option.PhotoFrameInterval.5min' },
|
|
1235
|
+
],
|
|
1236
|
+
},
|
|
1237
|
+
),
|
|
1238
|
+
|
|
1239
|
+
'lockScreen.photoFrameImages': def<LockScreenImage[]>(
|
|
1240
|
+
'lockScreen',
|
|
1241
|
+
'json',
|
|
1242
|
+
[],
|
|
1243
|
+
'CUSTOM_IMAGE_ARRAY',
|
|
872
1244
|
),
|
|
873
1245
|
|
|
874
1246
|
// ═══════════════════════════════════════════════════════════════════════
|
|
875
1247
|
// Touch / gestures
|
|
876
1248
|
// ═══════════════════════════════════════════════════════════════════════
|
|
877
|
-
'touch.enableTapToCreate': def<boolean>('touch', 'boolean', false),
|
|
878
|
-
'touch.enableDragDrop': def<boolean>('touch', 'boolean', false),
|
|
1249
|
+
'touch.enableTapToCreate': def<boolean>('touch', 'boolean', false, 'TOGGLE'),
|
|
1250
|
+
'touch.enableDragDrop': def<boolean>('touch', 'boolean', false, 'TOGGLE'),
|
|
879
1251
|
|
|
880
1252
|
// ═══════════════════════════════════════════════════════════════════════
|
|
881
1253
|
// Device (not synced unless noted)
|
|
882
1254
|
// ═══════════════════════════════════════════════════════════════════════
|
|
883
|
-
'device.id': def<string>('device', 'string', '', false),
|
|
884
|
-
'device.timePickerMode': def<TimePickerMode>(
|
|
885
|
-
|
|
1255
|
+
'device.id': def<string>('device', 'string', '', 'HIDDEN', { sync: false }),
|
|
1256
|
+
'device.timePickerMode': def<TimePickerMode>(
|
|
1257
|
+
'device',
|
|
1258
|
+
'string',
|
|
1259
|
+
'dials',
|
|
1260
|
+
'SELECT',
|
|
1261
|
+
{
|
|
1262
|
+
options: [
|
|
1263
|
+
{ value: 'dials', labelKey: 'Settings.Option.TimePickerMode.Dials' },
|
|
1264
|
+
{
|
|
1265
|
+
value: 'keypad',
|
|
1266
|
+
labelKey: 'Settings.Option.TimePickerMode.Keypad',
|
|
1267
|
+
},
|
|
1268
|
+
],
|
|
1269
|
+
},
|
|
1270
|
+
),
|
|
1271
|
+
'device.devMenuEnabled': def<boolean>(
|
|
1272
|
+
'device',
|
|
1273
|
+
'boolean',
|
|
1274
|
+
false,
|
|
1275
|
+
'HIDDEN',
|
|
1276
|
+
{ sync: false },
|
|
1277
|
+
),
|
|
886
1278
|
'device.authWarningDismissTtlDays': def<number>(
|
|
887
1279
|
'device',
|
|
888
1280
|
'number',
|
|
889
1281
|
3,
|
|
890
|
-
|
|
1282
|
+
'HIDDEN',
|
|
1283
|
+
{ sync: false },
|
|
891
1284
|
),
|
|
892
1285
|
|
|
893
1286
|
// ═══════════════════════════════════════════════════════════════════════
|
|
@@ -897,27 +1290,42 @@ function buildEntries(config: RegistryConfig) {
|
|
|
897
1290
|
'language',
|
|
898
1291
|
'string',
|
|
899
1292
|
config.defaultLocale,
|
|
1293
|
+
'CUSTOM_LANGUAGE_PICKER',
|
|
1294
|
+
{
|
|
1295
|
+
options: [
|
|
1296
|
+
{ value: 'nb', labelKey: 'Settings.Option.Language.Norwegian' },
|
|
1297
|
+
{ value: 'en', labelKey: 'Settings.Option.Language.English' },
|
|
1298
|
+
],
|
|
1299
|
+
},
|
|
900
1300
|
),
|
|
901
1301
|
|
|
902
1302
|
// ═══════════════════════════════════════════════════════════════════════
|
|
903
1303
|
// Notifications
|
|
904
1304
|
// ═══════════════════════════════════════════════════════════════════════
|
|
905
|
-
'notification.enabled': def<boolean>(
|
|
1305
|
+
'notification.enabled': def<boolean>(
|
|
1306
|
+
'notification',
|
|
1307
|
+
'boolean',
|
|
1308
|
+
false,
|
|
1309
|
+
'TOGGLE',
|
|
1310
|
+
),
|
|
906
1311
|
'notification.notifyAllCalendars': def<boolean>(
|
|
907
1312
|
'notification',
|
|
908
1313
|
'boolean',
|
|
909
1314
|
true,
|
|
1315
|
+
'TOGGLE',
|
|
910
1316
|
),
|
|
911
1317
|
'notification.enabledCalendarIds': def<string[]>(
|
|
912
1318
|
'notification',
|
|
913
1319
|
'json',
|
|
914
1320
|
[],
|
|
1321
|
+
'CUSTOM_CALENDAR_IDS',
|
|
915
1322
|
),
|
|
916
1323
|
'notification.hasBeenPrompted': def<boolean>(
|
|
917
1324
|
'notification',
|
|
918
1325
|
'boolean',
|
|
919
1326
|
false,
|
|
920
|
-
|
|
1327
|
+
'HIDDEN',
|
|
1328
|
+
{ sync: false },
|
|
921
1329
|
),
|
|
922
1330
|
|
|
923
1331
|
// ═══════════════════════════════════════════════════════════════════════
|
|
@@ -928,225 +1336,292 @@ function buildEntries(config: RegistryConfig) {
|
|
|
928
1336
|
'chronological',
|
|
929
1337
|
'boolean',
|
|
930
1338
|
true,
|
|
1339
|
+
'TOGGLE',
|
|
931
1340
|
),
|
|
932
|
-
'chronological.header.
|
|
1341
|
+
'chronological.header.showCurrentYearInDate': def<boolean>(
|
|
933
1342
|
'chronological',
|
|
934
1343
|
'boolean',
|
|
935
|
-
|
|
1344
|
+
false,
|
|
1345
|
+
'TOGGLE',
|
|
936
1346
|
),
|
|
937
|
-
'chronological.header.
|
|
1347
|
+
'chronological.header.showClock': def<boolean>(
|
|
938
1348
|
'chronological',
|
|
939
1349
|
'boolean',
|
|
940
|
-
|
|
1350
|
+
true,
|
|
1351
|
+
'TOGGLE',
|
|
941
1352
|
),
|
|
942
1353
|
'chronological.header.showTimeOfDay': def<boolean>(
|
|
943
1354
|
'chronological',
|
|
944
1355
|
'boolean',
|
|
945
1356
|
false,
|
|
1357
|
+
'TOGGLE',
|
|
946
1358
|
),
|
|
947
1359
|
// Footer
|
|
948
1360
|
'chronological.footer.showMenuButton': def<boolean>(
|
|
949
1361
|
'chronological',
|
|
950
1362
|
'boolean',
|
|
951
1363
|
true,
|
|
1364
|
+
'TOGGLE',
|
|
952
1365
|
),
|
|
953
1366
|
'chronological.footer.showViewSwitcherDay': def<boolean>(
|
|
954
1367
|
'chronological',
|
|
955
1368
|
'boolean',
|
|
956
1369
|
true,
|
|
1370
|
+
'TOGGLE',
|
|
957
1371
|
),
|
|
958
1372
|
'chronological.footer.showViewSwitcherWeek': def<boolean>(
|
|
959
1373
|
'chronological',
|
|
960
1374
|
'boolean',
|
|
961
1375
|
true,
|
|
1376
|
+
'TOGGLE',
|
|
962
1377
|
),
|
|
963
1378
|
'chronological.footer.showViewSwitcherMonth': def<boolean>(
|
|
964
1379
|
'chronological',
|
|
965
1380
|
'boolean',
|
|
966
1381
|
true,
|
|
1382
|
+
'TOGGLE',
|
|
967
1383
|
),
|
|
968
1384
|
'chronological.footer.showTimerButton': def<boolean>(
|
|
969
1385
|
'chronological',
|
|
970
1386
|
'boolean',
|
|
971
1387
|
true,
|
|
1388
|
+
'TOGGLE',
|
|
972
1389
|
),
|
|
973
1390
|
'chronological.footer.showNewEventButton': def<boolean>(
|
|
974
1391
|
'chronological',
|
|
975
1392
|
'boolean',
|
|
976
1393
|
true,
|
|
1394
|
+
'TOGGLE',
|
|
977
1395
|
),
|
|
978
1396
|
'chronological.footer.showSettingsButton': def<boolean>(
|
|
979
1397
|
'chronological',
|
|
980
1398
|
'boolean',
|
|
981
1399
|
true,
|
|
1400
|
+
'TOGGLE',
|
|
982
1401
|
),
|
|
983
1402
|
// Timer features
|
|
984
1403
|
'chronological.timer.showNewCountdown': def<boolean>(
|
|
985
1404
|
'chronological',
|
|
986
1405
|
'boolean',
|
|
987
1406
|
true,
|
|
1407
|
+
'TOGGLE',
|
|
988
1408
|
),
|
|
989
1409
|
'chronological.timer.showFromTemplate': def<boolean>(
|
|
990
1410
|
'chronological',
|
|
991
1411
|
'boolean',
|
|
992
1412
|
true,
|
|
1413
|
+
'TOGGLE',
|
|
993
1414
|
),
|
|
994
|
-
'chronological.timer.
|
|
1415
|
+
'chronological.timer.showAddTemplate': def<boolean>(
|
|
995
1416
|
'chronological',
|
|
996
1417
|
'boolean',
|
|
997
1418
|
true,
|
|
1419
|
+
'TOGGLE',
|
|
998
1420
|
),
|
|
999
|
-
'chronological.timer.
|
|
1421
|
+
'chronological.timer.showEditTemplate': def<boolean>(
|
|
1000
1422
|
'chronological',
|
|
1001
1423
|
'boolean',
|
|
1002
1424
|
true,
|
|
1425
|
+
'TOGGLE',
|
|
1003
1426
|
),
|
|
1004
|
-
'chronological.timer.
|
|
1427
|
+
'chronological.timer.showDeleteTemplate': def<boolean>(
|
|
1005
1428
|
'chronological',
|
|
1006
1429
|
'boolean',
|
|
1007
1430
|
true,
|
|
1431
|
+
'TOGGLE',
|
|
1008
1432
|
),
|
|
1009
1433
|
// Menu
|
|
1010
1434
|
'chronological.menu.showSettingsButton': def<boolean>(
|
|
1011
1435
|
'chronological',
|
|
1012
1436
|
'boolean',
|
|
1013
1437
|
true,
|
|
1438
|
+
'TOGGLE',
|
|
1014
1439
|
),
|
|
1015
1440
|
// Quick settings
|
|
1016
1441
|
'chronological.quickSettings.showTimerVolume': def<boolean>(
|
|
1017
1442
|
'chronological',
|
|
1018
1443
|
'boolean',
|
|
1019
1444
|
true,
|
|
1445
|
+
'TOGGLE',
|
|
1020
1446
|
),
|
|
1021
1447
|
'chronological.quickSettings.showReminderVolume': def<boolean>(
|
|
1022
1448
|
'chronological',
|
|
1023
1449
|
'boolean',
|
|
1024
1450
|
true,
|
|
1451
|
+
'TOGGLE',
|
|
1025
1452
|
),
|
|
1026
1453
|
'chronological.quickSettings.showMediaVolume': def<boolean>(
|
|
1027
1454
|
'chronological',
|
|
1028
1455
|
'boolean',
|
|
1029
1456
|
true,
|
|
1457
|
+
'TOGGLE',
|
|
1030
1458
|
),
|
|
1031
1459
|
'chronological.quickSettings.showBrightness': def<boolean>(
|
|
1032
1460
|
'chronological',
|
|
1033
1461
|
'boolean',
|
|
1034
1462
|
true,
|
|
1463
|
+
'TOGGLE',
|
|
1035
1464
|
),
|
|
1036
1465
|
'chronological.quickSettings.showLockScreen': def<boolean>(
|
|
1037
1466
|
'chronological',
|
|
1038
1467
|
'boolean',
|
|
1039
1468
|
true,
|
|
1469
|
+
'TOGGLE',
|
|
1040
1470
|
),
|
|
1041
1471
|
// Time-of-day periods
|
|
1042
1472
|
'chronological.timeOfDay.morningStart': def<number>(
|
|
1043
1473
|
'chronological',
|
|
1044
1474
|
'number',
|
|
1045
1475
|
6,
|
|
1476
|
+
'SLIDER',
|
|
1477
|
+
{ sliderConfig: TIME_OF_DAY_SLIDER },
|
|
1046
1478
|
),
|
|
1047
1479
|
'chronological.timeOfDay.forenoonStart': def<number>(
|
|
1048
1480
|
'chronological',
|
|
1049
1481
|
'number',
|
|
1050
1482
|
9,
|
|
1483
|
+
'SLIDER',
|
|
1484
|
+
{ sliderConfig: TIME_OF_DAY_SLIDER },
|
|
1051
1485
|
),
|
|
1052
1486
|
'chronological.timeOfDay.afternoonStart': def<number>(
|
|
1053
1487
|
'chronological',
|
|
1054
1488
|
'number',
|
|
1055
1489
|
12,
|
|
1490
|
+
'SLIDER',
|
|
1491
|
+
{ sliderConfig: TIME_OF_DAY_SLIDER },
|
|
1056
1492
|
),
|
|
1057
1493
|
'chronological.timeOfDay.eveningStart': def<number>(
|
|
1058
1494
|
'chronological',
|
|
1059
1495
|
'number',
|
|
1060
1496
|
18,
|
|
1497
|
+
'SLIDER',
|
|
1498
|
+
{ sliderConfig: TIME_OF_DAY_SLIDER },
|
|
1061
1499
|
),
|
|
1062
1500
|
'chronological.timeOfDay.nightStart': def<number>(
|
|
1063
1501
|
'chronological',
|
|
1064
1502
|
'number',
|
|
1065
1503
|
0,
|
|
1504
|
+
'SLIDER',
|
|
1505
|
+
{ sliderConfig: TIME_OF_DAY_SLIDER },
|
|
1066
1506
|
),
|
|
1067
1507
|
|
|
1068
1508
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1069
1509
|
// Chronological event form
|
|
1070
1510
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1071
|
-
//
|
|
1072
|
-
'chronologicalEventForm.
|
|
1511
|
+
// Fixed fields
|
|
1512
|
+
'chronologicalEventForm.fixedField.allDay': def<boolean>(
|
|
1073
1513
|
'chronologicalEventForm',
|
|
1074
1514
|
'boolean',
|
|
1075
1515
|
true,
|
|
1516
|
+
'TOGGLE',
|
|
1076
1517
|
),
|
|
1077
|
-
'chronologicalEventForm.
|
|
1518
|
+
'chronologicalEventForm.fixedField.endTime': def<boolean>(
|
|
1078
1519
|
'chronologicalEventForm',
|
|
1079
1520
|
'boolean',
|
|
1080
1521
|
true,
|
|
1522
|
+
'TOGGLE',
|
|
1081
1523
|
),
|
|
1082
|
-
'chronologicalEventForm.
|
|
1524
|
+
'chronologicalEventForm.fixedField.visibility': def<boolean>(
|
|
1083
1525
|
'chronologicalEventForm',
|
|
1084
1526
|
'boolean',
|
|
1085
1527
|
true,
|
|
1528
|
+
'TOGGLE',
|
|
1086
1529
|
),
|
|
1087
|
-
|
|
1530
|
+
// Toggleable fields
|
|
1531
|
+
'chronologicalEventForm.field.acknowledge': def<boolean>(
|
|
1088
1532
|
'chronologicalEventForm',
|
|
1089
1533
|
'boolean',
|
|
1090
1534
|
true,
|
|
1535
|
+
'TOGGLE',
|
|
1091
1536
|
),
|
|
1092
|
-
'chronologicalEventForm.field.
|
|
1537
|
+
'chronologicalEventForm.field.description': def<boolean>(
|
|
1093
1538
|
'chronologicalEventForm',
|
|
1094
1539
|
'boolean',
|
|
1095
1540
|
true,
|
|
1541
|
+
'TOGGLE',
|
|
1096
1542
|
),
|
|
1097
|
-
'chronologicalEventForm.field.
|
|
1543
|
+
'chronologicalEventForm.field.recurrence': def<boolean>(
|
|
1098
1544
|
'chronologicalEventForm',
|
|
1099
1545
|
'boolean',
|
|
1100
1546
|
true,
|
|
1547
|
+
'TOGGLE',
|
|
1101
1548
|
),
|
|
1102
|
-
'chronologicalEventForm.field.
|
|
1549
|
+
'chronologicalEventForm.field.checklist': def<boolean>(
|
|
1103
1550
|
'chronologicalEventForm',
|
|
1104
1551
|
'boolean',
|
|
1105
1552
|
true,
|
|
1553
|
+
'TOGGLE',
|
|
1106
1554
|
),
|
|
1107
|
-
|
|
1108
|
-
'chronologicalEventForm.fixedField.allDay': def<boolean>(
|
|
1555
|
+
'chronologicalEventForm.field.extraImages': def<boolean>(
|
|
1109
1556
|
'chronologicalEventForm',
|
|
1110
1557
|
'boolean',
|
|
1111
1558
|
true,
|
|
1559
|
+
'TOGGLE',
|
|
1112
1560
|
),
|
|
1113
|
-
'chronologicalEventForm.
|
|
1561
|
+
'chronologicalEventForm.field.reminders': def<boolean>(
|
|
1114
1562
|
'chronologicalEventForm',
|
|
1115
1563
|
'boolean',
|
|
1116
1564
|
true,
|
|
1565
|
+
'TOGGLE',
|
|
1117
1566
|
),
|
|
1118
|
-
'chronologicalEventForm.
|
|
1567
|
+
'chronologicalEventForm.field.audioClips': def<boolean>(
|
|
1119
1568
|
'chronologicalEventForm',
|
|
1120
1569
|
'boolean',
|
|
1121
1570
|
true,
|
|
1571
|
+
'TOGGLE',
|
|
1122
1572
|
),
|
|
1123
1573
|
// Suggest end time
|
|
1124
1574
|
'chronologicalEventForm.suggestEndTime.enabled': def<boolean>(
|
|
1125
1575
|
'chronologicalEventForm',
|
|
1126
1576
|
'boolean',
|
|
1127
1577
|
false,
|
|
1578
|
+
'TOGGLE',
|
|
1128
1579
|
),
|
|
1129
1580
|
'chronologicalEventForm.suggestEndTime.value': def<number>(
|
|
1130
1581
|
'chronologicalEventForm',
|
|
1131
1582
|
'number',
|
|
1132
1583
|
30,
|
|
1584
|
+
'SLIDER',
|
|
1585
|
+
{ sliderConfig: { min: 5, max: 480, step: 5 } },
|
|
1133
1586
|
),
|
|
1134
1587
|
'chronologicalEventForm.suggestEndTime.unit': def<SuggestEndTimeUnit>(
|
|
1135
1588
|
'chronologicalEventForm',
|
|
1136
1589
|
'string',
|
|
1137
1590
|
'minutes',
|
|
1591
|
+
'SELECT',
|
|
1592
|
+
{
|
|
1593
|
+
options: [
|
|
1594
|
+
{
|
|
1595
|
+
value: 'minutes',
|
|
1596
|
+
labelKey: 'Settings.Option.SuggestEndTimeUnit.Minutes',
|
|
1597
|
+
},
|
|
1598
|
+
{
|
|
1599
|
+
value: 'hours',
|
|
1600
|
+
labelKey: 'Settings.Option.SuggestEndTimeUnit.Hours',
|
|
1601
|
+
},
|
|
1602
|
+
],
|
|
1603
|
+
},
|
|
1138
1604
|
),
|
|
1139
1605
|
// Default visibility
|
|
1140
1606
|
'chronologicalEventForm.defaultVisibility': def<EventVisibility>(
|
|
1141
1607
|
'chronologicalEventForm',
|
|
1142
1608
|
'string',
|
|
1143
1609
|
'Private',
|
|
1610
|
+
'SELECT',
|
|
1611
|
+
{
|
|
1612
|
+
options: [
|
|
1613
|
+
{ value: 'Public', labelKey: 'Settings.Option.Visibility.Public' },
|
|
1614
|
+
{ value: 'Private', labelKey: 'Settings.Option.Visibility.Private' },
|
|
1615
|
+
{ value: 'Custom', labelKey: 'Settings.Option.Visibility.Custom' },
|
|
1616
|
+
],
|
|
1617
|
+
},
|
|
1144
1618
|
),
|
|
1145
1619
|
// Reminder presets
|
|
1146
1620
|
'chronologicalEventForm.reminderPresets.timed': def<number[]>(
|
|
1147
1621
|
'chronologicalEventForm',
|
|
1148
1622
|
'json',
|
|
1149
1623
|
[5, 15, 30, 60, 120, 1440],
|
|
1624
|
+
'CUSTOM_REMINDER_PRESETS',
|
|
1150
1625
|
),
|
|
1151
1626
|
'chronologicalEventForm.reminderPresets.allDay': def<AllDayPreset[]>(
|
|
1152
1627
|
'chronologicalEventForm',
|
|
@@ -1156,6 +1631,7 @@ function buildEntries(config: RegistryConfig) {
|
|
|
1156
1631
|
{ daysBefore: 1, time: '18:00' },
|
|
1157
1632
|
{ daysBefore: 2, time: '09:00' },
|
|
1158
1633
|
],
|
|
1634
|
+
'CUSTOM_REMINDER_PRESETS',
|
|
1159
1635
|
),
|
|
1160
1636
|
} as const satisfies Record<string, SettingDef>;
|
|
1161
1637
|
}
|
|
@@ -1232,8 +1708,21 @@ export class SettingsRegistry {
|
|
|
1232
1708
|
case 'number':
|
|
1233
1709
|
return String(value ?? 0);
|
|
1234
1710
|
case 'boolean':
|
|
1711
|
+
if (typeof value === 'string') {
|
|
1712
|
+
return value === 'true' ? 'true' : 'false';
|
|
1713
|
+
}
|
|
1235
1714
|
return value ? 'true' : 'false';
|
|
1236
1715
|
case 'json':
|
|
1716
|
+
// If already a serialized JSON string, return as-is to avoid
|
|
1717
|
+
// double-wrapping (e.g. JSON.stringify("[]") → "\"[]\"")
|
|
1718
|
+
if (typeof value === 'string') {
|
|
1719
|
+
try {
|
|
1720
|
+
JSON.parse(value);
|
|
1721
|
+
return value;
|
|
1722
|
+
} catch {
|
|
1723
|
+
return JSON.stringify(value);
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1237
1726
|
return JSON.stringify(value);
|
|
1238
1727
|
}
|
|
1239
1728
|
}
|
|
@@ -1257,7 +1746,17 @@ export class SettingsRegistry {
|
|
|
1257
1746
|
return (raw === 'true') as SettingValue<K>;
|
|
1258
1747
|
case 'json':
|
|
1259
1748
|
try {
|
|
1260
|
-
|
|
1749
|
+
let result: unknown = JSON.parse(raw);
|
|
1750
|
+
// Unwrap multiply-escaped JSON strings caused by repeated
|
|
1751
|
+
// double-serialization (each push/pull cycle added a layer)
|
|
1752
|
+
while (typeof result === 'string') {
|
|
1753
|
+
try {
|
|
1754
|
+
result = JSON.parse(result);
|
|
1755
|
+
} catch {
|
|
1756
|
+
break;
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
return result as SettingValue<K>;
|
|
1261
1760
|
} catch {
|
|
1262
1761
|
return this.getDefault(key);
|
|
1263
1762
|
}
|
|
@@ -1316,6 +1815,16 @@ export type ParsedSettingEntry = {
|
|
|
1316
1815
|
descriptionKey?: string;
|
|
1317
1816
|
/** The setting value */
|
|
1318
1817
|
value: unknown;
|
|
1818
|
+
/** UI component type for rendering this setting */
|
|
1819
|
+
uiType: SettingUiType;
|
|
1820
|
+
/** Available options for SELECT-type settings */
|
|
1821
|
+
options?: readonly SettingOption[];
|
|
1822
|
+
/** Slider configuration for SLIDER-type settings */
|
|
1823
|
+
sliderConfig?: SliderConfig;
|
|
1824
|
+
/** Only show this setting for enrolled/kiosk devices (undefined = always) */
|
|
1825
|
+
appMode?: 'ENROLLED';
|
|
1826
|
+
/** Only show this setting for a specific calendar type (undefined = always) */
|
|
1827
|
+
calendarType?: CalendarType;
|
|
1319
1828
|
};
|
|
1320
1829
|
|
|
1321
1830
|
export type ParsedSettingsGroup = {
|
|
@@ -1374,6 +1883,9 @@ export function parseSettingsSnapshot(
|
|
|
1374
1883
|
}
|
|
1375
1884
|
|
|
1376
1885
|
const labelDef = SETTINGS_LABELS[key];
|
|
1886
|
+
const entryDef = key in registry.entries
|
|
1887
|
+
? registry.entries[key as SettingKey]
|
|
1888
|
+
: undefined;
|
|
1377
1889
|
groups.get(category)!.push({
|
|
1378
1890
|
key,
|
|
1379
1891
|
name,
|
|
@@ -1381,6 +1893,11 @@ export function parseSettingsSnapshot(
|
|
|
1381
1893
|
labelKey: labelDef?.labelKey,
|
|
1382
1894
|
descriptionKey: labelDef?.descriptionKey,
|
|
1383
1895
|
value,
|
|
1896
|
+
uiType: entryDef?.uiType ?? 'TEXT_INPUT',
|
|
1897
|
+
...(entryDef?.options && { options: entryDef.options as readonly SettingOption[] }),
|
|
1898
|
+
...(entryDef?.sliderConfig && { sliderConfig: entryDef.sliderConfig }),
|
|
1899
|
+
...(entryDef?.appMode && { appMode: entryDef.appMode }),
|
|
1900
|
+
...(entryDef?.calendarType && { calendarType: entryDef.calendarType }),
|
|
1384
1901
|
});
|
|
1385
1902
|
}
|
|
1386
1903
|
|
|
@@ -1424,6 +1941,51 @@ export function formatSettingValue(value: unknown): string {
|
|
|
1424
1941
|
return String(value);
|
|
1425
1942
|
}
|
|
1426
1943
|
|
|
1944
|
+
/**
|
|
1945
|
+
* Serialize a settings object (with mixed native types) into a
|
|
1946
|
+
* string-values-only snapshot suitable for pushing to a mobile device.
|
|
1947
|
+
*
|
|
1948
|
+
* Uses the registry's `serialize()` method for known keys so that
|
|
1949
|
+
* booleans become "true"/"false", numbers become digit strings, etc.
|
|
1950
|
+
* Unknown keys are converted with `String(value)`.
|
|
1951
|
+
*/
|
|
1952
|
+
export function serializeSettingsSnapshot(
|
|
1953
|
+
settings: Record<string, unknown>,
|
|
1954
|
+
registry: SettingsRegistry,
|
|
1955
|
+
): Record<string, string> {
|
|
1956
|
+
const result: Record<string, string> = {};
|
|
1957
|
+
for (const [key, value] of Object.entries(settings)) {
|
|
1958
|
+
if (key in registry.entries) {
|
|
1959
|
+
result[key] = registry.serialize(key as SettingKey, value);
|
|
1960
|
+
} else {
|
|
1961
|
+
result[key] = String(value ?? '');
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
return result;
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
/**
|
|
1968
|
+
* Deserialize a settings snapshot (string values from the server/DB) into
|
|
1969
|
+
* native-typed values using the registry.
|
|
1970
|
+
*
|
|
1971
|
+
* This ensures local state always contains native types (boolean, number, etc.)
|
|
1972
|
+
* so that subsequent `serializeSettingsSnapshot` calls produce correct results.
|
|
1973
|
+
*/
|
|
1974
|
+
export function deserializeSettingsSnapshot(
|
|
1975
|
+
snapshot: Record<string, unknown>,
|
|
1976
|
+
registry: SettingsRegistry = defaultRegistry,
|
|
1977
|
+
): Record<string, unknown> {
|
|
1978
|
+
const result: Record<string, unknown> = {};
|
|
1979
|
+
for (const [key, value] of Object.entries(snapshot)) {
|
|
1980
|
+
if (typeof value === 'string' && key in registry.entries) {
|
|
1981
|
+
result[key] = registry.deserialize(key as SettingKey, value);
|
|
1982
|
+
} else {
|
|
1983
|
+
result[key] = value;
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
return result;
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1427
1989
|
// ---------------------------------------------------------------------------
|
|
1428
1990
|
// Default registry instance (non-enrolled, system theme, nb locale)
|
|
1429
1991
|
// ---------------------------------------------------------------------------
|