Files
hub/public/automations-view.js
T

37 lines
5.9 KiB
JavaScript

'use strict';
// ---- Automations (Settings): "when this happens, do that" rules for you. When: every day at a time, a task is finished, or new mail arrives. Then: add a task, add to
// the Shopping list, or send yourself a notification. {title}, {from} and {subject} in the text are filled in from what happened.
const auApi = (p, o) => jfetch('/api/auto' + p, o), auOpt = o => ({ method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(o) });
const AU_WHEN = [['time', 'Every day at a time'], ['task.done', 'A task is finished'], ['mail.new', 'New mail arrives'], ['pantry.low', 'A pantry item runs low'], ['podcast.new', 'A new podcast episode comes out']], AU_THEN = [['task', 'Add a task'], ['shopping', 'Add to the Shopping list'], ['notify', 'Notify me']];
const auSummary = r => `${r.when.type === 'time' ? 'Every day at ' + r.when.at : r.when.type === 'task.done' ? 'When a task' + (r.when.match ? ' with “' + r.when.match + '”' : '') + ' is finished' : r.when.type === 'pantry.low' ? 'When ' + (r.when.match ? '“' + r.when.match + '”' : 'a pantry item') + ' runs low' : r.when.type === 'podcast.new' ? 'When a new episode of “' + r.when.match + '” comes out' : 'When mail from or about “' + r.when.match + '” arrives'} → ${r.then.type === 'task' ? 'add the task' : r.then.type === 'shopping' ? 'add to Shopping' : 'notify me'} “${r.then.text}”`;
function autoSettings(el) {
if (!el) return;
const draw = rules => {
el.innerHTML = `<h2>Automations</h2><p class="sub">Little rules that run for you. Each can run up to 20 times a day.</p>
<div class="list">${rules.map(r => `<div class="row" style="align-items:flex-start"><label class="grow" style="cursor:pointer;white-space:normal"><b>${esc(r.name)}</b><small style="white-space:normal">${esc(auSummary(r))}${r.runs ? ' · ran ' + r.runs + ' time' + (r.runs === 1 ? '' : 's') : ''}</small></label>
<input type="checkbox" data-on="${esc(r.id)}" aria-label="Turn ${esc(r.name)} on or off"${r.on ? ' checked' : ''}><button class="ibtn" data-e="${esc(r.id)}" aria-label="Edit ${esc(r.name)}" title="Edit">${icon('edit')}</button></div>`).join('') || '<div class="row"><span class="grow"><small>No automations yet.</small></span></div>'}</div>
<div class="bar"><button class="btn ghost" id="auadd">${icon('plus')}Add an automation</button></div>`;
el.querySelector('#auadd').onclick = () => edit(null);
el.querySelectorAll('[data-on]').forEach(c => c.onchange = async () => { try { draw((await auApi('/toggle', auOpt({ id: c.dataset.on, on: c.checked }))).rules); } catch (e) { toast(e.message); c.checked = !c.checked; } });
el.querySelectorAll('[data-e]').forEach(b => b.onclick = () => edit(rules.find(r => r.id === b.dataset.e)));
};
const edit = r => {
const w = r ? r.when : { type: 'time', at: '08:00', match: '' }, t = r ? r.then : { type: 'task', text: '' };
const sh = openSheet(r ? 'Edit automation' : 'New automation', '', `<form id="auf"><label for="aun">Name (optional)</label><input type="text" id="aun" value="${esc(r ? r.name : '')}" autocomplete="off">
<label for="auw">When</label><select id="auw">${AU_WHEN.map(([v, l]) => `<option value="${v}"${v === w.type ? ' selected' : ''}>${l}</option>`).join('')}</select>
<div id="auwd"></div><label for="aut">Then</label><select id="aut">${AU_THEN.map(([v, l]) => `<option value="${v}"${v === t.type ? ' selected' : ''}>${l}</option>`).join('')}</select>
<label for="aux" id="auxl"></label><input type="text" id="aux" value="${esc(t.text)}" autocomplete="off" maxlength="200"><p class="sub" style="margin:4px 0 0">You can use {title} (the task, item or episode), {from} and {show}.</p><div class="err" id="aue"></div>
<div class="bar"><button class="btn" style="flex:1;justify-content:center">Save</button>${r ? '<button type="button" class="btn ghost" id="autest">Test</button><button type="button" class="btn danger" id="aude">Delete</button>' : ''}</div></form>`);
const label = () => { sh.querySelector('#auxl').textContent = sh.querySelector('#aut').value === 'notify' ? 'Message' : 'Task'; };
const detail = () => { const ty = sh.querySelector('#auw').value; sh.querySelector('#auwd').innerHTML = ty === 'time' ? `<label for="aua">At</label><input type="time" id="aua" value="${esc(w.at || '08:00')}" required>` : `<label for="aum">${ty === 'mail.new' ? 'Sender or subject contains' : ty === 'podcast.new' ? 'Show name contains' : ty === 'pantry.low' ? 'Item name contains (blank: any item)' : 'Task title contains (blank: any task)'}</label><input type="text" id="aum" value="${esc(w.type === ty ? w.match || '' : '')}" autocomplete="off"${ty === 'mail.new' || ty === 'podcast.new' ? ' required' : ''}>`; label(); };
detail(); label(); sh.querySelector('#auw').onchange = detail; sh.querySelector('#aut').onchange = label;
const body = () => { const ty = sh.querySelector('#auw').value; return { id: r && r.id, name: sh.querySelector('#aun').value, on: r ? r.on : true, when: { type: ty, at: ty === 'time' ? sh.querySelector('#aua').value : undefined, match: ty === 'time' ? '' : sh.querySelector('#aum').value }, then: { type: sh.querySelector('#aut').value, text: sh.querySelector('#aux').value } }; };
sh.querySelector('#auf').onsubmit = async e => { e.preventDefault(); try { const l = (await auApi('/', auOpt(body()))).rules; closeSheet(); draw(l); toast('Saved'); } catch (er) { sh.querySelector('#aue').textContent = er.message; } };
if (r) {
sh.querySelector('#autest').onclick = async () => { try { const o = await auApi('/test', auOpt({ id: r.id })); toast(o.ok ? 'Ran it once' : 'Did not run (the daily limit?)'); } catch (e) { toast(e.message); } };
sh.querySelector('#aude').onclick = () => confirmSheet('Delete this automation?', r.name, 'Delete', async () => { try { draw((await auApi('/delete', auOpt({ id: r.id }))).rules); } catch (e) { toast(e.message); } });
}
};
auApi('/').then(r => draw(r.rules)).catch(() => {});
}