import { convertEventDateTimeToISO } from "@/utils/timezone"; import moment from "moment-timezone"; import { Calendar } from "../../Calendars/CalendarTypes"; import { RepetitionRule, VCalComponent, VObjectProperty, } from "../../Calendars/types/CalendarData"; import { userAttendee } from "../../User/models/attendee"; import { createAttendee } from "../../User/models/attendee.mapper"; import { AlarmObject, CalendarEvent } from "../EventsTypes"; import { inferTimezoneFromValue } from "./inferTimezoneFromValue"; import { buildDelegatedEventURL } from "./buildDelegatedEventURL"; import { formatDateToICal } from "./formatDateToICal"; export function parseCalendarEvent( data: VObjectProperty[], color: Record, calendar: Calendar, eventURL: string, valarm?: VCalComponent ): CalendarEvent { const event: Partial = { color, attendee: [] }; let recurrenceId; let duration; const dateRegex = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/; for (const [key, params, , value] of data) { switch (key.toLowerCase()) { case "uid": event.uid = String(value); break; case "transp": event.transp = String(value); break; case "dtstart": { event.start = String(value); const detectedTz = inferTimezoneFromValue( params as Record ); if (detectedTz) { event.timezone = detectedTz; } if (dateRegex.test(String(value))) { event.allday = true; } else { event.allday = false; } break; } case "dtend": { event.end = String(value); if (!event.timezone) { const detectedTz = inferTimezoneFromValue( params as Record ); if (detectedTz) { event.timezone = detectedTz; } } if (dateRegex.test(String(value))) { event.allday = true; } else { event.allday = false; } break; } case "class": if ( ["PRIVATE", "PUBLIC", "CONFIDENTIAL"].includes( String(value).toUpperCase() ) ) { event.class = String(value).toUpperCase() as CalendarEvent["class"]; } break; case "x-openpaas-videoconference": event.x_openpass_videoconference = String(value); break; case "summary": event.title = String(value); break; case "description": event.description = String(value); break; case "location": event.location = String(value); break; case "organizer": { const paramsObj = params as Record; event.organizer = { cn: paramsObj?.cn ?? "", cal_address: String(value).replace(/^mailto:/i, ""), }; break; } case "attendee": { const paramsObj = params as Record; if ( !event.attendee?.find( (attendee) => attendee.cal_address === String(value).replace(/^mailto:/i, "") ) ) { (event.attendee as userAttendee[]).push( createAttendee({ cn: paramsObj?.cn, cal_address: String(value).replace(/^mailto:/i, ""), partstat: paramsObj?.partstat as userAttendee["partstat"], rsvp: paramsObj?.rsvp as userAttendee["rsvp"], role: paramsObj?.role as userAttendee["role"], cutype: paramsObj?.cutype as userAttendee["cutype"], }) ); } break; } case "dtstamp": event.stamp = String(value); break; case "sequence": event.sequence = Number(value); break; case "recurrence-id": recurrenceId = String(value); break; case "exdate": if (!event.exdates) event.exdates = []; event.exdates.push(String(value)); break; case "status": event.status = String(value); break; case "duration": duration = String(value); break; case "rrule": { const ruleValue = value as RepetitionRule; event.repetition = { freq: ruleValue.freq.toLowerCase() }; if (ruleValue.byday) { if (typeof ruleValue.byday === "string") { event.repetition.byday = [ruleValue.byday]; } else { event.repetition.byday = ruleValue.byday; } } if (ruleValue.until) { event.repetition.endDate = ruleValue.until; } if (ruleValue.count) { event.repetition.occurrences = ruleValue.count; } if (ruleValue.interval) { event.repetition.interval = ruleValue.interval; } break; } } } if (recurrenceId && event.uid) { event.uid = `${event.uid}/${recurrenceId}`; event.recurrenceId = recurrenceId; } if (valarm) { event.alarm = {} as AlarmObject; for (const [key, , , value] of valarm[1]) { switch (key.toLowerCase()) { case "action": event.alarm.action = String(value); break; case "trigger": event.alarm.trigger = String(value); break; } } } event.calId = calendar.id; event.URL = calendar.delegated ? buildDelegatedEventURL(calendar, eventURL) : eventURL; if (!event.uid || !event.start) { console.error( `missing crucial event param in calendar ${calendar.id} `, data ); event.error = `missing crucial event param in calendar ${calendar.id} `; } const eventTimezone = event.timezone; if (!event.end) { const start = event.start ? new Date(event.start) : new Date(); const timeToAdd = duration ? moment.duration(duration).asMilliseconds() : moment.duration(30, "minutes").asMilliseconds(); const artificialEnd = new Date(start.getTime() + timeToAdd); event.end = formatDateToICal(artificialEnd, false, eventTimezone); } if (!event.allday && event.start && eventTimezone) { const startISO = convertEventDateTimeToISO(event.start, eventTimezone); if (startISO) { event.start = startISO; } } if (!event.allday && event.end && eventTimezone) { const endISO = convertEventDateTimeToISO(event.end, eventTimezone); if (endISO) { event.end = endISO; } } return event as CalendarEvent; }