feat(todo): a place reminds once a day, from the task's day on (ADR 0033, D32)

A task for tomorrow stays quiet today; an overdue task or one without a
day reminds on each day the phone arrives; a daily task once a day, not
again after it was ticked off in the morning. Todoist's location
reminders ignore the due date, which r/todoist's reports show reminding
of a weekly task on every arrival and of a daily one up to twenty times
a day; once until the task changed had both faults and lost the
shopping list left open to remind on every visit.

The device keeps which tasks reminded today, and an inexact alarm at
midnight lets the new day's places wait. A place waiting again on a new
day reminds only after the phone crosses its circle, so a task whose
place is home doesn't remind just after midnight.

Whats-New: A task's place reminds once a day, and not before the task's day.
Whats-New-De: Der Ort einer Aufgabe erinnert einmal am Tag und nicht vor ihrem Tag.

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-07 19:38:47 +01:00
commit 30ee2a9ca3
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -4,9 +4,9 @@ import 'package:halkyon_todo_flutter/src/feature/reminder/model/reminder_plan.da
import 'package:halkyon_todo_flutter/src/feature/task/model/task.dart';
/// A place the device is to watch for the task [taskId] (ADR 0033, D32): on
/// [arrive] or leaving it, it shows [title]. [id] is the same on every plan;
/// [stamp] changes with the task, so a task that has reminded does again
/// once it changes.
/// [arrive] or leaving it, it shows [title], once a day, from the day [from]
/// on (any day without one). [id] is the same on every plan; [stamp] changes
/// with the task, so a task that has reminded does again once it changes.
final class const LocationWatch({
required final int id,
required final String taskId,
@ -17,6 +17,9 @@ final class const LocationWatch({
required final double lon,
required final bool arrive,
/// The task's day: the first it reminds on. Null for a task without one.
final Day? from,
/// What its notification says below [title], in the app's language.
final String body = '',
}) with Equatable {
@ -30,6 +33,7 @@ final class const LocationWatch({
lat: lat,
lon: lon,
arrive: arrive,
from: from,
body: body,
);
@ -43,6 +47,7 @@ final class const LocationWatch({
'lat': lat,
'lon': lon,
'arrive': arrive,
'from': ?from?.toString(),
'body': body,
};
@ -56,11 +61,13 @@ final class const LocationWatch({
lat,
lon,
arrive,
from,
body,
];
}
/// The places to watch: every location with a reminder of each open task.
/// The places to watch: every location with a reminder of each open task,
/// from the task's day on (ADR 0033, D32's amendment of 7 Nov 2026).
List<LocationWatch> planLocationWatches(List<StoredTask> tasks) => [
for (final StoredTask(:id, :content) in tasks)
if (content case TaskContent(completed: false, :final locations))
@ -78,6 +85,7 @@ List<LocationWatch> planLocationWatches(List<StoredTask> tasks) => [
lat: location.lat,
lon: location.lon,
arrive: location.trigger == LocationTrigger.arrive,
from: content.date?.day,
),
];