feat(todo): reminders at a time of their own planned and set in a task's sheet, durations in its date sheet

planReminders plans each reminder at a time of its own of an open task,
whatever its date, under an ID of its own; the notification says
"Reminder" for a task without a time. The task's sheet always has its
reminders' row: those before its time where it has one, those at a time
of their own each with its remove button and "Remind at a time" (a day,
then a time), the permission asked as a first one is chosen. The date
sheet sets a duration once there is a time (none, 15 minutes to 4
hours, as Todoist's), a time cleared taking it with it. Recorded as
ADR 0033, D18 (the developer's, 3 Nov 2026).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-11-03 13:56:19 +01:00
commit f5b657dfad
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -242,7 +242,8 @@ alternative was put to them, the choice is theirs.
changes as the person switches keyboards while typing. Symbols hold in every language.
As built (`parseQuickAdd`, `QuickAddWords.en` and `.de`): a `#` naming no project
stays text, an `@` naming no label makes one as the task is added; only the first of
each kind counts, the rest stays in the title; a time alone is its next to come
each kind counts, the rest stays in the title (but for labels and, since D18,
reminders); a time alone is its next to come
(tomorrow once it has passed today), a weekday alone the coming one (today too),
`next` one after today; a month needs its day (`may` alone is a word), a number alone
is none; a recurrence starts on its first day from today (`every weekday` on a Sunday:
@ -345,6 +346,53 @@ alternative was put to them, the choice is theirs.
on Upcoming's day (`moveTask`, shared with `DayDropArea`). A later time of a
repeating task doesn't move: only the task itself, at its next time, does. Dragging
past the three days shown waits until someone misses it.
- **D18 Quick add as Todoist's newsletter writes it** (the developer's, 3 Nov 2026, the
four recommended, after comparing a newsletter's examples): *Check work emails every
weekday at 9am !30m*, *Prepare document on Monday at 10am for 2h*, *Stop watching that
viral cat video !1pm !2pm !3pm !4pm*, *File my tax return every year on 1st March*.
- **`on` before a day** is taken with it (German `am`, `on` and `am` being words of the
tables, `QuickAddWords.on`), as `at` (`um`) before a time is; before anything that is
no day it stays text (*Work on report*, *Treffen am Bahnhof*). `am` after a number is
still the morning (*9 am*), read first as a time.
- **Durations**: `for` (`für`) and an amount with its unit, `for 2h`, `for 30m`, `for
1h30m`, `for 90 min`, `for 2 hours`, `für 2 Std`, `für 30 Min`, `für 1,5 Std.`, a
decimal point or comma either way, a day at most, into `TaskDate.duration`. The
units are the tables' (`durationUnits`), read by a pattern over the text, as a word
splits at a comma. **Only with a time**: a duration lasts from one, so without one
(*Read for 2h*, *Read tomorrow for 2h*) it stays text, as `!30m` on a day without a
time does (recommended, over giving the task a time to hold it, which would invent
one). The date sheet sets it once there is a time (*Duration*: none, 15, 30, 45
minutes, 1, 1½, 2, 3, 4 hours, as Todoist's time sheet offers), a time cleared taking
it with it; a row and the sheet show the span (*Tomorrow 14:00–15:30*) and the 3-day
grid a block as long, a block dragged keeping it (D17).
- **Every `!30m` counts**, each once, sorted (`[30, 60]`), as do the `@labels`; the
other kinds still count once. With a time, each is so long before it; without a date,
the task is due at the latest and each reminds that long from now (*Leave !30m !1h*:
due in an hour, reminded in 30 minutes and then); on a day without a time they stay
text. The title's highlighting marks each token, whatever its kind.
- **Reminders at a time of their own**: `!1pm`, `!13:00`, `!tomorrow 9am`, `!Monday
8:30`, `!morgen um 9 Uhr`, as many as typed, into a task's `reminderTimes`
(`ReminderTime`: a day and minutes after midnight, the wall clock without a zone,
as `TaskDate`'s, so one comes at that hour wherever the device is). **A time without
a day** is on the task's day where it has one (a recurrence's first), else today, or
tomorrow where that time has passed, as a time alone is (D15). Each is a moment of
its own: it neither needs a date nor follows one, a recurring task's coming once,
not every time. A `!` before no time (*!tomorrow*, *!9*) stays text. Planned with the
rest (`planReminders`, an ID of their own each), the notification saying *Due at
14:00* for a task with a time, *Reminder* for one without; the task's sheet now
always has its reminders' row, the reminders before its time only where it has
one, and those at a time of their own each with its ✕ and *Remind at a time* (a day,
then a time), the permission asked as a first one is chosen.
- **The payload's schema stays 1** (as D17's view): `reminderTimes`, as `[{"day":
"2026-11-03", "time": 780}]`, left out where there are none, so older payloads read
with none. And from now on **a task's payload keeps what this version doesn't know**
(`TaskContent.unknown`, the fields at its top that it doesn't read, written back
beneath the ones it does, which win): Todo isn't released yet, so every version out
keeps a newer one's fields as it changes a task, where Learn's and Train's payloads
drop them. Recommended over bumping the schema for each field, which would show a
task locked on every device not yet updated. A field a newer version adds within the
date, or one whose meaning hangs on fields an older one changes, isn't kept so; such
a change still bumps the schema. No server model changes: the payload is sealed.
## Steps