1 import { normalize } from '../helpers/string';
4 VcalAttendeePropertyWithCn,
5 VcalAttendeePropertyWithPartstat,
6 VcalAttendeePropertyWithRole,
7 VcalAttendeePropertyWithToken,
9 VcalCalendarComponentWithMaybeErrors,
10 VcalDateOrDateTimeProperty,
11 VcalDateOrDateTimeValue,
17 VcalVcalendarWithMaybeErrors,
19 VcalVeventComponentWithMaybeErrors,
20 VcalVfreebusyComponentWithMaybeErrors,
21 VcalVjournalComponentWithMaybeErrors,
22 VcalVtimezoneComponent,
23 VcalVtodoComponentWithMaybeErrors,
25 } from '../interfaces/calendar';
32 ICAL_METHODS_ATTENDEE,
33 ICAL_METHODS_ORGANIZER,
36 export const getIsPropertyAllDay = (property: VcalDateOrDateTimeProperty): property is VcalDateProperty => {
37 return property.parameters?.type === 'date' ?? false;
40 export const getPropertyTzid = (property: VcalDateOrDateTimeProperty) => {
41 if (getIsPropertyAllDay(property)) {
44 return property.value.isUTC ? 'UTC' : property.parameters?.tzid;
47 export const getIsDateTimeValue = (value: VcalDateOrDateTimeValue): value is VcalDateTimeValue => {
48 return (value as VcalDateTimeValue).hours !== undefined;
51 export const getIsCalendar = (vcalComponent: VcalCalendarComponent): vcalComponent is VcalVcalendar => {
52 return (vcalComponent as VcalVcalendar)?.component?.toLowerCase() === 'vcalendar';
55 export const getIsVcalErrorComponent = (
56 component: VcalErrorComponent | VcalCalendarComponentWithMaybeErrors
57 ): component is VcalErrorComponent => {
58 return 'error' in component;
61 export const getVcalendarHasNoErrorComponents = (
62 vcalendar: VcalVcalendarWithMaybeErrors
63 ): vcalendar is VcalVcalendar => {
64 return (vcalendar.components || []).every((component) => !getIsVcalErrorComponent(component));
67 export function getIsEventComponent(vcalComponent: VcalCalendarComponent): vcalComponent is VcalVeventComponent;
68 export function getIsEventComponent(
69 vcalComponent: VcalCalendarComponentWithMaybeErrors
70 ): vcalComponent is VcalVeventComponentWithMaybeErrors {
71 return vcalComponent?.component?.toLowerCase() === 'vevent';
74 export const getIsTodoComponent = (
75 vcalComponent: VcalCalendarComponentWithMaybeErrors
76 ): vcalComponent is VcalVtodoComponentWithMaybeErrors => {
77 return vcalComponent?.component?.toLowerCase() === 'vtodo';
80 export const getIsJournalComponent = (
81 vcalComponent: VcalCalendarComponentWithMaybeErrors
82 ): vcalComponent is VcalVjournalComponentWithMaybeErrors => {
83 return vcalComponent?.component?.toLowerCase() === 'vjournal';
86 export const getIsFreebusyComponent = (
87 vcalComponent: VcalCalendarComponentWithMaybeErrors
88 ): vcalComponent is VcalVfreebusyComponentWithMaybeErrors => {
89 return vcalComponent?.component?.toLowerCase() === 'vfreebusy';
92 export const getIsTimezoneComponent = (
93 vcalComponent: VcalCalendarComponent
94 ): vcalComponent is VcalVtimezoneComponent => {
95 return vcalComponent?.component?.toLowerCase() === 'vtimezone';
98 export const getIsAlarmComponent = (vcalComponent: VcalCalendarComponent): vcalComponent is VcalVtimezoneComponent => {
99 return vcalComponent?.component?.toLowerCase() === 'valarm';
102 export const getIsXOrIanaComponent = (vcalComponent: VcalCalendarComponent): vcalComponent is VcalXOrIanaComponent => {
103 const name = vcalComponent?.component?.toLowerCase();
104 return !['vcalendar', 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'vtimezone'].includes(name);
107 export const getHasUid = (
108 vevent: VcalVeventComponent
109 ): vevent is VcalVeventComponent & Required<Pick<VcalVeventComponent, 'uid'>> => {
110 return !!vevent.uid?.value;
113 export const getHasDtStart = (
114 vevent: VcalVeventComponent
115 ): vevent is VcalVeventComponent & Required<Pick<VcalVeventComponent, 'dtstart'>> => {
116 return !!vevent.dtstart?.value;
119 export const getHasDtend = (
120 vevent: VcalVeventComponent
121 ): vevent is VcalVeventComponent & Required<Pick<VcalVeventComponent, 'dtend'>> => {
122 return !!vevent.dtend;
125 export const getHasRecurrenceId = (
126 vevent?: VcalVeventComponent
127 ): vevent is VcalVeventComponent & Required<Pick<VcalVeventComponent, 'recurrence-id'>> => {
128 return !!vevent?.['recurrence-id'];
131 export const getHasAttendee = (
132 vevent: VcalVeventComponent
133 ): vevent is VcalVeventComponent & Required<Pick<VcalVeventComponent, 'attendee'>> => {
134 return !!vevent.attendee;
137 export const getHasAttendees = (
138 vevent: VcalVeventComponent
139 ): vevent is VcalVeventComponent & Required<Pick<VcalVeventComponent, 'attendee'>> => {
140 return !!vevent.attendee?.length;
143 export const getAttendeeHasCn = (attendee: VcalAttendeeProperty): attendee is VcalAttendeePropertyWithCn => {
144 return !!attendee.parameters?.cn;
147 export const getAttendeesHaveCn = (
148 vcalAttendee: VcalAttendeeProperty[]
149 ): vcalAttendee is VcalAttendeePropertyWithCn[] => {
150 return !vcalAttendee.some((vcalAttendee) => !getAttendeeHasCn(vcalAttendee));
153 export const getAttendeeHasToken = (attendee: VcalAttendeeProperty): attendee is VcalAttendeePropertyWithToken => {
154 return !!attendee.parameters?.['x-pm-token'];
157 export const getAttendeesHaveToken = (
158 vcalAttendee: VcalAttendeeProperty[]
159 ): vcalAttendee is VcalAttendeePropertyWithToken[] => {
160 return !vcalAttendee.some((vcalAttendee) => !getAttendeeHasToken(vcalAttendee));
163 export const getAttendeeHasPartStat = (
164 attendee: VcalAttendeeProperty
165 ): attendee is VcalAttendeePropertyWithPartstat => {
166 return !!attendee.parameters?.partstat;
169 export const getAttendeeHasRole = (attendee: VcalAttendeeProperty): attendee is VcalAttendeePropertyWithRole => {
170 return !!attendee.parameters?.role;
173 export const getIcalMethod = (method?: VcalStringProperty) => {
175 return ICAL_METHOD.PUBLISH;
177 const normalizedValue = normalize(method.value);
178 const matchesNormalizedValue = (icalMethod: ICAL_METHOD) => normalize(icalMethod) === normalizedValue;
180 return Object.values(ICAL_METHOD).find(matchesNormalizedValue);
183 export const getIsValidMethod = (method: ICAL_METHOD, isOrganizerMode: boolean) => {
184 if (method === ICAL_METHOD.DECLINECOUNTER) {
185 // we should never encounter DECLINECOUNTER for the moment
188 return isOrganizerMode ? ICAL_METHODS_ATTENDEE.includes(method) : ICAL_METHODS_ORGANIZER.includes(method);
191 export const getVeventStatus = <T extends { status?: VcalVeventComponent['status'] }>({ status }: T) => {
192 if (Object.values(ICAL_EVENT_STATUS).some((icalStatus) => icalStatus === status?.value)) {
193 return status?.value as ICAL_EVENT_STATUS;
195 return ICAL_EVENT_STATUS.CONFIRMED;
198 export const getIsVeventCancelled = (vevent: VcalVeventComponent) => {
199 return getVeventStatus(vevent) === ICAL_EVENT_STATUS.CANCELLED;
202 export const getAttendeePartstat = (attendee: Partial<VcalAttendeeProperty> = {}, xYahooUserStatus?: string) => {
203 const partstat = attendee.parameters?.partstat;
204 if (partstat === ICAL_ATTENDEE_STATUS.NEEDS_ACTION && xYahooUserStatus) {
205 // Yahoo Calendar does not follow the RFC and encodes the partstat in a custom property
206 if (xYahooUserStatus === 'BUSY') {
207 return ICAL_ATTENDEE_STATUS.ACCEPTED;
209 if (xYahooUserStatus === 'TENTATIVE') {
210 return ICAL_ATTENDEE_STATUS.TENTATIVE;
212 if (xYahooUserStatus === 'FREE') {
213 return ICAL_ATTENDEE_STATUS.DECLINED;
216 if (Object.values(ICAL_ATTENDEE_STATUS).some((icalPartstat) => icalPartstat === partstat)) {
217 return partstat as ICAL_ATTENDEE_STATUS;
219 return ICAL_ATTENDEE_STATUS.NEEDS_ACTION;
222 export const getAttendeeRole = (attendee: Partial<VcalAttendeeProperty> = {}) => {
223 const role = attendee.parameters?.role;
224 if (Object.values(ICAL_ATTENDEE_ROLE).some((icalRole) => icalRole === role)) {
225 return role as ICAL_ATTENDEE_ROLE;
227 return ICAL_ATTENDEE_ROLE.REQUIRED;
230 export const getAttendeeToken = (attendee: Partial<VcalAttendeeProperty> = {}) => {
231 return attendee?.parameters?.['x-pm-token'];
234 export const getIsYahooEvent = (veventComponent: VcalVeventComponent) => {
235 return !!(veventComponent['x-yahoo-yid'] || veventComponent['x-yahoo-user-status']);
238 export const getIsProtonReply = (veventComponent: VcalVeventComponent) => {
239 const stringifiedValue = veventComponent['x-pm-proton-reply']?.value;
240 if (!stringifiedValue) {
243 return stringifiedValue === 'true';
246 export const getPmSharedEventID = (veventComponent: VcalVeventComponent) => {
247 return veventComponent['x-pm-shared-event-id']?.value;
250 export const getPmSharedSessionKey = (veventComponent: VcalVeventComponent) => {
251 return veventComponent['x-pm-session-key']?.value;
254 export const getRSVPStatus = (attendee: Partial<VcalAttendeeProperty> = {}) => {
255 const rsvp = attendee.parameters?.rsvp;
256 if (Object.values(ICAL_ATTENDEE_RSVP).some((icalRsvp) => icalRsvp === rsvp)) {
257 return rsvp as ICAL_ATTENDEE_RSVP;
259 return ICAL_ATTENDEE_RSVP.TRUE;