Tasks: a due time with a reminder, and Today and Upcoming views

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-27 10:19:05 -04:00
1 parent d761020869
commit fe651a8c37
6 files changed
+43 -20

No files matched your search

+5 -4
View File
@@ -81,6 +81,7 @@ function validParent(d, raw, parent, selfId) {
if (selfId && d.tasks.some(x => x.parent === selfId)) throw new Error('A task that has subtasks cannot become a subtask');
return parent;
}
const cleanTime = v => /^([01]\d|2[0-3]):[0-5]\d$/.test(String(v || '')) ? String(v) : ''; // a time of day on the due date, for a reminder at that time
const fieldsOf = t => ({ title: t.title, notes: t.notes, due: t.due, done: t.done });
async function handle(req, res) {
@@ -107,7 +108,7 @@ async function handle(req, res) {
const list = q.get('list');
if (isLocal(list)) {
const raw = list.slice(6);
return json(res, 200, load().tasks.filter(t => t.list === raw).map(t => ({ id: t.id, list, title: t.title, notes: t.notes || '', due: t.due || '', done: !!t.done, completedAt: t.completedAt || null, repeat: t.repeat || 'none', parent: t.parent || '', by: t.by || '' })));
return json(res, 200, load().tasks.filter(t => t.list === raw).map(t => ({ id: t.id, list, title: t.title, notes: t.notes || '', due: t.due || '', dueTime: t.dueTime || '', done: !!t.done, completedAt: t.completedAt || null, repeat: t.repeat || 'none', parent: t.parent || '', by: t.by || '' })));
}
if (caldav.todoLists().some(l => l.id === list)) {
const rows = await caldav.listTodoRaw(list), tasks = [];
@@ -125,14 +126,14 @@ async function handle(req, res) {
if (b.id) {
const t = d.tasks.find(x => x.id === b.id); if (!t) throw new Error('Task not found');
const wasDone = !!t.done, repeat = f.repeat === undefined ? t.repeat || 'none' : cleanRepeat(f.repeat);
Object.assign(t, { title: f.title.trim(), notes: f.notes || '', due: f.due || '', repeat, updated: Date.now() });
Object.assign(t, { title: f.title.trim(), notes: f.notes || '', due: f.due || '', dueTime: f.due ? cleanTime(f.dueTime) : '', repeat, updated: Date.now() });
if (f.parent !== undefined) t.parent = validParent(d, raw, f.parent, t.id) || undefined;
const rolled = finishLocal(t, !!f.done);
if (wasDone && f.done) { t.done = true; t.completedAt = t.completedAt || Date.now(); }
if (b.moveTo && isLocal(b.moveTo) && !t.parent) { const to = b.moveTo.slice(6); d.tasks.filter(x => x.parent === t.id).forEach(x => { x.list = to; }); t.list = to; }
save(d); return json(res, 200, { ok: true, rolled });
}
d.tasks.push({ id: crypto.randomUUID(), list: raw, title: f.title.trim(), notes: f.notes || '', due: f.due || '', done: !!f.done, completedAt: f.done ? Date.now() : null, repeat: cleanRepeat(f.repeat), parent: validParent(d, raw, f.parent, null) || undefined, created: Date.now(), updated: Date.now(), by: users.current().name, byId: users.current().id }); // (who added it, so a shared list shows whose it was)
d.tasks.push({ id: crypto.randomUUID(), list: raw, title: f.title.trim(), notes: f.notes || '', due: f.due || '', dueTime: f.due ? cleanTime(f.dueTime) : '', done: !!f.done, completedAt: f.done ? Date.now() : null, repeat: cleanRepeat(f.repeat), parent: validParent(d, raw, f.parent, null) || undefined, created: Date.now(), updated: Date.now(), by: users.current().name, byId: users.current().id }); // (who added it, so a shared list shows whose it was)
save(d); return json(res, 200, { ok: true });
}
if (b.href) { // edit an iCloud task: patch the latest copy so alarms/priority/etc. survive
@@ -182,7 +183,7 @@ async function handle(req, res) {
/** Every task from every list (NE Hub and iCloud), with its list name. */
async function allTasks() {
const d = load(), out = [];
for (const t of d.tasks) { const l = d.lists.find(x => x.id === t.list); out.push({ id: t.id, list: 'local:' + t.list, listName: l ? l.name : 'Tasks', title: t.title, notes: t.notes || '', due: t.due || '', done: !!t.done, repeat: t.repeat || 'none', parent: t.parent || '', by: t.by || '' }); }
for (const t of d.tasks) { const l = d.lists.find(x => x.id === t.list); out.push({ id: t.id, list: 'local:' + t.list, listName: l ? l.name : 'Tasks', title: t.title, notes: t.notes || '', due: t.due || '', dueTime: t.dueTime || '', done: !!t.done, repeat: t.repeat || 'none', parent: t.parent || '', by: t.by || '' }); }
for (const l of caldav.todoLists()) { try { for (const r of await caldav.listTodoRaw(l.id)) for (const t of parseTodos(r.ics)) out.push({ ...t, list: l.id, listName: l.name, href: r.href, etag: r.etag }); } catch {} }
return out;
}