feat(todo): Settings' Tasks and Reminders, with Snooze on a reminder (ADR 0033, D4)
Settings gains Todo's own groups, kept in the person's sealed settings row on every device. Tasks: the weekday weeks start on (the language's unless set), which the week strip, the date and repeat pickers and the filters follow; and whether a recurring task done opens its sub-tasks again (on at first). Reminders: an automatic reminder (none at first) that TaskRepository writes into a task gaining a time without reminders of its own, and how long a reminder is snoozed for (15 minutes at first). Each reminder's notification has a Snooze action on Android and Linux: the reminder comes back that much later under an ID of its own, scheduled from what the notification carries, so on Android with the app closed too (the plugin's ActionBroadcastReceiver declared); the plan of the person's reminders leaves snoozed ones alone. iOS, macOS and Windows get it later. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
2939437c74
commit
51ebed41cd
1 changed files with 1204 additions and 69 deletions
|
|
@ -134,6 +134,93 @@ void main() {
|
|||
);
|
||||
});
|
||||
|
||||
test('a recurring task done leaves its sub-tasks done where the person '
|
||||
'turned resetting them off (D4)', () async {
|
||||
final phone = await device(alice);
|
||||
final weekly = milk.copyWith(
|
||||
date: () => milk.date!.repeating(const Repeat(RepeatUnit.week)),
|
||||
);
|
||||
final id = await phone.add(weekly);
|
||||
final doneAt = DateTime(2026, 10, 30);
|
||||
final subId = await phone.add(
|
||||
TaskContent(
|
||||
title: 'Oat',
|
||||
projectId: 'inbox',
|
||||
parentId: id,
|
||||
completedAt: doneAt,
|
||||
),
|
||||
);
|
||||
|
||||
final before = await withClock(
|
||||
Clock.fixed(DateTime(2026, 11, 2, 9, 30)),
|
||||
() => phone.complete(StoredTask(id, weekly), reopenSubTasks: false),
|
||||
);
|
||||
|
||||
final after = {for (final t in await phone.all()) t.id: t.content};
|
||||
expect(after[id]?.date?.day, const Day(2026, 11, 9));
|
||||
expect(after[subId]?.completedAt, doneAt);
|
||||
expect([for (final t in before) t.id], [id]);
|
||||
});
|
||||
|
||||
group('the automatic reminder (D4)', () {
|
||||
Future<TaskRepository> reminding(int? minutes) async => TaskRepository(
|
||||
rows,
|
||||
await keysOf(alice),
|
||||
automaticReminder: () async => minutes,
|
||||
);
|
||||
|
||||
const untimed = TaskContent(
|
||||
title: 'Call',
|
||||
projectId: 'inbox',
|
||||
date: TaskDate(Day(2026, 11, 2)),
|
||||
);
|
||||
|
||||
test(
|
||||
'a task added with a time and no reminder of its own gets it',
|
||||
() async {
|
||||
final phone = await reminding(30);
|
||||
final timed = await phone.add(milk);
|
||||
final own = await phone.add(milk.copyWith(reminders: [10]));
|
||||
final dayOnly = await phone.add(untimed);
|
||||
final contents = {for (final t in await phone.all()) t.id: t.content};
|
||||
expect(contents[timed]?.reminders, [30]);
|
||||
expect(contents[own]?.reminders, [10]);
|
||||
expect(contents[dayOnly]?.reminders, isEmpty);
|
||||
},
|
||||
);
|
||||
|
||||
test('a task given a time gets it; one that had a time keeps what the '
|
||||
'person left it, none too', () async {
|
||||
final phone = await reminding(0);
|
||||
final id = await phone.add(untimed);
|
||||
await phone.update(
|
||||
id,
|
||||
untimed.copyWith(
|
||||
date: () => const TaskDate(Day(2026, 11, 2), time: 540),
|
||||
),
|
||||
);
|
||||
var content = (await phone.all()).single.content!;
|
||||
expect(content.reminders, [0]);
|
||||
|
||||
await phone.update(id, content.copyWith(reminders: const []));
|
||||
await phone.update(
|
||||
id,
|
||||
content.copyWith(
|
||||
reminders: const [],
|
||||
date: () => const TaskDate(Day(2026, 11, 3), time: 600),
|
||||
),
|
||||
);
|
||||
content = (await phone.all()).single.content!;
|
||||
expect(content.reminders, isEmpty);
|
||||
});
|
||||
|
||||
test('none set, nothing is added', () async {
|
||||
final phone = await reminding(null);
|
||||
await phone.add(milk);
|
||||
expect((await phone.all()).single.content?.reminders, isEmpty);
|
||||
});
|
||||
});
|
||||
|
||||
test('a recurring task done its last time is done, its sub-tasks as they '
|
||||
'were', () async {
|
||||
final phone = await device(alice);
|
||||
|
|
|
|||
Loading…
Reference in a new issue