View, Import, and Export attendees!

This commit is contained in:
Bruno Bernardino
2024-03-30 08:18:53 +00:00
parent abd1fdee62
commit 0ffe4e03f1
4 changed files with 86 additions and 7 deletions

View File

@@ -1,4 +1,4 @@
import { Calendar, CalendarEvent } from '../types.ts';
import { Calendar, CalendarEvent, CalendarEventAttendee } from '../types.ts';
export const CALENDAR_COLOR_OPTIONS = [
'bg-red-700',
@@ -47,6 +47,24 @@ export const CALENDAR_BORDER_COLOR_OPTIONS = [
'border-rose-700',
] as const;
function getVCalendarAttendeeStatus(status: CalendarEventAttendee['status']) {
if (status === 'accepted' || status === 'rejected') {
return status.toUpperCase();
}
return `NEEDS-ACTION`;
}
function getAttendeeStatusFromVCalendar(
status: 'NEEDS-ACTION' | 'ACCEPTED' | 'REJECTED',
): CalendarEventAttendee['status'] {
if (status === 'ACCEPTED' || status === 'REJECTED') {
return status.toLowerCase() as CalendarEventAttendee['status'];
}
return 'invited';
}
// TODO: Build this
export function formatCalendarEventsToVCalendar(
calendarEvents: CalendarEvent[],
@@ -57,10 +75,17 @@ export function formatCalendarEventsToVCalendar(
DTSTAMP:${new Date(calendarEvent.created_at).toISOString().substring(0, 19).replaceAll('-', '').replaceAll(':', '')}
DTSTART:${new Date(calendarEvent.start_date).toISOString().substring(0, 19).replaceAll('-', '').replaceAll(':', '')}
DTEND:${new Date(calendarEvent.end_date).toISOString().substring(0, 19).replaceAll('-', '').replaceAll(':', '')}
ORGANIZER;CN=:MAILTO:${calendarEvent.extra.organizer_email}
ORGANIZER;CN=:mailto:${calendarEvent.extra.organizer_email}
SUMMARY:${calendarEvent.title}
TRANSP:${getCalendarEventTransparency(calendarEvent, calendars).toUpperCase()}
${calendarEvent.extra.uid ? `UID:${calendarEvent.extra.uid}` : ''}
${
calendarEvent.extra.attendees?.map((attendee) =>
`ATTENDEE;PARTSTAT=${getVCalendarAttendeeStatus(attendee.status)};CN=${
attendee.name || ''
}:mailto:${attendee.email}`
).join('\n') || ''
}
END:VEVENT`
).join('\n');
@@ -74,7 +99,18 @@ END:VEVENT`
type VCalendarVersion = '1.0' | '2.0';
export function parseVCalendarFromTextContents(text: string): Partial<CalendarEvent>[] {
const lines = text.split('\n').map((line) => line.trim()).filter(Boolean);
// Lines that start with a space should be moved to the line above them, as it's the same field/value to parse
const lines = text.split('\n').reduce((previousLines, currentLine) => {
if (currentLine.startsWith(' ')) {
previousLines[previousLines.length - 1] = `${previousLines[previousLines.length - 1]}${
currentLine.substring(1).replaceAll('\r', '')
}`;
} else {
previousLines.push(currentLine.replaceAll('\r', ''));
}
return previousLines;
}, [] as string[]).map((line) => line.trim()).filter(Boolean);
const partialCalendarEvents: Partial<CalendarEvent>[] = [];
@@ -95,7 +131,7 @@ export function parseVCalendarFromTextContents(text: string): Partial<CalendarEv
continue;
}
// Finish contact
// Finish event
if (line.startsWith('END:VEVENT')) {
partialCalendarEvents.push(partialCalendarEvent);
continue;
@@ -209,6 +245,35 @@ export function parseVCalendarFromTextContents(text: string): Partial<CalendarEv
continue;
}
if (line.startsWith('ATTENDEE;')) {
const attendeeInfo = line.split(':');
const attendeeEmail = attendeeInfo.slice(-1)[0] || '';
const attendeeStatusInfo = line.split('PARTSTAT=')[1] || '';
const attendeeStatus = getAttendeeStatusFromVCalendar(
(attendeeStatusInfo.split(';')[0] || 'NEEDS-ACTION') as 'ACCEPTED' | 'REJECTED' | 'NEEDS-ACTION',
);
const attendeeNameInfo = line.split('CN=')[1] || '';
const attendeeName = (attendeeNameInfo.split(';')[0] || '').trim();
if (!attendeeEmail) {
continue;
}
const attendee: CalendarEventAttendee = {
email: attendeeEmail,
status: attendeeStatus,
};
if (attendeeName) {
attendee.name = attendeeName;
}
partialCalendarEvent.extra = {
...(partialCalendarEvent.extra! || {}),
attendees: [...(partialCalendarEvent.extra?.attendees || []), attendee],
};
}
}
return partialCalendarEvents;