notify: event reminders with a per-event lead time #103
No reviewers
Labels
No labels
adr
android
area/calendar
area/design-system
area/i18n
area/jobs
area/offline
area/server
area/testing
bug
ci
duplicate
enhancement
help wanted
invalid
notifications
question
reliability
security
severity/low
severity/medium
tracking
web
wontfix
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
eagraiclainne/app!103
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/event-reminders"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Stacked on #102 (merge #101 → #102 → this; Forgejo retargets down the chain). Closes #98 — the last of the reminders-audit tickers, and the first to fire on exact instants.
The gap. An appointment, a birthday or an
EVENT_TYPE_DEADLINEon the calendar never reminded anyone before it happened. PR #90's kinds fire at creation and cancellation; the day itself passed in silence.Per-event lead time. Each event can carry
reminder_lead_minutes(0 = no reminder, the default — reminders are the creator's choice, not ambient noise).EVENT_REMINDERrings at start minus lead. Per the design decisions on the issue: a deadline-type event with a reminder also rings a day ahead of its start, and birthday reminders broadcast to the whole family — a birthday with one member on it defeats its purpose. Other events ring their members only. The board is speaking, so recipients are their own actor, like the meal reminder.Events gain a stored time zone. Requests always carried
time_zonetransiently for span validation; the ticker has no request. The event now keeps the zone whenever a create or update names one (theItemRepeat.time_zoneprecedent), and reminder projection plus message rendering run in it — "“Reminder Test” starts at 2026-08-16 16:46" comes out in Europe/Dublin, not UTC.The ticker. Third consumer of the #97 seam. Each scan projects occurrences through
domain.ExpandEventOccurrences(45-day lookahead), skips slots owned by child rows (an override speaks for itself; a tombstoned slot is silent), and fires every nudge whose window is open:fireAt ≤ now < start— after the start, silence, which is also the catch-up bound. Claims are a compare-and-set token"<slot>|<phase>"onEvent.reminder_last_sent— bookkeeping without a mutation entry, like the other tickers. The ahead phase is superseded once the lead fire is due, so a fired lead can never let the day-ahead nudge ring again behind it.Surfaces. The event form on web and Android gains a "Remind everyone going?" choice row (no / 15 min / an hour / a day before), saved through create and update on both. The Android occurrence cache learns the lead (schema v14, drop-and-rebuild as ever) so the edit sheet prefills honestly instead of silently zeroing a stored lead. Usual kind plumbing throughout: proto enum, notify method, en+ga messages, webhook decorator, web push-preference checkbox +
PUSH_KINDS, Android channel.make checkand the Android unit tests are green; the ticker has specs for the lead window, the claim, the missing-lead silence, the deadline day-ahead phase and the next-wake computation, and the lead reminder was verified against the live deploy end to end. On-device Android verification pending, as with #102.Test report
Coverage: 28.0%
Updated by the check workflow · commit
2b2c5e8ba8Android test report
Coverage: 2.4% of lines
Updated by the android workflow · commit
2b2c5e8ba8