import ICAL from "ical.js"; import type { CalendarEvent } from "@/lib/types"; export function parseICS(icsString: string): CalendarEvent[] { const jcalData = ICAL.parse(icsString); const comp = new ICAL.Component(jcalData); const vevents = comp.getAllSubcomponents("vevent"); return vevents.map((v) => { const ev = new ICAL.Event(v); const isAllDay = ev.startDate.isDate; return { id: ev.uid || crypto.randomUUID(), title: ev.summary || "Untitled Event", description: ev.description || "", location: ev.location || "", url: v.getFirstPropertyValue("url") || undefined, start: ev.startDate.toJSDate().toISOString(), end: ev.endDate ? ev.endDate.toJSDate().toISOString() : undefined, allDay: isAllDay, createdAt: v.getFirstPropertyValue("dtstamp") ? (v.getFirstPropertyValue("dtstamp") as ICAL.Time) .toJSDate() .toISOString() : undefined, lastModified: v.getFirstPropertyValue("last-modified") ? (v.getFirstPropertyValue("last-modified") as ICAL.Time) .toJSDate() .toISOString() : undefined, recurrenceRule: v.getFirstPropertyValue("rrule") ? (v.getFirstPropertyValue("rrule") as ICAL.Recur).toString() : undefined, }; }); } export function generateICS(events: CalendarEvent[]): string { const comp = new ICAL.Component(["vcalendar", [], []]); comp.addPropertyWithValue("version", "2.0"); comp.addPropertyWithValue("prodid", "-//iCalPWA//EN"); events.forEach((ev) => { const vevent = new ICAL.Component("vevent"); vevent.addPropertyWithValue("uid", ev.id); vevent.addPropertyWithValue("summary", ev.title); if (ev.description) vevent.addPropertyWithValue("description", ev.description); if (ev.location) vevent.addPropertyWithValue("location", ev.location); if (ev.url) vevent.addPropertyWithValue("url", ev.url); // Start/End if (ev.allDay) { vevent.addPropertyWithValue( "dtstart", ICAL.Time.fromDateString(ev.start.split("T")[0]), ); if (ev.end) vevent.addPropertyWithValue( "dtend", ICAL.Time.fromDateString(ev.end.split("T")[0]), ); } else { vevent.addPropertyWithValue( "dtstart", ICAL.Time.fromJSDate(new Date(ev.start)), ); if (ev.end) vevent.addPropertyWithValue( "dtend", ICAL.Time.fromJSDate(new Date(ev.end)), ); } // Timestamps vevent.addPropertyWithValue( "dtstamp", ICAL.Time.fromJSDate(ev.createdAt ? new Date(ev.createdAt) : new Date()), ); if (ev.lastModified) { vevent.addPropertyWithValue( "last-modified", ICAL.Time.fromJSDate(new Date(ev.lastModified)), ); } // Recurrence if (ev.recurrenceRule) { vevent.addPropertyWithValue( "rrule", ICAL.Recur.fromString(ev.recurrenceRule), ); } comp.addSubcomponent(vevent); }); return comp.toString(); }