Use same lock values as mobile clients
[ProtonMail-WebClient.git] / packages / shared / lib / calendar / vcalConverter.ts
blobaf90478d3d9133c6bca2f2fe18faafbe0e9a74bc
1 import mod from '@proton/utils/mod';
3 import { addDays, isNextDay } from '../date-fns-utc';
4 import {
5     convertUTCDateTimeToZone,
6     convertZonedDateTimeToUTC,
7     fromUTCDate,
8     toLocalDate,
9     toUTCDate,
10 } from '../date/timezone';
11 import { buildMailTo, getEmailTo } from '../helpers/email';
12 import type {
13     DateTime,
14     VcalAttendeeProperty,
15     VcalDateOrDateTimeProperty,
16     VcalDateOrDateTimeValue,
17     VcalDateProperty,
18     VcalDateTimeProperty,
19     VcalDaysKeys,
20     VcalOrganizerProperty,
21     VcalVeventComponent,
22 } from '../interfaces/calendar';
23 import { VcalDays } from '../interfaces/calendar';
24 import { getIsPropertyAllDay, getPropertyTzid } from './vcalHelper';
26 export const dateToProperty = ({
27     year = 1,
28     month = 1,
29     day = 1,
30 }: {
31     year: number;
32     month: number;
33     day: number;
34 }): VcalDateProperty => {
35     return {
36         value: { year, month, day },
37         parameters: { type: 'date' },
38     };
41 export const dateTimeToProperty = (
42     { year = 1, month = 1, day = 1, hours = 0, minutes = 0, seconds = 0 }: DateTime,
43     isUTC = false,
44     tzid?: string
45 ): VcalDateTimeProperty => {
46     const value = { year, month, day, hours, minutes, seconds, isUTC };
47     if (!tzid || isUTC) {
48         return {
49             value,
50         };
51     }
52     return {
53         value,
54         parameters: {
55             tzid,
56         },
57     };
60 export const getDateProperty = ({ year, month, day }: { year: number; month: number; day: number }) => {
61     return dateToProperty({ year, month, day });
64 export const getDateTimeProperty = (zonelessTime: DateTime, tzid = '') => {
65     const isUTC = (tzid || '').toLowerCase().includes('utc');
66     return dateTimeToProperty(zonelessTime, isUTC, isUTC ? undefined : tzid);
69 export const getDateOrDateTimeProperty = (property: VcalDateOrDateTimeProperty, start: Date) => {
70     if (getIsPropertyAllDay(property)) {
71         return getDateProperty(fromUTCDate(start));
72     }
73     return getDateTimeProperty(fromUTCDate(start), getPropertyTzid(property));
76 export const propertyToLocalDate = (property: VcalDateOrDateTimeProperty) => {
77     if (getIsPropertyAllDay(property)) {
78         return toLocalDate(property.value);
79     }
80     if (property.value.isUTC || !property.parameters?.tzid) {
81         return toUTCDate(property.value);
82     }
83     // For dates with a timezone, convert the relative date time to UTC time
84     return toUTCDate(convertZonedDateTimeToUTC(property.value, property.parameters.tzid));
87 export const propertyToUTCDate = (property: VcalDateOrDateTimeProperty) => {
88     if (getIsPropertyAllDay(property) || property.value.isUTC || !property.parameters?.tzid) {
89         return toUTCDate(property.value);
90     }
91     // For dates with a timezone, convert the relative date time to UTC time
92     return toUTCDate(convertZonedDateTimeToUTC(property.value, property.parameters.tzid));
95 interface GetDtendPropertyArgs {
96     dtstart: VcalDateOrDateTimeProperty;
97     dtend?: VcalDateOrDateTimeProperty;
99 export const getDtendProperty = ({ dtstart, dtend }: GetDtendPropertyArgs) => {
100     if (dtend) {
101         return dtend;
102     }
103     if (getIsPropertyAllDay(dtstart)) {
104         const utcEnd = addDays(toUTCDate(dtstart.value), 1);
105         return getDateProperty(fromUTCDate(utcEnd));
106     }
107     return getDateTimeProperty(dtstart.value, getPropertyTzid(dtstart));
110 export const dayToNumericDay = (day: VcalDaysKeys): VcalDays | undefined => {
111     return VcalDays[day];
114 export const numericDayToDay = (number: VcalDays): VcalDaysKeys => {
115     if (number in VcalDays) {
116         return VcalDays[number] as VcalDaysKeys;
117     }
118     return VcalDays[mod(number, 7)] as VcalDaysKeys;
121 export const getDateTimePropertyInDifferentTimezone = (
122     property: VcalDateOrDateTimeProperty,
123     tzid: string,
124     isAllDay?: boolean
125 ) => {
126     if (isAllDay === true || getIsPropertyAllDay(property)) {
127         return getDateProperty(property.value);
128     }
129     const utcDate = propertyToUTCDate(property);
130     const zonedDate = convertUTCDateTimeToZone(fromUTCDate(utcDate), tzid);
131     return getDateTimeProperty(zonedDate, tzid);
134 export const getAllDayInfo = (dtstart: VcalDateOrDateTimeProperty, dtend?: VcalDateOrDateTimeProperty) => {
135     const isAllDay = getIsPropertyAllDay(dtstart);
136     if (!isAllDay) {
137         return { isAllDay: false, isSingleAllDay: false };
138     }
139     if (!dtend) {
140         return { isAllDay: true, isSingleAllDay: true };
141     }
142     // For all-day events, we need fake UTC dates to determine if the event lasts a single day
143     const fakeUTCStart = toUTCDate(dtstart.value);
144     const fakeUTCEnd = toUTCDate(dtend.value);
145     // account for non-RFC-compliant all-day events with DTSTART = DTEND
146     return { isAllDay: true, isSingleAllDay: isNextDay(fakeUTCStart, fakeUTCEnd) || +fakeUTCStart === +fakeUTCEnd };
149 export interface UntilDateArgument {
150     year: number;
151     month: number;
152     day: number;
154 export const getUntilProperty = (
155     untilDateTime: UntilDateArgument,
156     isAllDay: boolean,
157     tzid = 'UTC'
158 ): VcalDateOrDateTimeValue => {
159     // According to the RFC, we should use UTC dates if and only if the event is not all-day.
160     if (isAllDay) {
161         // we should use a floating date in this case
162         return {
163             year: untilDateTime.year,
164             month: untilDateTime.month,
165             day: untilDateTime.day,
166         };
167     }
168     // Pick end of day in the event start date timezone
169     const zonedEndOfDay = { ...untilDateTime, hours: 23, minutes: 59, seconds: 59 };
170     const utcEndOfDay = convertZonedDateTimeToUTC(zonedEndOfDay, tzid);
171     return { ...utcEndOfDay, isUTC: true };
174 export const extractEmailAddress = ({ value, parameters }: VcalAttendeeProperty | VcalOrganizerProperty) => {
175     const email = value || parameters?.cn;
176     return email && getEmailTo(email);
179 export const buildVcalOrganizer = (email: string, cn?: string) => {
180     return {
181         value: buildMailTo(email),
182         parameters: {
183             cn: cn || email,
184         },
185     };
188 export const buildVcalAttendee = (email: string) => {
189     return {
190         value: buildMailTo(email),
191         parameters: {
192             cn: email,
193         },
194     };
197 export const getHasModifiedDtstamp = (newVevent: VcalVeventComponent, oldVevent: VcalVeventComponent) => {
198     const { dtstamp: newDtstamp } = newVevent;
199     const { dtstamp: oldDtstamp } = oldVevent;
200     if (!newDtstamp || !oldDtstamp) {
201         return undefined;
202     }
203     return +propertyToUTCDate(newDtstamp) !== +propertyToUTCDate(oldDtstamp);
206 export const getHasStartChanged = (newVevent: VcalVeventComponent, oldVevent: VcalVeventComponent) =>
207     +propertyToUTCDate(oldVevent.dtstart) !== +propertyToUTCDate(newVevent.dtstart);
209 export const getHasModifiedDateTimes = (newVevent: VcalVeventComponent, oldVevent: VcalVeventComponent) => {
210     const isStartPreserved = !getHasStartChanged(newVevent, oldVevent);
211     const isEndPreserved =
212         +propertyToUTCDate(getDtendProperty(newVevent)) === +propertyToUTCDate(getDtendProperty(oldVevent));
213     return !isStartPreserved || !isEndPreserved;
216 export const getIsEquivalentOrganizer = (newOrganizer: VcalOrganizerProperty, oldOrganizer: VcalOrganizerProperty) => {
217     if (newOrganizer.value !== oldOrganizer.value) {
218         return false;
219     }
220     if (newOrganizer.parameters?.cn !== oldOrganizer.parameters?.cn) {
221         return false;
222     }
223     return true;
226 export const getIsEquivalentAttendee = (newAttendee: VcalAttendeeProperty, oldAttendee: VcalAttendeeProperty) => {
227     if (newAttendee.value !== oldAttendee.value) {
228         return false;
229     }
230     if (newAttendee.parameters?.partstat !== oldAttendee.parameters?.partstat) {
231         return false;
232     }
233     if (newAttendee.parameters?.role !== oldAttendee.parameters?.role) {
234         return false;
235     }
236     return true;