fix(11-05): CR-01 surface reminderIsCustom to preserve custom VALARMs on edit
- expand.ts: add reminderIsCustom:boolean to CalendarOccurrence interface; derived from classifyValarms kind==='custom'; propagated to both non-recurring and recurring occurrence branches - client.ts: mirror reminderIsCustom on CalendarOccurrence (atomic mirror) - EventForm.tsx: extend deriveReminderValue to accept isCustom flag; returns '__custom__' when true, making the existing D-08 preserve branch live — editing a custom-alarm event now omits reminderLeadMinutes from the payload so outboxWorker extractValarms keeps the original VALARM - Fix existing test fixtures (EventForm.test.tsx, EventDetailPopover.test.tsx) to include reminderIsCustom:false on all CalendarOccurrence literals Fixes CAL-14 Pitfall 1: Apple Calendar absolute DATE-TIME / multi-VALARM alarms no longer silently stripped on any edit round-trip from the PWA.
This commit is contained in:
@@ -75,12 +75,22 @@ function humanizeReminderLead(minutes: number): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive the initial reminder picker value from an occurrence's reminderLeadMinutes.
|
||||
* Returns '__none__' for null, the matching preset string for a preset, or the numeric
|
||||
* string for an off-list value (synthetic option will be rendered for this case).
|
||||
* There is no '__custom__' path here — the occurrence only carries number|null.
|
||||
* Derive the initial reminder picker value from an occurrence's reminder fields.
|
||||
*
|
||||
* CR-01 (Plan 11-05): When reminderIsCustom is true, the event carries an absolute
|
||||
* DATE-TIME trigger or multiple VALARMs that cannot be reduced to a single lead.
|
||||
* Returning '__custom__' makes the existing preserve branch live: the form emits an
|
||||
* absent reminderLeadMinutes field, and the outboxWorker's D-08 preserve path keeps
|
||||
* the original VALARM intact (no silent data loss on edit).
|
||||
*
|
||||
* Precedence: custom → '__custom__'; null → '__none__'; else preset/off-list string.
|
||||
*/
|
||||
function deriveReminderValue(leadMinutes: number | null, isAllDay: boolean): string {
|
||||
function deriveReminderValue(
|
||||
leadMinutes: number | null,
|
||||
isAllDay: boolean,
|
||||
isCustom = false,
|
||||
): string {
|
||||
if (isCustom) return '__custom__';
|
||||
if (leadMinutes === null) return '__none__';
|
||||
const presets = isAllDay ? ALLDAY_REMINDER_PRESETS : TIMED_REMINDER_PRESETS;
|
||||
if (presets.has(leadMinutes)) return String(leadMinutes);
|
||||
@@ -317,8 +327,13 @@ export function EventForm() {
|
||||
setLocation(occurrence?.location ?? '');
|
||||
setDescription(occurrence?.description ?? '');
|
||||
// Phase 11: derive reminder picker value from occurrence (edit-mode pre-population, D-01/D-07)
|
||||
// CR-01 (Plan 11-05): pass reminderIsCustom so custom alarms initialize to '__custom__'
|
||||
// instead of '__none__', making the D-08 preserve path reachable on edit.
|
||||
const occAllDay = occurrence?.allDay ?? false;
|
||||
setReminderValue(deriveReminderValue(occurrence?.reminderLeadMinutes ?? null, occAllDay));
|
||||
const occIsCustom = occurrence?.reminderIsCustom ?? false;
|
||||
setReminderValue(
|
||||
deriveReminderValue(occurrence?.reminderLeadMinutes ?? null, occAllDay, occIsCustom),
|
||||
);
|
||||
}
|
||||
}, [eventFormOpen, eventFormMode, eventFormUid, occurrence?.uid]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
|
||||
Reference in New Issue
Block a user