fix(alarms): never announce an alarm that has since stopped ringing

A notice is now re-checked against the engine before a screen shows it,
so an alarm switched off or deleted by any path - the ring screen, a
notification, an intent, a restore - is never announced as set. A
retract takes the publish lock, so a publish already past its read
cannot put the notice back.

Settling is per alarm: switching one alarm off in the list no longer
cancels the repeat days still settling on another. The identical-words
dedupe is gone, since it could swallow a real confirmation.

The editor's writes that move a fire time, with their reschedule and
announcement, run on the application scope. Pick, OK, back could
otherwise cancel the reschedule half-way and leave a stored time the
system had not been given. upcomingFor() reads alarm and state under
the engine lock, so a fire cannot land between the two reads.

Refs #19
This commit is contained in:
makiolaj committed 2026-10-21 19:18:19 +02:00
1 parent fa5ce55503
commit d2bf4349c5
6 files changed
+100 -53

No files matched your search

+9 -7
View File
@@ -1126,13 +1126,15 @@ at once, but waits for repeat-day taps to settle for a second, so Mon–Fri is
one snackbar rather than five. `AlarmSetAnnouncer` is an app-wide singleton
holding the notice as *state* until a screen has shown it to the end: pick →
OK → back disposes the editor's snackbar a moment after it appears, and the
list underneath picks the same notice up. The settle runs on the application
scope, so leaving the editor mid-settle still announces; a screen shows the
notice only while resumed, so a cross-fade never shows it twice; the editor
shows only notices about its own alarm; deleting an alarm, or the `AlarmClock`
chooser dismissing it, takes its notice back. A notice older than ten seconds
on the monotonic clock is dropped unshown rather than shown with a stale
countdown.
list underneath picks the same notice up. The editor's fire-time-moving
writes, their reschedule and the per-alarm settle all run on the application
scope, so leaving the editor at once neither loses the announcement nor leaves
a stored time unscheduled. A screen shows the notice only while resumed, so a
cross-fade never shows it twice; the editor shows only notices about its own
alarm; a notice is re-checked against the engine before it is shown, so an
alarm switched off or deleted by any path is never announced as set. A notice
older than ten seconds on the monotonic clock is dropped unshown rather than
shown with a stale countdown.
`upcoming(refresh: Flow<Unit>)` takes its **re-read cadence as a parameter**.
The resolver needs a `now`, and `upcoming` reads one inside a `combine` of the