feat: add night mode settings and validation for board devices
This commit is contained in:
@@ -11,7 +11,7 @@ function name(value) {
|
||||
}
|
||||
function deviceId(value) { if (typeof value !== 'string' || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)) throw new DeviceError(400, 'invalid_device_id'); return value; }
|
||||
function publicDevice(row, now) {
|
||||
return { id: row.id, name: row.name, cityId: row.city_id, cityName: row.city_name, stopName: row.stop_name, stopDirection: row.stop_direction || '', settings: row.settings,
|
||||
return { id: row.id, name: row.name, cityId: row.city_id, cityName: row.city_name, stopName: row.stop_name, stopDirection: row.stop_direction || '', settings: validSettings(row.settings),
|
||||
revision: row.revision, appliedRevision: row.applied_revision, lastSeenAt: row.last_seen_at, deviceInfo: row.device_info, revokedAt: row.revoked_at,
|
||||
online: !row.revoked_at && Boolean(row.last_seen_at && now - Date.parse(row.last_seen_at) < 90000) };
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ const defaults = {
|
||||
theme: 'dark', colorScheme: 'lime', lightColorScheme: 'blue',
|
||||
infoServiceEnabled: true, infoWeatherEnabled: true, infoQrEnabled: true,
|
||||
showNextSameRoute: true, longWaitEnabled: true, displayProfile: 'tv', viewingDistance: 'near',
|
||||
nightMode: 'auto', nightWakeMinutes: 60,
|
||||
adsEnabled: false, adSeconds: 7, adTitle: 'Ваше место в городе', adText: 'Короткая история. Большая аудитория.',
|
||||
};
|
||||
class DeviceError extends Error {
|
||||
@@ -17,7 +18,8 @@ function validSettings(input) {
|
||||
if (!Number.isInteger(value[key]) || value[key] < min || value[key] > max) fail(key);
|
||||
}
|
||||
for (const key of ['holdNear', 'infoServiceEnabled', 'infoWeatherEnabled', 'infoQrEnabled', 'showNextSameRoute', 'longWaitEnabled', 'adsEnabled']) if (typeof value[key] !== 'boolean') fail(key);
|
||||
for (const [key, choices] of Object.entries({ theme: ['dark', 'light'], colorScheme: ['lime', 'amber', 'cyan', 'mint', 'lavender', 'white'], lightColorScheme: ['blue', 'forest', 'crimson', 'violet', 'teal', 'graphite'], displayProfile: ['tv', 'street'], viewingDistance: ['near', 'medium', 'far'] })) if (!choices.includes(value[key])) fail(key);
|
||||
for (const [key, choices] of Object.entries({ theme: ['dark', 'light'], colorScheme: ['lime', 'amber', 'cyan', 'mint', 'lavender', 'white'], lightColorScheme: ['blue', 'forest', 'crimson', 'violet', 'teal', 'graphite'], displayProfile: ['tv', 'street'], viewingDistance: ['near', 'medium', 'far'], nightMode: ['off', 'auto'] })) if (!choices.includes(value[key])) fail(key);
|
||||
if (![45, 60].includes(value.nightWakeMinutes)) fail('nightWakeMinutes');
|
||||
if (typeof value.routeFilter !== 'string' || !/^(|[1-9]\d{0,8})$/.test(value.routeFilter)) fail('routeFilter');
|
||||
for (const [key, max] of [['adTitle', 80], ['adText', 200]]) if (typeof value[key] !== 'string' || value[key].length > max) fail(key);
|
||||
return value;
|
||||
|
||||
@@ -49,3 +49,13 @@ test('remote settings are bounded, preserve supported palettes and cannot enable
|
||||
for (const bad of [{ ...defaults, holdNear: 'yes' }, { ...defaults, adText: 'x'.repeat(201) }, { ...defaults, toString: 'bad' }, { ...defaults, stopId: NaN }, { ...defaults, routeFilter: '-1' }]) assert.throws(() => validSettings(bad), e => e.status === 400);
|
||||
assert.equal(validSettings({ ...defaults, colorScheme: 'cyan' }).colorScheme, 'cyan');
|
||||
});
|
||||
|
||||
test('night preferences validate, survive updates and migrate old stored device settings without a SQL migration', async () => {
|
||||
const repo=memoryRepository(), service=createBoardDeviceService(repo);const p=await service.start({});const d=await service.claim(1,{code:p.code,name:'Night',settings:defaults},1);
|
||||
delete repo.devices.get(d.id).settings.nightMode;delete repo.devices.get(d.id).settings.nightWakeMinutes;
|
||||
assert.equal((await service.status(p.token)).device.settings.nightMode,'auto');assert.equal((await service.list(1))[0].settings.nightWakeMinutes,60);
|
||||
assert.equal(repo.devices.get(d.id).settings.nightMode,undefined);
|
||||
for(const settings of [{...defaults,nightMode:'always'},{...defaults,nightWakeMinutes:5}]) assert.throws(()=>validSettings(settings),e=>e.status===400);
|
||||
await service.update(d.id,1,{name:'Night',revision:1,settings:{...defaults,nightMode:'off',nightWakeMinutes:45}},1);
|
||||
const saved=(await service.status(p.token)).device;assert.equal(saved.settings.nightMode,'off');assert.equal(saved.settings.nightWakeMinutes,45);assert.equal(saved.revision,2);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user