@focus8/settings-registry 0.7.0 → 0.8.1
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 +49 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +152 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +193 -46
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,27 +1121,27 @@ 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
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1069
1135
|
// Timer
|
|
1070
1136
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1071
1137
|
'timer.showTimeRemaining': def<boolean>('timer', 'boolean', true, 'TOGGLE'),
|
|
1072
|
-
'timer.showEndTime': def<boolean>('timer', 'boolean', true, 'TOGGLE'),
|
|
1138
|
+
'timer.showEndTime': def<boolean>('timer', 'boolean', true, 'TOGGLE', { calendarType: 'time-based' }),
|
|
1073
1139
|
'timer.showRestartButton': def<boolean>('timer', 'boolean', true, 'TOGGLE'),
|
|
1074
|
-
'timer.showPauseButton': def<boolean>('timer', 'boolean', true, 'TOGGLE'),
|
|
1140
|
+
'timer.showPauseButton': def<boolean>('timer', 'boolean', true, 'TOGGLE', { calendarType: 'time-based' }),
|
|
1075
1141
|
|
|
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>(
|
|
@@ -1326,6 +1393,12 @@ function buildEntries(config: RegistryConfig) {
|
|
|
1326
1393
|
true,
|
|
1327
1394
|
'TOGGLE',
|
|
1328
1395
|
),
|
|
1396
|
+
'chronological.footer.showNowButton': def<boolean>(
|
|
1397
|
+
'chronological',
|
|
1398
|
+
'boolean',
|
|
1399
|
+
true,
|
|
1400
|
+
'TOGGLE',
|
|
1401
|
+
),
|
|
1329
1402
|
'chronological.footer.showSettingsButton': def<boolean>(
|
|
1330
1403
|
'chronological',
|
|
1331
1404
|
'boolean',
|
|
@@ -1345,19 +1418,19 @@ function buildEntries(config: RegistryConfig) {
|
|
|
1345
1418
|
true,
|
|
1346
1419
|
'TOGGLE',
|
|
1347
1420
|
),
|
|
1348
|
-
'chronological.timer.
|
|
1421
|
+
'chronological.timer.showAddTemplate': def<boolean>(
|
|
1349
1422
|
'chronological',
|
|
1350
1423
|
'boolean',
|
|
1351
1424
|
true,
|
|
1352
1425
|
'TOGGLE',
|
|
1353
1426
|
),
|
|
1354
|
-
'chronological.timer.
|
|
1427
|
+
'chronological.timer.showEditTemplate': def<boolean>(
|
|
1355
1428
|
'chronological',
|
|
1356
1429
|
'boolean',
|
|
1357
1430
|
true,
|
|
1358
1431
|
'TOGGLE',
|
|
1359
1432
|
),
|
|
1360
|
-
'chronological.timer.
|
|
1433
|
+
'chronological.timer.showDeleteTemplate': def<boolean>(
|
|
1361
1434
|
'chronological',
|
|
1362
1435
|
'boolean',
|
|
1363
1436
|
true,
|
|
@@ -1441,63 +1514,63 @@ function buildEntries(config: RegistryConfig) {
|
|
|
1441
1514
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1442
1515
|
// Chronological event form
|
|
1443
1516
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1444
|
-
//
|
|
1445
|
-
'chronologicalEventForm.
|
|
1517
|
+
// Fixed fields
|
|
1518
|
+
'chronologicalEventForm.fixedField.allDay': def<boolean>(
|
|
1446
1519
|
'chronologicalEventForm',
|
|
1447
1520
|
'boolean',
|
|
1448
1521
|
true,
|
|
1449
1522
|
'TOGGLE',
|
|
1450
1523
|
),
|
|
1451
|
-
'chronologicalEventForm.
|
|
1524
|
+
'chronologicalEventForm.fixedField.endTime': def<boolean>(
|
|
1452
1525
|
'chronologicalEventForm',
|
|
1453
1526
|
'boolean',
|
|
1454
1527
|
true,
|
|
1455
1528
|
'TOGGLE',
|
|
1456
1529
|
),
|
|
1457
|
-
'chronologicalEventForm.
|
|
1530
|
+
'chronologicalEventForm.fixedField.visibility': def<boolean>(
|
|
1458
1531
|
'chronologicalEventForm',
|
|
1459
1532
|
'boolean',
|
|
1460
1533
|
true,
|
|
1461
1534
|
'TOGGLE',
|
|
1462
1535
|
),
|
|
1463
|
-
|
|
1536
|
+
// Toggleable fields
|
|
1537
|
+
'chronologicalEventForm.field.acknowledge': def<boolean>(
|
|
1464
1538
|
'chronologicalEventForm',
|
|
1465
1539
|
'boolean',
|
|
1466
1540
|
true,
|
|
1467
1541
|
'TOGGLE',
|
|
1468
1542
|
),
|
|
1469
|
-
'chronologicalEventForm.field.
|
|
1543
|
+
'chronologicalEventForm.field.description': def<boolean>(
|
|
1470
1544
|
'chronologicalEventForm',
|
|
1471
1545
|
'boolean',
|
|
1472
1546
|
true,
|
|
1473
1547
|
'TOGGLE',
|
|
1474
1548
|
),
|
|
1475
|
-
'chronologicalEventForm.field.
|
|
1549
|
+
'chronologicalEventForm.field.recurrence': def<boolean>(
|
|
1476
1550
|
'chronologicalEventForm',
|
|
1477
1551
|
'boolean',
|
|
1478
1552
|
true,
|
|
1479
1553
|
'TOGGLE',
|
|
1480
1554
|
),
|
|
1481
|
-
'chronologicalEventForm.field.
|
|
1555
|
+
'chronologicalEventForm.field.checklist': def<boolean>(
|
|
1482
1556
|
'chronologicalEventForm',
|
|
1483
1557
|
'boolean',
|
|
1484
1558
|
true,
|
|
1485
1559
|
'TOGGLE',
|
|
1486
1560
|
),
|
|
1487
|
-
|
|
1488
|
-
'chronologicalEventForm.fixedField.allDay': def<boolean>(
|
|
1561
|
+
'chronologicalEventForm.field.extraImages': def<boolean>(
|
|
1489
1562
|
'chronologicalEventForm',
|
|
1490
1563
|
'boolean',
|
|
1491
1564
|
true,
|
|
1492
1565
|
'TOGGLE',
|
|
1493
1566
|
),
|
|
1494
|
-
'chronologicalEventForm.
|
|
1567
|
+
'chronologicalEventForm.field.reminders': def<boolean>(
|
|
1495
1568
|
'chronologicalEventForm',
|
|
1496
1569
|
'boolean',
|
|
1497
1570
|
true,
|
|
1498
1571
|
'TOGGLE',
|
|
1499
1572
|
),
|
|
1500
|
-
'chronologicalEventForm.
|
|
1573
|
+
'chronologicalEventForm.field.audioClips': def<boolean>(
|
|
1501
1574
|
'chronologicalEventForm',
|
|
1502
1575
|
'boolean',
|
|
1503
1576
|
true,
|
|
@@ -1641,8 +1714,21 @@ export class SettingsRegistry {
|
|
|
1641
1714
|
case 'number':
|
|
1642
1715
|
return String(value ?? 0);
|
|
1643
1716
|
case 'boolean':
|
|
1717
|
+
if (typeof value === 'string') {
|
|
1718
|
+
return value === 'true' ? 'true' : 'false';
|
|
1719
|
+
}
|
|
1644
1720
|
return value ? 'true' : 'false';
|
|
1645
1721
|
case 'json':
|
|
1722
|
+
// If already a serialized JSON string, return as-is to avoid
|
|
1723
|
+
// double-wrapping (e.g. JSON.stringify("[]") → "\"[]\"")
|
|
1724
|
+
if (typeof value === 'string') {
|
|
1725
|
+
try {
|
|
1726
|
+
JSON.parse(value);
|
|
1727
|
+
return value;
|
|
1728
|
+
} catch {
|
|
1729
|
+
return JSON.stringify(value);
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1646
1732
|
return JSON.stringify(value);
|
|
1647
1733
|
}
|
|
1648
1734
|
}
|
|
@@ -1666,7 +1752,17 @@ export class SettingsRegistry {
|
|
|
1666
1752
|
return (raw === 'true') as SettingValue<K>;
|
|
1667
1753
|
case 'json':
|
|
1668
1754
|
try {
|
|
1669
|
-
|
|
1755
|
+
let result: unknown = JSON.parse(raw);
|
|
1756
|
+
// Unwrap multiply-escaped JSON strings caused by repeated
|
|
1757
|
+
// double-serialization (each push/pull cycle added a layer)
|
|
1758
|
+
while (typeof result === 'string') {
|
|
1759
|
+
try {
|
|
1760
|
+
result = JSON.parse(result);
|
|
1761
|
+
} catch {
|
|
1762
|
+
break;
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
return result as SettingValue<K>;
|
|
1670
1766
|
} catch {
|
|
1671
1767
|
return this.getDefault(key);
|
|
1672
1768
|
}
|
|
@@ -1731,6 +1827,10 @@ export type ParsedSettingEntry = {
|
|
|
1731
1827
|
options?: readonly SettingOption[];
|
|
1732
1828
|
/** Slider configuration for SLIDER-type settings */
|
|
1733
1829
|
sliderConfig?: SliderConfig;
|
|
1830
|
+
/** Only show this setting for enrolled/kiosk devices (undefined = always) */
|
|
1831
|
+
appMode?: 'ENROLLED';
|
|
1832
|
+
/** Only show this setting for a specific calendar type (undefined = always) */
|
|
1833
|
+
calendarType?: CalendarType;
|
|
1734
1834
|
};
|
|
1735
1835
|
|
|
1736
1836
|
export type ParsedSettingsGroup = {
|
|
@@ -1802,6 +1902,8 @@ export function parseSettingsSnapshot(
|
|
|
1802
1902
|
uiType: entryDef?.uiType ?? 'TEXT_INPUT',
|
|
1803
1903
|
...(entryDef?.options && { options: entryDef.options as readonly SettingOption[] }),
|
|
1804
1904
|
...(entryDef?.sliderConfig && { sliderConfig: entryDef.sliderConfig }),
|
|
1905
|
+
...(entryDef?.appMode && { appMode: entryDef.appMode }),
|
|
1906
|
+
...(entryDef?.calendarType && { calendarType: entryDef.calendarType }),
|
|
1805
1907
|
});
|
|
1806
1908
|
}
|
|
1807
1909
|
|
|
@@ -1845,6 +1947,51 @@ export function formatSettingValue(value: unknown): string {
|
|
|
1845
1947
|
return String(value);
|
|
1846
1948
|
}
|
|
1847
1949
|
|
|
1950
|
+
/**
|
|
1951
|
+
* Serialize a settings object (with mixed native types) into a
|
|
1952
|
+
* string-values-only snapshot suitable for pushing to a mobile device.
|
|
1953
|
+
*
|
|
1954
|
+
* Uses the registry's `serialize()` method for known keys so that
|
|
1955
|
+
* booleans become "true"/"false", numbers become digit strings, etc.
|
|
1956
|
+
* Unknown keys are converted with `String(value)`.
|
|
1957
|
+
*/
|
|
1958
|
+
export function serializeSettingsSnapshot(
|
|
1959
|
+
settings: Record<string, unknown>,
|
|
1960
|
+
registry: SettingsRegistry,
|
|
1961
|
+
): Record<string, string> {
|
|
1962
|
+
const result: Record<string, string> = {};
|
|
1963
|
+
for (const [key, value] of Object.entries(settings)) {
|
|
1964
|
+
if (key in registry.entries) {
|
|
1965
|
+
result[key] = registry.serialize(key as SettingKey, value);
|
|
1966
|
+
} else {
|
|
1967
|
+
result[key] = String(value ?? '');
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
return result;
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
/**
|
|
1974
|
+
* Deserialize a settings snapshot (string values from the server/DB) into
|
|
1975
|
+
* native-typed values using the registry.
|
|
1976
|
+
*
|
|
1977
|
+
* This ensures local state always contains native types (boolean, number, etc.)
|
|
1978
|
+
* so that subsequent `serializeSettingsSnapshot` calls produce correct results.
|
|
1979
|
+
*/
|
|
1980
|
+
export function deserializeSettingsSnapshot(
|
|
1981
|
+
snapshot: Record<string, unknown>,
|
|
1982
|
+
registry: SettingsRegistry = defaultRegistry,
|
|
1983
|
+
): Record<string, unknown> {
|
|
1984
|
+
const result: Record<string, unknown> = {};
|
|
1985
|
+
for (const [key, value] of Object.entries(snapshot)) {
|
|
1986
|
+
if (typeof value === 'string' && key in registry.entries) {
|
|
1987
|
+
result[key] = registry.deserialize(key as SettingKey, value);
|
|
1988
|
+
} else {
|
|
1989
|
+
result[key] = value;
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
return result;
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1848
1995
|
// ---------------------------------------------------------------------------
|
|
1849
1996
|
// Default registry instance (non-enrolled, system theme, nb locale)
|
|
1850
1997
|
// ---------------------------------------------------------------------------
|