build-container / build (push) Successful in 4m30s
- Admin can make a screen a family device; it opens on a family board - Name tiles with KID labels; kids tap, others enter a PIN (lockout after 5 wrong tries) - Name label with Switch user, auto-return to the board after 5 idle minutes - Short 30-minute sessions on family devices - Settings section for PIN and turning a device on or off - Test, README and roadmap updated Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
26 lines
3.9 KiB
JavaScript
26 lines
3.9 KiB
JavaScript
'use strict';
|
|
// ---- Family devices: on a shared screen, a label that says whose account this is, a Switch button, and a return to the family board after a few idle minutes.
|
|
// Also the Settings part: your PIN for shared screens, and (admins) making this screen a family device.
|
|
let devOn = false, devIdle = 0;
|
|
const DEV_IDLE_MS = 5 * 60e3;
|
|
async function devBack() { try { await fetch('/api/auth/logout', { method: 'POST', body: '{}' }); } catch {} location.replace('/board'); }
|
|
async function devStart() {
|
|
try { const r = await jfetch('/api/device'); devOn = !!r.device; } catch { return; }
|
|
if (!devOn || !ME) return;
|
|
const kid = ME.kid && ME.kid.on, chip = document.createElement('div'); chip.id = 'devchip';
|
|
chip.innerHTML = `<span class="devwho"><b>${esc(ME.name)}</b>${kid ? '<i class="devkid">KID</i>' : ''}</span><button class="btn" id="devswitch">Switch user</button>`;
|
|
document.body.appendChild(chip); document.body.classList.add('ondevice'); chip.querySelector('#devswitch').onclick = devBack;
|
|
const bump = () => { devIdle = Date.now(); }; ['pointerdown', 'keydown', 'scroll', 'touchstart'].forEach(e => addEventListener(e, bump, { passive: true })); bump();
|
|
setInterval(() => { const a = typeof pl !== 'undefined' && pl.a && !pl.a.paused; if (!a && Date.now() - devIdle > DEV_IDLE_MS) devBack(); }, 15000); // (music playing keeps it signed in)
|
|
}
|
|
async function devSettings(box) {
|
|
if (!box) return; let r; try { r = await jfetch('/api/device'); } catch { return; }
|
|
const admin = ME && ME.role === 'admin', post = (p, b) => jfetch('/api/device/' + p, { method: 'POST', body: JSON.stringify(b || {}) });
|
|
box.innerHTML = `<h2>Shared screens</h2><div class="list">
|
|
<div class="row"><span class="grow"><b>${r.pin ? 'Your PIN is set' : 'Set a PIN'}</b><small style="white-space:normal">On a shared family screen, you pick your name and enter this PIN (children can just tap their name). 4 to 8 digits.</small></span><button class="btn ghost" id="devpin">${r.pin ? 'Change' : 'Set PIN'}</button>${r.pin ? '<button class="btn ghost" id="devpinx">Remove</button>' : ''}</div>
|
|
${admin ? `<div class="row"><span class="grow"><b>${r.device ? 'This is a family device' : 'Make this a family device'}</b><small style="white-space:normal">${r.device ? 'It opens on the family board and signs people out after a few idle minutes.' : 'For a kitchen tablet or the family computer: it opens on the family board, and everyone picks their name to use it. You will be signed out here.'}</small></span><button class="btn ${r.device ? 'ghost' : ''}" id="devtoggle">${r.device ? 'Turn off' : 'Turn on'}</button></div>` : ''}</div>`;
|
|
box.querySelector('#devpin').onclick = () => { const el = openSheet('Your PIN', 'Used to pick your name on a shared family screen.', '<input type="password" inputmode="numeric" pattern="[0-9]*" id="pn" maxlength="8" placeholder="4 to 8 digits" autocomplete="off"><div class="bar"><button class="btn" id="pns" style="flex:1;justify-content:center">Save</button></div>'); el.querySelector('#pns').onclick = async () => { try { await post('pin', { pin: el.querySelector('#pn').value }); closeSheet(); toast('PIN saved'); devSettings(box); } catch (e) { toast(e.message); } }; };
|
|
const x = box.querySelector('#devpinx'); if (x) x.onclick = async () => { try { await jfetch('/api/device/pin', { method: 'DELETE' }); devSettings(box); } catch (e) { toast(e.message); } };
|
|
const t = box.querySelector('#devtoggle'); if (t) t.onclick = () => r.device ? confirmSheet('Turn off family device?', 'This screen goes back to the normal sign-in.', 'Turn off', async () => { try { await post('remove'); location.replace('/login'); } catch (e) { toast(e.message); } }) : confirmSheet('Make this a family device?', 'You will be signed out, and this screen opens on the family board from now on.', 'Make it', async () => { try { await post('enable', { name: /iPad/.test(navigator.userAgent) ? 'iPad' : 'Family screen' }); location.replace('/board'); } catch (e) { toast(e.message); } });
|
|
}
|