45 lines
4.8 KiB
JavaScript
45 lines
4.8 KiB
JavaScript
// Settings -> Reminders: turn on push notifications for tasks due today and events about to start (Web Push).
|
|
const remSupported = () => 'serviceWorker' in navigator && 'PushManager' in window && 'Notification' in window;
|
|
const b64ToBytes = s => { const p = '='.repeat((4 - s.length % 4) % 4), r = atob((s + p).replace(/-/g, '+').replace(/_/g, '/')); return Uint8Array.from([...r].map(c => c.charCodeAt(0))); };
|
|
const myTz = () => Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';
|
|
|
|
function remindersHtml() {
|
|
return `<div id="sec-rem"><h2>Reminders</h2><p class="sub">Get a notification for tasks due today and events about to start, even when the app is closed. Turn it on for each device you want reminders on.</p>
|
|
<div class="list" id="remlist"><div class="row"><span class="grow"><small>Checking…</small></span></div></div></div>`;
|
|
}
|
|
|
|
async function remindersInit() {
|
|
const box = document.getElementById('remlist'); if (!box) return;
|
|
if (!(allowed('tasks') || allowed('calendar'))) return document.getElementById('sec-rem').remove();
|
|
if (!remSupported()) { box.innerHTML = `<div class="row"><span class="grow"><b>Not available here</b><small style="white-space:normal">${/iPhone|iPad/.test(navigator.userAgent) ? 'On iPhone and iPad, add NE Hub to the Home Screen first (Share → Add to Home Screen), then open it from there.' : 'This browser does not support push notifications.'}</small></span></div>`; return; }
|
|
let info; try { info = await jfetch('/api/push/key'); } catch (e) { box.innerHTML = `<div class="row"><span class="grow"><small>${esc(e.message)}</small></span></div>`; return; }
|
|
const reg = await navigator.serviceWorker.ready, sub = await reg.pushManager.getSubscription();
|
|
const on = !!sub && Notification.permission === 'granted';
|
|
const post = (p, body) => jfetch('/api/push/' + p, { method: 'POST', body: JSON.stringify({ tz: myTz(), ...body }) });
|
|
box.innerHTML = `<label class="row" style="cursor:pointer"><span class="grow"><b>Reminders on this device</b><small id="remst" style="white-space:normal">${Notification.permission === 'denied' ? 'Blocked in the browser settings for this site; allow notifications there first' : on ? 'On' : 'Off'}${info.devices ? ' · ' + info.devices + (info.devices === 1 ? ' device' : ' devices') + ' in total' : ''}</small></span><input type="checkbox" id="remon" ${on ? 'checked' : ''} ${Notification.permission === 'denied' ? 'disabled' : ''}></label>
|
|
${allowed('tasks') ? `<label class="row" style="cursor:pointer"><span class="grow"><b>Tasks due today</b><small>One summary each morning</small></span><input type="checkbox" id="remt" ${info.tasks ? 'checked' : ''}></label>
|
|
<div class="row"><span class="grow"><b>Morning time</b></span><input type="time" id="remat" value="${esc(info.tasksAt)}" style="width:auto;min-width:130px"></div>` : ''}
|
|
${allowed('calendar') ? `<label class="row" style="cursor:pointer"><span class="grow"><b>Upcoming events</b><small>Shortly before they start</small></span><input type="checkbox" id="reme" ${info.events ? 'checked' : ''}></label>
|
|
<div class="row"><span class="grow"><b>How early</b></span><select id="remm" style="width:auto">${[5, 10, 15, 30, 60].map(m => `<option value="${m}" ${m === info.eventMins ? 'selected' : ''}>${m} min</option>`).join('')}</select></div>` : ''}
|
|
<div class="row"><span class="grow"><small>Send a test to every device that is on</small></span><button class="btn ghost" id="remtest" style="min-height:36px">Test</button></div>`;
|
|
const on1 = document.getElementById('remon');
|
|
on1.onchange = async () => {
|
|
try {
|
|
if (on1.checked) {
|
|
const perm = Notification.permission === 'default' ? await Notification.requestPermission() : Notification.permission;
|
|
if (perm !== 'granted') { on1.checked = false; return toast('Notifications were not allowed'); }
|
|
const s = (await reg.pushManager.getSubscription()) || await reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: b64ToBytes(info.publicKey) });
|
|
await post('subscribe', { subscription: s.toJSON() }); toast('Reminders are on for this device');
|
|
} else {
|
|
const s = await reg.pushManager.getSubscription();
|
|
if (s) { await post('unsubscribe', { endpoint: s.endpoint }); await s.unsubscribe(); }
|
|
toast('Reminders are off for this device');
|
|
}
|
|
remindersInit();
|
|
} catch (e) { on1.checked = !on1.checked; toast(e.message); }
|
|
};
|
|
const pref = (id, key, get) => { const el = document.getElementById(id); if (el) el.onchange = () => post('prefs', { [key]: get(el) }).catch(e => toast(e.message)); };
|
|
pref('remt', 'tasks', e => e.checked); pref('remat', 'tasksAt', e => e.value); pref('reme', 'events', e => e.checked); pref('remm', 'eventMins', e => +e.value);
|
|
document.getElementById('remtest').onclick = () => post('test', {}).then(() => toast('Test sent')).catch(e => toast(e.message));
|
|
}
|