fix(alarms): let an unanswered Undo lapse, and make delete and restore atomic

A snackbar the screen leaves unanswered (another tab, Settings, the app
closing) now counts as expired, except across a rotation. Held back, it
could restore a snapshot from hours ago, ring a long-gone timer, or
land on top of a backup restored in the meantime. One shared
UndoSnackbarEffect does this for alarms and timers.

The engines' delete and restore run non-cancellable, so a caller
cancelled half-way cannot leave a restored alarm unscheduled. The
editor writes the label still in flight before it deletes, so Undo
brings back what was typed.

The swipe counts the touch-slop overshoot, keeps a card flung back
towards the start, slides a card home if its row is still there a
second after the delete, and no longer recomposes on every drag frame.
This commit is contained in:
makiolaj committed 2026-10-21 20:13:09 +02:00
1 parent 2feed2ee35
commit ef26d433e1
12 files changed
+157 -74

No files matched your search

+8 -7
View File
@@ -1740,17 +1740,18 @@ alarm is the exception: deleting it was a dismissal in all but name, so it comes
back as a dismissal would have left it (cycle closed, a one-shot switched off)
and never rings again, and a ringing transient alarm, which that dismissal would
have deleted anyway, offers no Undo at all. A snooze that lapsed while the alarm
was gone is closed by the resolver on restore, and gets no notification. An id
that a backup restore claimed in the meantime makes the restore write nothing.
was gone is closed by the resolver on restore, and gets no notification.
The pending deletion lives in `AlarmUndo`, a singleton, not in either
ViewModel. The editor is popped the moment its alarm is gone, so its ViewModel
is cleared before any snackbar could be answered; the list underneath reads the
deletion from the singleton and shows the snackbar. It is cleared only when the
snackbar is answered or times out, or a newer deletion replaces it, so a
rotation or a trip to another tab re-shows the snackbar rather than losing the
Undo. It is memory only: an Undo that outlived process death would be offered
with no snackbar anyone remembers.
deletion from the singleton and shows the snackbar (`UndoSnackbarEffect`). It
is cleared when the snackbar is answered or times out, when a newer deletion
replaces it, and when the screen is left with the snackbar unanswered (another
tab, Settings, the app closed): an Undo held back and offered later would
restore a snapshot from long ago, out of any context, and over a backup that
was restored meanwhile. A rotation is the one exception and shows the snackbar
again. It is memory only, so process death drops it too.
The swipe is hand-rolled (`ui/common/SwipeToDelete`) rather than M3's
`SwipeToDismissBox`, because the tabs are pages of a `HorizontalPager` and the