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

@ -14,6 +14,7 @@ void main() {
String id, {
TaskDate? date,
List<int> reminders = const [30],
List<ReminderTime> reminderTimes = const [],
DateTime? completedAt,
}) => StoredTask(
id,
@ -22,6 +23,7 @@ void main() {
projectId: 'p',
date: date,
reminders: reminders,
reminderTimes: reminderTimes,
completedAt: completedAt,
),
);
@ -93,6 +95,53 @@ void main() {
expect([for (final r in plan) r.taskId], ['1', '2', '3']);
});
test('a reminder at a time of its own (D18), whatever the date, among '
'the others by when it comes', () {
final plan = planReminders([
task(
'timed',
date: const TaskDate(today, time: 10 * 60),
reminders: [0],
reminderTimes: const [ReminderTime(today, 9 * 60)],
),
task(
'undated',
reminders: const [],
reminderTimes: const [
ReminderTime(today, 13 * 60),
ReminderTime(today, 14 * 60),
ReminderTime(today, 7 * 60),
],
),
task(
'day only',
date: const TaskDate(Day(2026, 11, 5)),
reminderTimes: const [ReminderTime(Day(2026, 11, 3), 8 * 60 + 30)],
),
task(
'done',
completedAt: now,
reminderTimes: const [ReminderTime(today, 12 * 60)],
),
], now: now);
expect(
[for (final r in plan) (r.taskId, r.at, r.minutesBefore, r.time)],
[
('timed', DateTime(2026, 11, 2, 9), null, 10 * 60),
('timed', DateTime(2026, 11, 2, 10), 0, 10 * 60),
('undated', DateTime(2026, 11, 2, 13), null, null),
('undated', DateTime(2026, 11, 2, 14), null, null),
('day only', DateTime(2026, 11, 3, 8, 30), null, null),
],
reason: 'the one at 7:00 is past',
);
expect(
plan.map((r) => r.id).toSet(),
hasLength(plan.length),
reason: "each its own notification, apart from the task's others",
);
});
test("a reminder's ID is the same on every plan, and differs between a "
"task's reminders and between tasks", () {
PlannedReminder only(String id, int before) => planReminders([