Use same lock values as mobile clients
[ProtonMail-WebClient.git] / packages / shared / lib / calendar / vcalHelper.ts
blobcae55cc3c026382597b67d92db5c63970e8f6365
1 import { normalize } from '../helpers/string';
2 import type {
3     VcalAttendeeProperty,
4     VcalAttendeePropertyWithCn,
5     VcalAttendeePropertyWithPartstat,
6     VcalAttendeePropertyWithRole,
7     VcalAttendeePropertyWithToken,
8     VcalCalendarComponent,
9     VcalCalendarComponentWithMaybeErrors,
10     VcalDateOrDateTimeProperty,
11     VcalDateOrDateTimeValue,
12     VcalDateProperty,
13     VcalDateTimeValue,
14     VcalErrorComponent,
15     VcalStringProperty,
16     VcalVcalendar,
17     VcalVcalendarWithMaybeErrors,
18     VcalVeventComponent,
19     VcalVeventComponentWithMaybeErrors,
20     VcalVfreebusyComponentWithMaybeErrors,
21     VcalVjournalComponentWithMaybeErrors,
22     VcalVtimezoneComponent,
23     VcalVtodoComponentWithMaybeErrors,
24     VcalXOrIanaComponent,
25 } from '../interfaces/calendar';
26 import {
27     ICAL_ATTENDEE_ROLE,
28     ICAL_ATTENDEE_RSVP,
29     ICAL_ATTENDEE_STATUS,
30     ICAL_EVENT_STATUS,
31     ICAL_METHOD,
32     ICAL_METHODS_ATTENDEE,
33     ICAL_METHODS_ORGANIZER,
34 } from './constants';
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)) {
42         return;
43     }
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) => {
174     if (!method) {
175         return ICAL_METHOD.PUBLISH;
176     }
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
186         return false;
187     }
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;
194     }
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;
208         }
209         if (xYahooUserStatus === 'TENTATIVE') {
210             return ICAL_ATTENDEE_STATUS.TENTATIVE;
211         }
212         if (xYahooUserStatus === 'FREE') {
213             return ICAL_ATTENDEE_STATUS.DECLINED;
214         }
215     }
216     if (Object.values(ICAL_ATTENDEE_STATUS).some((icalPartstat) => icalPartstat === partstat)) {
217         return partstat as ICAL_ATTENDEE_STATUS;
218     }
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;
226     }
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) {
241         return;
242     }
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;
258     }
259     return ICAL_ATTENDEE_RSVP.TRUE;