feat(messaging): message reminders - remind me about this (LC-63) #163
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/lc-63-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?
Summary
"Remind me about this message" (LC-63). Hover a message -> Remind -> pick a time; at that time the server pings the owner (Web Push + on-screen toast) with a deep link back to the message. Mirrors the LC-62 scheduled-message dispatcher.
Changes
0037_reminders.sql):reminders(id, user_id, message_id REFERENCES messages ON DELETE CASCADE, remind_at, fired_at, created_at). Pending whilefired_at IS NULL; FK cascade + soft-delete guard => a deleted message's reminder never fires. State in SQLite => survives restart.spawn_reminders_dispatcher, 30s tick): selects due rows, claims each atomically (UPDATE fired_at WHERE fired_at IS NULL= at-most-once), broadcasts aReminderWS event + fires Web Push. Deep link is/room/{id}#msg-{mid}(or/dm/{peer}#msg-{mid}for DMs).ChatEvent::Reminderreuses the notify pipeline but does not bump unread-mention counts and ignores room mute (explicit reminder).push::dispatch+payload::buildhandle it;routes/ws.rsrendersReminderFragmentinto#lc-notify-bus; the notify-bus client gets areminderbranch.GET /reminders(list pending + recently-fired),GET /reminders/picker?message_id=(hover-menu popover),POST /reminders(preset15m/1h/3h/tomorrowor custom datetime, room-access checked),DELETE /reminders/{id}(owner-scoped cancel). Picker swaps into a singleton#lc-reminder-modal; presets post via HTMX, custom converts to UTC client-side.Acceptance criteria
remind_atthrough enabled channels (Web Push + on-screen banner).Open questions from the issue (left as-is for v1): reminders for rooms the user has left still fire (the link resolves; access is re-checked on navigation); snooze-from-notification not implemented.
Tests
routes_reminders.rs: create+confirm, list, owner-scoped cancel, picker access gating, dispatcher fires-due / skips-future / no-double-fire, soft-deleted-message skip. Migration0037appended to hand-rolled lists.just check,just test,just test-saasgreen.🤖 Generated with Claude Code
Right-click/hover a message -> "Remind" -> pick a time; at that time the server pings the owner (Web Push + on-screen toast) with a deep link back to the message. Mirrors the scheduled-message dispatcher. Schema: migration 0037 adds reminders(id, user_id, message_id REFERENCES messages ON DELETE CASCADE, remind_at, fired_at, created_at). A row is pending while fired_at IS NULL; the FK cascade plus a soft-delete guard mean a deleted message's reminder never fires. State lives in SQLite, so reminders survive a restart. Dispatcher: spawn_reminders_dispatcher in main.rs polls every 30s. It selects due, not-yet-fired reminders (joined to message + room), claims each atomically (UPDATE fired_at WHERE fired_at IS NULL -> at-most-once), then broadcasts a Reminder event over the WS hub and fires Web Push. The deep link resolves to /room/{id}#msg-{mid}, or /dm/{peer}#msg-{mid} for DM rooms. Notification: new ChatEvent::Reminder reuses the existing notify pipeline. It does NOT bump the client's unread-mention counts (it is not a new message) and it ignores room mute (the user explicitly asked to be pinged). push::dispatch and payload::build now handle Reminder alongside Mentioned; routes/ws.rs renders a ReminderFragment into #lc-notify-bus; the layout notify-bus client gets a dedicated "reminder" branch that fires a banner without touching counts. Routes: GET /reminders (pending + recently-fired list), GET /reminders/picker?message_id= (the hover-menu popover), POST /reminders (preset 15m/1h/3h/tomorrow or a custom datetime, room-access checked), DELETE /reminders/{id} (owner-scoped cancel). The picker swaps into a singleton #lc-reminder-modal slot in the shell; presets post via HTMX, custom time converts to UTC client-side. Reminders are private: every query is scoped to the caller, and a reminder can only be set on a message in a room the caller can see. Account deletion now also drops the user's reminders (those on other people's messages; reminders on their own messages already cascade). Tests: routes_reminders.rs covers create+confirm, list, owner-scoped cancel, picker access gating, dispatcher fires-due/skips-future/no-double-fire, and the soft-deleted-message skip. Migration 0037 appended to the hand-rolled migration lists. just check, just test, just test-saas all green. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>