feat(reminders): view and cancel a message's existing reminders

There was no way to see or cancel a reminder once set (removeReminder was only
called by the fire-and-forget monitor), and addReminder didn't dedupe, so a
message could silently accumulate duplicate reminders. The Remind Me dialog now
lists the reminders already set on that message (soonest first) each with a
cancel button.

- New shared, tested formatFriendlyDateTime(ts, now?) in utils/datetimeInput.ts
  (Today/Tomorrow/date + time).
- Per-row cancel busy-guard; inline "Could not cancel" on failure.

Also applies two nits from the custom-time review: focus the date input when the
custom picker is revealed, and clear the error when editing date/time.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 15:51:11 -04:00
co-authored by Claude Opus 4.8
parent b6725a6ee3
commit 7d67cdeef0
4 changed files with 123 additions and 5 deletions
+17
View File
@@ -23,6 +23,23 @@ export function parseLocalDateTime(dateValue: string, timeValue: string): Date |
return Number.isNaN(dt.getTime()) ? null : dt;
}
// Human-friendly absolute time: "Today at 3:00 PM", "Tomorrow at 9:00 AM", or
// "1/5/2026 at 3:00 PM". `now` is injectable so the relative-day logic is testable.
export function formatFriendlyDateTime(ts: number, now: number = Date.now()): string {
const date = new Date(ts);
const nowDate = new Date(now);
const sameDay = (a: Date, b: Date): boolean =>
a.getFullYear() === b.getFullYear() &&
a.getMonth() === b.getMonth() &&
a.getDate() === b.getDate();
const tomorrow = new Date(nowDate);
tomorrow.setDate(tomorrow.getDate() + 1);
const timeStr = date.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });
if (sameDay(date, nowDate)) return `Today at ${timeStr}`;
if (sameDay(date, tomorrow)) return `Tomorrow at ${timeStr}`;
return `${date.toLocaleDateString()} at ${timeStr}`;
}
// Shared style for date/time <input>s — matches the app's surface tokens and
// hints a dark-mode calendar/clock popup via colorScheme.
export function pickerInputStyle(