@focus8/settings-registry 0.7.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 +47 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +149 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +185 -44
package/src/index.ts
CHANGED
|
@@ -285,7 +285,7 @@ export const SETTINGS_GROUPS: readonly SettingsGroupDef[] = [
|
|
|
285
285
|
id: 'calendarType',
|
|
286
286
|
labelKey: 'Settings.CalendarType',
|
|
287
287
|
icon: 'ArrowLeftRight',
|
|
288
|
-
keys: ['calendarView.type'
|
|
288
|
+
keys: ['calendarView.type'],
|
|
289
289
|
},
|
|
290
290
|
{
|
|
291
291
|
id: 'calendarView',
|
|
@@ -294,9 +294,9 @@ export const SETTINGS_GROUPS: readonly SettingsGroupDef[] = [
|
|
|
294
294
|
keys: [
|
|
295
295
|
'calendarView.showCalendarNames',
|
|
296
296
|
'calendarView.splitView',
|
|
297
|
-
'calendarView.calendarColumns',
|
|
298
297
|
'calendarView.dayViewZoom',
|
|
299
298
|
'calendarView.weekViewZoom',
|
|
299
|
+
'calendarView.calendarColumns',
|
|
300
300
|
],
|
|
301
301
|
},
|
|
302
302
|
{
|
|
@@ -310,9 +310,9 @@ export const SETTINGS_GROUPS: readonly SettingsGroupDef[] = [
|
|
|
310
310
|
labelKey: 'Settings.DateWeather',
|
|
311
311
|
icon: 'CloudSun',
|
|
312
312
|
keys: [
|
|
313
|
-
'calendarView.showWeatherOnEvents',
|
|
314
313
|
'calendarView.showWeatherOnTimeline',
|
|
315
314
|
'calendarView.weatherLocation',
|
|
315
|
+
'calendarView.showWeatherOnEvents',
|
|
316
316
|
],
|
|
317
317
|
},
|
|
318
318
|
{
|
|
@@ -322,13 +322,13 @@ export const SETTINGS_GROUPS: readonly SettingsGroupDef[] = [
|
|
|
322
322
|
keys: [
|
|
323
323
|
'sound.reminderVolume',
|
|
324
324
|
'sound.mediaVolume',
|
|
325
|
-
'sound.alarmSound',
|
|
326
325
|
'sound.reminderAlarmSound',
|
|
327
326
|
'sound.reminderAlarmTimeout',
|
|
328
327
|
'sound.allowCustomReminderSounds',
|
|
328
|
+
'notification.',
|
|
329
329
|
'sound.ttsEnabled',
|
|
330
330
|
'sound.ttsRate',
|
|
331
|
-
'
|
|
331
|
+
'sound.alarmSound',
|
|
332
332
|
],
|
|
333
333
|
},
|
|
334
334
|
{
|
|
@@ -336,10 +336,11 @@ export const SETTINGS_GROUPS: readonly SettingsGroupDef[] = [
|
|
|
336
336
|
labelKey: 'Settings.TimerTitle',
|
|
337
337
|
icon: 'Timer',
|
|
338
338
|
keys: [
|
|
339
|
-
'
|
|
339
|
+
'chronological.timer.',
|
|
340
|
+
'timer.',
|
|
340
341
|
'sound.timerAlarmSound',
|
|
341
342
|
'sound.timerAlarmTimeout',
|
|
342
|
-
'
|
|
343
|
+
'sound.timerVolume',
|
|
343
344
|
],
|
|
344
345
|
appMode: 'ENROLLED',
|
|
345
346
|
},
|
|
@@ -389,6 +390,9 @@ export const EXCLUDED_DEVICE_SETTINGS: ReadonlySet<string> = new Set([
|
|
|
389
390
|
'device.id',
|
|
390
391
|
'device.devMenuEnabled',
|
|
391
392
|
'device.authWarningDismissTtlDays',
|
|
393
|
+
'notification.enabled',
|
|
394
|
+
'notification.notifyAllCalendars',
|
|
395
|
+
'notification.enabledCalendarIds',
|
|
392
396
|
'notification.hasBeenPrompted',
|
|
393
397
|
]);
|
|
394
398
|
|
|
@@ -647,6 +651,33 @@ function matchesGroup(key: string, group: SettingsGroupDef): boolean {
|
|
|
647
651
|
);
|
|
648
652
|
}
|
|
649
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
|
+
|
|
650
681
|
/**
|
|
651
682
|
* Group parsed setting entries into user-facing groups matching the mobile
|
|
652
683
|
* app's settings menu structure.
|
|
@@ -674,6 +705,14 @@ export function groupSettingsForDevice(
|
|
|
674
705
|
if (!isChronological && s.key.startsWith('chronologicalEventForm.')) {
|
|
675
706
|
return false;
|
|
676
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
|
+
}
|
|
677
716
|
return true;
|
|
678
717
|
});
|
|
679
718
|
|
|
@@ -703,6 +742,18 @@ export function groupSettingsForDevice(
|
|
|
703
742
|
continue;
|
|
704
743
|
}
|
|
705
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
|
+
|
|
706
757
|
for (const s of matched) {
|
|
707
758
|
claimed.add(s.key);
|
|
708
759
|
}
|
|
@@ -740,6 +791,10 @@ export type SettingDef<T = unknown> = {
|
|
|
740
791
|
options?: readonly SettingOption<T>[];
|
|
741
792
|
/** Slider configuration for SLIDER-type settings */
|
|
742
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;
|
|
743
798
|
};
|
|
744
799
|
|
|
745
800
|
// ---------------------------------------------------------------------------
|
|
@@ -790,7 +845,7 @@ const ALARM_TIMEOUT_OPTIONS: readonly SettingOption<AlarmTimeout>[] = [
|
|
|
790
845
|
const DAY_VIEW_ZOOM_OPTIONS: readonly SettingOption<CalendarDayViewCellZoom>[] = [
|
|
791
846
|
{ value: 15, labelKey: 'Settings.Option.DayViewZoom.15min' },
|
|
792
847
|
{ value: 30, labelKey: 'Settings.Option.DayViewZoom.30min' },
|
|
793
|
-
{ value: 60, labelKey: 'Settings.Option.DayViewZoom.
|
|
848
|
+
{ value: 60, labelKey: 'Settings.Option.DayViewZoom.1hour' },
|
|
794
849
|
];
|
|
795
850
|
|
|
796
851
|
const TIME_OF_DAY_SLIDER: SliderConfig = { min: 0, max: 23, step: 1 };
|
|
@@ -808,6 +863,8 @@ function def<T>(
|
|
|
808
863
|
sync?: boolean;
|
|
809
864
|
options?: readonly SettingOption<T>[];
|
|
810
865
|
sliderConfig?: SliderConfig;
|
|
866
|
+
appMode?: 'ENROLLED';
|
|
867
|
+
calendarType?: CalendarType;
|
|
811
868
|
},
|
|
812
869
|
): SettingDef<T> {
|
|
813
870
|
const result: SettingDef<T> = {
|
|
@@ -823,6 +880,12 @@ function def<T>(
|
|
|
823
880
|
if (extra?.sliderConfig) {
|
|
824
881
|
result.sliderConfig = extra.sliderConfig;
|
|
825
882
|
}
|
|
883
|
+
if (extra?.appMode) {
|
|
884
|
+
result.appMode = extra.appMode;
|
|
885
|
+
}
|
|
886
|
+
if (extra?.calendarType) {
|
|
887
|
+
result.calendarType = extra.calendarType;
|
|
888
|
+
}
|
|
826
889
|
return result;
|
|
827
890
|
}
|
|
828
891
|
|
|
@@ -858,6 +921,7 @@ function buildEntries(config: RegistryConfig) {
|
|
|
858
921
|
{ value: 'digital', labelKey: 'Settings.Option.ClockType.Digital' },
|
|
859
922
|
{ value: 'analog', labelKey: 'Settings.Option.ClockType.Analog' },
|
|
860
923
|
],
|
|
924
|
+
appMode: 'ENROLLED',
|
|
861
925
|
},
|
|
862
926
|
),
|
|
863
927
|
'appearance.enableDayColors': def<boolean>(
|
|
@@ -865,6 +929,7 @@ function buildEntries(config: RegistryConfig) {
|
|
|
865
929
|
'boolean',
|
|
866
930
|
false,
|
|
867
931
|
'TOGGLE',
|
|
932
|
+
{ calendarType: 'chronological' },
|
|
868
933
|
),
|
|
869
934
|
|
|
870
935
|
// ═══════════════════════════════════════════════════════════════════════
|
|
@@ -925,14 +990,14 @@ function buildEntries(config: RegistryConfig) {
|
|
|
925
990
|
'number',
|
|
926
991
|
60,
|
|
927
992
|
'SELECT',
|
|
928
|
-
{ options: DAY_VIEW_ZOOM_OPTIONS },
|
|
993
|
+
{ options: DAY_VIEW_ZOOM_OPTIONS, calendarType: 'time-based' },
|
|
929
994
|
),
|
|
930
995
|
'calendarView.weekViewZoom': def<CalendarDayViewCellZoom>(
|
|
931
996
|
'calendarView',
|
|
932
997
|
'number',
|
|
933
998
|
60,
|
|
934
999
|
'SELECT',
|
|
935
|
-
{ options: DAY_VIEW_ZOOM_OPTIONS },
|
|
1000
|
+
{ options: DAY_VIEW_ZOOM_OPTIONS, calendarType: 'time-based' },
|
|
936
1001
|
),
|
|
937
1002
|
'calendarView.splitView': def<boolean>(
|
|
938
1003
|
'calendarView',
|
|
@@ -988,10 +1053,10 @@ function buildEntries(config: RegistryConfig) {
|
|
|
988
1053
|
// Event form field visibility (time-based)
|
|
989
1054
|
// ═══════════════════════════════════════════════════════════════════════
|
|
990
1055
|
'eventForm.recurrence': def<boolean>('eventForm', 'boolean', true, 'TOGGLE'),
|
|
991
|
-
'eventForm.reminders': def<boolean>('eventForm', 'boolean', true, 'TOGGLE'),
|
|
992
|
-
'eventForm.emailReminders': def<boolean>('eventForm', 'boolean', false, 'TOGGLE'),
|
|
993
1056
|
'eventForm.location': def<boolean>('eventForm', 'boolean', true, 'TOGGLE'),
|
|
994
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'),
|
|
995
1060
|
'eventForm.description': def<boolean>('eventForm', 'boolean', true, 'TOGGLE'),
|
|
996
1061
|
'eventForm.checklist': def<boolean>('eventForm', 'boolean', true, 'TOGGLE'),
|
|
997
1062
|
'eventForm.images': def<boolean>('eventForm', 'boolean', false, 'TOGGLE'),
|
|
@@ -1008,8 +1073,8 @@ function buildEntries(config: RegistryConfig) {
|
|
|
1008
1073
|
// Sound & alerts
|
|
1009
1074
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1010
1075
|
'sound.timerVolume': def<number>('sound', 'number', 0.5, 'VOLUME_SLIDER'),
|
|
1011
|
-
'sound.reminderVolume': def<number>('sound', 'number', 0.5, 'VOLUME_SLIDER'),
|
|
1012
|
-
'sound.mediaVolume': 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' }),
|
|
1013
1078
|
'sound.alarmSound': def<AlarmSound>(
|
|
1014
1079
|
'sound',
|
|
1015
1080
|
'string',
|
|
@@ -1056,13 +1121,14 @@ function buildEntries(config: RegistryConfig) {
|
|
|
1056
1121
|
'boolean',
|
|
1057
1122
|
config.isEnrolled,
|
|
1058
1123
|
'TOGGLE',
|
|
1124
|
+
{ appMode: 'ENROLLED' },
|
|
1059
1125
|
),
|
|
1060
1126
|
'sound.ttsRate': def<number>(
|
|
1061
1127
|
'sound',
|
|
1062
1128
|
'number',
|
|
1063
1129
|
1.0,
|
|
1064
1130
|
'SLIDER',
|
|
1065
|
-
{ sliderConfig: { min: 0.5, max: 2, step: 0.1 } },
|
|
1131
|
+
{ sliderConfig: { min: 0.5, max: 2, step: 0.1 }, appMode: 'ENROLLED' },
|
|
1066
1132
|
),
|
|
1067
1133
|
|
|
1068
1134
|
// ═══════════════════════════════════════════════════════════════════════
|
|
@@ -1076,7 +1142,6 @@ function buildEntries(config: RegistryConfig) {
|
|
|
1076
1142
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1077
1143
|
// Lock screen
|
|
1078
1144
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1079
|
-
'lockScreen.pin': def<string>('lockScreen', 'string', '', 'PIN_INPUT'),
|
|
1080
1145
|
'lockScreen.inactivityLockEnabled': def<boolean>(
|
|
1081
1146
|
'lockScreen',
|
|
1082
1147
|
'boolean',
|
|
@@ -1099,6 +1164,7 @@ function buildEntries(config: RegistryConfig) {
|
|
|
1099
1164
|
],
|
|
1100
1165
|
},
|
|
1101
1166
|
),
|
|
1167
|
+
'lockScreen.pin': def<string>('lockScreen', 'string', '', 'PIN_INPUT'),
|
|
1102
1168
|
'lockScreen.clockDisplay': def<LockScreenClockDisplay>(
|
|
1103
1169
|
'lockScreen',
|
|
1104
1170
|
'string',
|
|
@@ -1118,13 +1184,13 @@ function buildEntries(config: RegistryConfig) {
|
|
|
1118
1184
|
],
|
|
1119
1185
|
},
|
|
1120
1186
|
),
|
|
1121
|
-
'lockScreen.
|
|
1187
|
+
'lockScreen.showHourNumbers': def<boolean>(
|
|
1122
1188
|
'lockScreen',
|
|
1123
1189
|
'boolean',
|
|
1124
1190
|
true,
|
|
1125
1191
|
'TOGGLE',
|
|
1126
1192
|
),
|
|
1127
|
-
'lockScreen.
|
|
1193
|
+
'lockScreen.showDate': def<boolean>(
|
|
1128
1194
|
'lockScreen',
|
|
1129
1195
|
'boolean',
|
|
1130
1196
|
true,
|
|
@@ -1155,12 +1221,6 @@ function buildEntries(config: RegistryConfig) {
|
|
|
1155
1221
|
null,
|
|
1156
1222
|
'CUSTOM_IMAGE',
|
|
1157
1223
|
),
|
|
1158
|
-
'lockScreen.photoFrameImages': def<LockScreenImage[]>(
|
|
1159
|
-
'lockScreen',
|
|
1160
|
-
'json',
|
|
1161
|
-
[],
|
|
1162
|
-
'CUSTOM_IMAGE_ARRAY',
|
|
1163
|
-
),
|
|
1164
1224
|
'lockScreen.photoFrameIntervalSeconds': def<PhotoFrameIntervalSeconds>(
|
|
1165
1225
|
'lockScreen',
|
|
1166
1226
|
'number',
|
|
@@ -1176,6 +1236,13 @@ function buildEntries(config: RegistryConfig) {
|
|
|
1176
1236
|
},
|
|
1177
1237
|
),
|
|
1178
1238
|
|
|
1239
|
+
'lockScreen.photoFrameImages': def<LockScreenImage[]>(
|
|
1240
|
+
'lockScreen',
|
|
1241
|
+
'json',
|
|
1242
|
+
[],
|
|
1243
|
+
'CUSTOM_IMAGE_ARRAY',
|
|
1244
|
+
),
|
|
1245
|
+
|
|
1179
1246
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1180
1247
|
// Touch / gestures
|
|
1181
1248
|
// ═══════════════════════════════════════════════════════════════════════
|
|
@@ -1271,16 +1338,16 @@ function buildEntries(config: RegistryConfig) {
|
|
|
1271
1338
|
true,
|
|
1272
1339
|
'TOGGLE',
|
|
1273
1340
|
),
|
|
1274
|
-
'chronological.header.
|
|
1341
|
+
'chronological.header.showCurrentYearInDate': def<boolean>(
|
|
1275
1342
|
'chronological',
|
|
1276
1343
|
'boolean',
|
|
1277
|
-
|
|
1344
|
+
false,
|
|
1278
1345
|
'TOGGLE',
|
|
1279
1346
|
),
|
|
1280
|
-
'chronological.header.
|
|
1347
|
+
'chronological.header.showClock': def<boolean>(
|
|
1281
1348
|
'chronological',
|
|
1282
1349
|
'boolean',
|
|
1283
|
-
|
|
1350
|
+
true,
|
|
1284
1351
|
'TOGGLE',
|
|
1285
1352
|
),
|
|
1286
1353
|
'chronological.header.showTimeOfDay': def<boolean>(
|
|
@@ -1345,19 +1412,19 @@ function buildEntries(config: RegistryConfig) {
|
|
|
1345
1412
|
true,
|
|
1346
1413
|
'TOGGLE',
|
|
1347
1414
|
),
|
|
1348
|
-
'chronological.timer.
|
|
1415
|
+
'chronological.timer.showAddTemplate': def<boolean>(
|
|
1349
1416
|
'chronological',
|
|
1350
1417
|
'boolean',
|
|
1351
1418
|
true,
|
|
1352
1419
|
'TOGGLE',
|
|
1353
1420
|
),
|
|
1354
|
-
'chronological.timer.
|
|
1421
|
+
'chronological.timer.showEditTemplate': def<boolean>(
|
|
1355
1422
|
'chronological',
|
|
1356
1423
|
'boolean',
|
|
1357
1424
|
true,
|
|
1358
1425
|
'TOGGLE',
|
|
1359
1426
|
),
|
|
1360
|
-
'chronological.timer.
|
|
1427
|
+
'chronological.timer.showDeleteTemplate': def<boolean>(
|
|
1361
1428
|
'chronological',
|
|
1362
1429
|
'boolean',
|
|
1363
1430
|
true,
|
|
@@ -1441,63 +1508,63 @@ function buildEntries(config: RegistryConfig) {
|
|
|
1441
1508
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1442
1509
|
// Chronological event form
|
|
1443
1510
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1444
|
-
//
|
|
1445
|
-
'chronologicalEventForm.
|
|
1511
|
+
// Fixed fields
|
|
1512
|
+
'chronologicalEventForm.fixedField.allDay': def<boolean>(
|
|
1446
1513
|
'chronologicalEventForm',
|
|
1447
1514
|
'boolean',
|
|
1448
1515
|
true,
|
|
1449
1516
|
'TOGGLE',
|
|
1450
1517
|
),
|
|
1451
|
-
'chronologicalEventForm.
|
|
1518
|
+
'chronologicalEventForm.fixedField.endTime': def<boolean>(
|
|
1452
1519
|
'chronologicalEventForm',
|
|
1453
1520
|
'boolean',
|
|
1454
1521
|
true,
|
|
1455
1522
|
'TOGGLE',
|
|
1456
1523
|
),
|
|
1457
|
-
'chronologicalEventForm.
|
|
1524
|
+
'chronologicalEventForm.fixedField.visibility': def<boolean>(
|
|
1458
1525
|
'chronologicalEventForm',
|
|
1459
1526
|
'boolean',
|
|
1460
1527
|
true,
|
|
1461
1528
|
'TOGGLE',
|
|
1462
1529
|
),
|
|
1463
|
-
|
|
1530
|
+
// Toggleable fields
|
|
1531
|
+
'chronologicalEventForm.field.acknowledge': def<boolean>(
|
|
1464
1532
|
'chronologicalEventForm',
|
|
1465
1533
|
'boolean',
|
|
1466
1534
|
true,
|
|
1467
1535
|
'TOGGLE',
|
|
1468
1536
|
),
|
|
1469
|
-
'chronologicalEventForm.field.
|
|
1537
|
+
'chronologicalEventForm.field.description': def<boolean>(
|
|
1470
1538
|
'chronologicalEventForm',
|
|
1471
1539
|
'boolean',
|
|
1472
1540
|
true,
|
|
1473
1541
|
'TOGGLE',
|
|
1474
1542
|
),
|
|
1475
|
-
'chronologicalEventForm.field.
|
|
1543
|
+
'chronologicalEventForm.field.recurrence': def<boolean>(
|
|
1476
1544
|
'chronologicalEventForm',
|
|
1477
1545
|
'boolean',
|
|
1478
1546
|
true,
|
|
1479
1547
|
'TOGGLE',
|
|
1480
1548
|
),
|
|
1481
|
-
'chronologicalEventForm.field.
|
|
1549
|
+
'chronologicalEventForm.field.checklist': def<boolean>(
|
|
1482
1550
|
'chronologicalEventForm',
|
|
1483
1551
|
'boolean',
|
|
1484
1552
|
true,
|
|
1485
1553
|
'TOGGLE',
|
|
1486
1554
|
),
|
|
1487
|
-
|
|
1488
|
-
'chronologicalEventForm.fixedField.allDay': def<boolean>(
|
|
1555
|
+
'chronologicalEventForm.field.extraImages': def<boolean>(
|
|
1489
1556
|
'chronologicalEventForm',
|
|
1490
1557
|
'boolean',
|
|
1491
1558
|
true,
|
|
1492
1559
|
'TOGGLE',
|
|
1493
1560
|
),
|
|
1494
|
-
'chronologicalEventForm.
|
|
1561
|
+
'chronologicalEventForm.field.reminders': def<boolean>(
|
|
1495
1562
|
'chronologicalEventForm',
|
|
1496
1563
|
'boolean',
|
|
1497
1564
|
true,
|
|
1498
1565
|
'TOGGLE',
|
|
1499
1566
|
),
|
|
1500
|
-
'chronologicalEventForm.
|
|
1567
|
+
'chronologicalEventForm.field.audioClips': def<boolean>(
|
|
1501
1568
|
'chronologicalEventForm',
|
|
1502
1569
|
'boolean',
|
|
1503
1570
|
true,
|
|
@@ -1641,8 +1708,21 @@ export class SettingsRegistry {
|
|
|
1641
1708
|
case 'number':
|
|
1642
1709
|
return String(value ?? 0);
|
|
1643
1710
|
case 'boolean':
|
|
1711
|
+
if (typeof value === 'string') {
|
|
1712
|
+
return value === 'true' ? 'true' : 'false';
|
|
1713
|
+
}
|
|
1644
1714
|
return value ? 'true' : 'false';
|
|
1645
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
|
+
}
|
|
1646
1726
|
return JSON.stringify(value);
|
|
1647
1727
|
}
|
|
1648
1728
|
}
|
|
@@ -1666,7 +1746,17 @@ export class SettingsRegistry {
|
|
|
1666
1746
|
return (raw === 'true') as SettingValue<K>;
|
|
1667
1747
|
case 'json':
|
|
1668
1748
|
try {
|
|
1669
|
-
|
|
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>;
|
|
1670
1760
|
} catch {
|
|
1671
1761
|
return this.getDefault(key);
|
|
1672
1762
|
}
|
|
@@ -1731,6 +1821,10 @@ export type ParsedSettingEntry = {
|
|
|
1731
1821
|
options?: readonly SettingOption[];
|
|
1732
1822
|
/** Slider configuration for SLIDER-type settings */
|
|
1733
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;
|
|
1734
1828
|
};
|
|
1735
1829
|
|
|
1736
1830
|
export type ParsedSettingsGroup = {
|
|
@@ -1802,6 +1896,8 @@ export function parseSettingsSnapshot(
|
|
|
1802
1896
|
uiType: entryDef?.uiType ?? 'TEXT_INPUT',
|
|
1803
1897
|
...(entryDef?.options && { options: entryDef.options as readonly SettingOption[] }),
|
|
1804
1898
|
...(entryDef?.sliderConfig && { sliderConfig: entryDef.sliderConfig }),
|
|
1899
|
+
...(entryDef?.appMode && { appMode: entryDef.appMode }),
|
|
1900
|
+
...(entryDef?.calendarType && { calendarType: entryDef.calendarType }),
|
|
1805
1901
|
});
|
|
1806
1902
|
}
|
|
1807
1903
|
|
|
@@ -1845,6 +1941,51 @@ export function formatSettingValue(value: unknown): string {
|
|
|
1845
1941
|
return String(value);
|
|
1846
1942
|
}
|
|
1847
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
|
+
|
|
1848
1989
|
// ---------------------------------------------------------------------------
|
|
1849
1990
|
// Default registry instance (non-enrolled, system theme, nb locale)
|
|
1850
1991
|
// ---------------------------------------------------------------------------
|