refactor: read event durations without loading the calendar UI

This commit is contained in:
Linus Rath
2026-11-03 05:41:35 +01:00
parent bad156eaed
commit fdeb7963ba
5 changed files with 19 additions and 17 deletions
+1 -14
View File
@@ -4,6 +4,7 @@ import { useCallback, useRef, useState, type CSSProperties, type DragEvent } fro
import { useTranslations } from "next-intl";
import { cn } from "@/lib/utils";
import type { CalendarEvent, Calendar } from "@/lib/jmap/types";
import { parseDuration } from "@/lib/calendar-event-normalization";
import { format } from "date-fns";
import { ChevronDown, ChevronUp, Users } from "@/components/icons";
import { getParticipantCount, isDeclinedByUser } from "@/lib/calendar-participants";
@@ -101,20 +102,6 @@ export function eventFillClass(tinted: boolean, inactive: boolean): string | und
return tinted && !inactive ? "start-bar" : undefined;
}
function parseDuration(duration: string | undefined): number {
if (!duration) return 0;
let totalMinutes = 0;
const weekMatch = duration.match(/(\d+)W/);
const hourMatch = duration.match(/(\d+)H/);
const minMatch = duration.match(/(\d+)M/);
const dayMatch = duration.match(/(\d+)D/);
if (weekMatch) totalMinutes += parseInt(weekMatch[1]) * 7 * 24 * 60;
if (dayMatch) totalMinutes += parseInt(dayMatch[1]) * 24 * 60;
if (hourMatch) totalMinutes += parseInt(hourMatch[1]) * 60;
if (minMatch) totalMinutes += parseInt(minMatch[1]);
return totalMinutes;
}
function createEventDragPreview(title: string, timeRange: string, color: string): HTMLElement {
const el = document.createElement("div");
el.style.cssText = `
+1 -1
View File
@@ -7,7 +7,7 @@ import type {
Calendar,
CalendarTask,
} from '@/lib/jmap/types';
import { parseDuration } from '@/components/calendar/event-card';
import { parseDuration } from '@/lib/calendar-event-normalization';
export interface PendingAlert {
eventId: string;
+15
View File
@@ -39,6 +39,21 @@ export function parseDurationSeconds(duration: string | undefined): number | nul
return (((weeks * 7 + days) * 24 + hours) * 60 + minutes) * 60 + seconds;
}
/** A duration in whole minutes, 0 when there is none. */
export function parseDuration(duration: string | undefined): number {
if (!duration) return 0;
let totalMinutes = 0;
const weekMatch = duration.match(/(\d+)W/);
const hourMatch = duration.match(/(\d+)H/);
const minMatch = duration.match(/(\d+)M/);
const dayMatch = duration.match(/(\d+)D/);
if (weekMatch) totalMinutes += parseInt(weekMatch[1]) * 7 * 24 * 60;
if (dayMatch) totalMinutes += parseInt(dayMatch[1]) * 24 * 60;
if (hourMatch) totalMinutes += parseInt(hourMatch[1]) * 60;
if (minMatch) totalMinutes += parseInt(minMatch[1]);
return totalMinutes;
}
export function normalizeAllDayDurationValue(duration: string | undefined): string | undefined {
const totalSeconds = parseDurationSeconds(duration);
if (totalSeconds === null || totalSeconds < 86400 || totalSeconds % 86400 !== 0) {
+1 -1
View File
@@ -1,5 +1,5 @@
import { addDays, differenceInCalendarDays, parseISO, startOfDay, subMilliseconds } from "date-fns";
import { parseDuration } from "@/components/calendar/event-card";
import { parseDuration } from "@/lib/calendar-event-normalization";
import type { CalendarEvent } from "@/lib/jmap/types";
import { fromWallClock, isValidTimeZone, parseLocalDateTime, toDisplayDate } from "@/lib/timezone";
+1 -1
View File
@@ -5,7 +5,7 @@ import type { Calendar, CalendarEvent, CalendarParticipant, CalendarParticipantI
import { debug } from '@/lib/debug';
import { getEventStartDate, normalizeAllDayDuration } from '@/lib/calendar-utils';
import { displayNow } from '@/lib/timezone';
import { parseDuration } from '@/components/calendar/event-card';
import { parseDuration } from '@/lib/calendar-event-normalization';
import { sanitizeOutgoingCalendarEventData } from '@/lib/calendar-event-normalization';
import { feedChanged, feedFields } from '@/lib/calendar-feed-diff';
import { expandRecurringEvents } from '@/lib/recurrence-expansion';