refactor: fix bugs in calendar logic across duration parsing, RFC compliance, and event handling
- Fix buildDuration() trailing "T" producing invalid ISO 8601 durations - Fix DURATION_RE missing week (W) support in alerts and invitation parsing - Fix computeFireTime() end fallback when utcEnd is missing - Fix recurrenceOverrides patch escaping per RFC 6901 (updateEvent/rsvpEvent) - Fix layoutOverlappingEvents endMin overflow past 1440 - Fix addDurationToDate() to support weeks and use UTC methods for UTC inputs - Fix getEffectiveAlerts() null guard on calendarIds - Fix buildAllDayDuration() DST-safe day calculation using differenceInCalendarDays - Fix participant matching to check calendarAddress and sendTo (not just email) - Fix buildParticipantMap() using crypto.randomUUID() instead of hardcoded IDs - Fix overnight preview negative endMin in week view - Fix sendImipInvitation() to emit DURATION when utcEnd is absent - Fix sendImipCancellation() to validate status before sending - Fix createEvent() to remap all calendarIds for shared calendars - Fix getCalendarTasks() to clone before mutating @type - Fix importEvents() error matching to include 'duplicate' and 'conflict' - Fix looksLikeReply() false positive by requiring organizer + responded attendee - Fix alert offset regex to require T before minutes - Fix handleDuplicate() to generate new UID - Fix formatSnapTime() input clamping - Replace console.log/error with debug.log/error/warn in iMIP functions
This commit is contained in:
+18
-9
@@ -6,6 +6,7 @@ import type {
|
||||
Calendar,
|
||||
CalendarTask,
|
||||
} from '@/lib/jmap/types';
|
||||
import { parseDuration } from '@/components/calendar/event-card';
|
||||
|
||||
export interface PendingAlert {
|
||||
eventId: string;
|
||||
@@ -17,19 +18,20 @@ export interface PendingAlert {
|
||||
|
||||
const STALE_THRESHOLD_MS = 10 * 60 * 1000; // 10 minutes
|
||||
|
||||
const DURATION_RE = /^(-?)P(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/;
|
||||
const DURATION_RE = /^(-?)P(?:(\d+)W)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/;
|
||||
|
||||
export function parseAlertOffset(offset: string): number | null {
|
||||
const match = DURATION_RE.exec(offset);
|
||||
if (!match) return null;
|
||||
|
||||
const negative = match[1] === '-';
|
||||
const days = parseInt(match[2] || '0', 10);
|
||||
const hours = parseInt(match[3] || '0', 10);
|
||||
const minutes = parseInt(match[4] || '0', 10);
|
||||
const seconds = parseInt(match[5] || '0', 10);
|
||||
const weeks = parseInt(match[2] || '0', 10);
|
||||
const days = parseInt(match[3] || '0', 10);
|
||||
const hours = parseInt(match[4] || '0', 10);
|
||||
const minutes = parseInt(match[5] || '0', 10);
|
||||
const seconds = parseInt(match[6] || '0', 10);
|
||||
|
||||
const ms = ((days * 24 * 60 * 60) + (hours * 60 * 60) + (minutes * 60) + seconds) * 1000;
|
||||
const ms = ((weeks * 7 * 24 * 60 * 60) + (days * 24 * 60 * 60) + (hours * 60 * 60) + (minutes * 60) + seconds) * 1000;
|
||||
return negative ? -ms : ms;
|
||||
}
|
||||
|
||||
@@ -47,9 +49,15 @@ export function computeFireTime(
|
||||
|
||||
let baseTime: number;
|
||||
if (trigger.relativeTo === 'end') {
|
||||
baseTime = event.utcEnd
|
||||
? new Date(event.utcEnd).getTime()
|
||||
: new Date(event.start).getTime();
|
||||
if (event.utcEnd) {
|
||||
baseTime = new Date(event.utcEnd).getTime();
|
||||
} else {
|
||||
// Compute end from start + duration
|
||||
const startMs = new Date(event.start).getTime();
|
||||
if (Number.isNaN(startMs)) return null;
|
||||
const durationMin = parseDuration(event.duration);
|
||||
baseTime = startMs + durationMin * 60000;
|
||||
}
|
||||
} else {
|
||||
baseTime = event.utcStart
|
||||
? new Date(event.utcStart).getTime()
|
||||
@@ -68,6 +76,7 @@ export function getEffectiveAlerts(
|
||||
return event.alerts;
|
||||
}
|
||||
|
||||
if (!event.calendarIds) return null;
|
||||
const calendarId = Object.keys(event.calendarIds)[0];
|
||||
if (!calendarId) return null;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user