Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / packages / shared / lib / calendar / icsSurgery / EventInvitationError.ts
blobe874279107aee8a4804e7c6c81e0525d19f2836d
1 import { c } from 'ttag';
3 import type { EventComponentIdentifiers } from '@proton/shared/lib/calendar/icsSurgery/interface';
5 import type { ICAL_ATTENDEE_STATUS } from '../constants';
6 import { ICAL_METHOD, ICAL_METHODS_ATTENDEE } from '../constants';
8 export enum INVITATION_ERROR_TYPE {
9     INVITATION_INVALID,
10     INVITATION_UNSUPPORTED,
11     INVALID_METHOD,
12     NO_COMPONENT,
13     NO_VEVENT,
14     PARSING_ERROR,
15     DECRYPTION_ERROR,
16     FETCHING_ERROR,
17     UPDATING_ERROR,
18     EVENT_CREATION_ERROR,
19     EVENT_UPDATE_ERROR,
20     CANCELLATION_ERROR,
21     UNEXPECTED_ERROR,
22     EXTERNAL_ERROR,
25 export enum EVENT_INVITATION_ERROR_TYPE {
26     NON_GREGORIAN,
27     INVALID_METHOD,
28     NO_VEVENT,
29     MISSING_ORIGINAL_UID,
30     MISSING_ORGANIZER,
31     MISSING_ORIGINAL_ORGANIZER,
32     MISSING_ATTENDEE,
33     DUPLICATE_ATTENDEES,
34     UNEXPECTED_FLOATING_TIME,
35     X_WR_TIMEZONE_UNSUPPORTED,
36     TZID_UNSUPPORTED,
37     DTSTART_MISSING,
38     ALLDAY_INCONSISTENCY,
39     DTSTART_MALFORMED,
40     DTSTART_OUT_OF_BOUNDS,
41     DTEND_MALFORMED,
42     DTEND_OUT_OF_BOUNDS,
43     RRULE_MALFORMED,
44     RRULE_UNSUPPORTED,
45     SINGLE_EDIT_UNSUPPORTED,
46     NO_OCCURRENCES,
47     EXTERNAL_ERROR,
48     VALIDATION_ERROR,
51 const {
52     INVITATION_INVALID,
53     INVITATION_UNSUPPORTED,
54     INVALID_METHOD,
55     NO_COMPONENT,
56     NO_VEVENT,
57     PARSING_ERROR,
58     DECRYPTION_ERROR,
59     FETCHING_ERROR,
60     UPDATING_ERROR,
61     EVENT_CREATION_ERROR,
62     EVENT_UPDATE_ERROR,
63     CANCELLATION_ERROR,
64     UNEXPECTED_ERROR,
65     EXTERNAL_ERROR,
66 } = INVITATION_ERROR_TYPE;
68 export const getErrorMessage = (errorType: INVITATION_ERROR_TYPE, config?: EventInvitationErrorConfig) => {
69     const isUnknown = !config?.method;
70     const isImport = config?.method === ICAL_METHOD.PUBLISH;
71     const isResponse = config?.method && ICAL_METHODS_ATTENDEE.includes(config?.method);
72     const isProtonInvite = !!config?.isProtonInvite;
74     if (errorType === INVITATION_INVALID) {
75         if (isUnknown) {
76             return c('Attached ics file error').t`Invalid ICS file`;
77         }
78         if (isImport) {
79             return c('Attached ics file error').t`Invalid event`;
80         }
81         return isResponse
82             ? c('Event invitation error').t`Invalid response`
83             : c('Event invitation error').t`Invalid invitation`;
84     }
85     if (errorType === INVITATION_UNSUPPORTED) {
86         if (isImport) {
87             return c('Attached ics file error').t`Unsupported event`;
88         }
89         return isResponse
90             ? c('Event invitation error').t`Unsupported response`
91             : c('Event invitation error').t`Unsupported invitation`;
92     }
93     if (errorType === NO_COMPONENT) {
94         return c('Attached ics file error').t`Empty ICS file`;
95     }
96     if (errorType === NO_VEVENT) {
97         return c('Attached ics file error').t`Unsupported calendar component`;
98     }
99     if (errorType === INVALID_METHOD) {
100         if (isUnknown) {
101             return c('Attached ics file error').t`Invalid method`;
102         }
103         // Here we invert response <-> invitation as we take the perspective of the sender
104         return isResponse
105             ? c('Event invitation error').t`Invalid invitation`
106             : c('Event invitation error').t`Invalid response`;
107     }
108     if (errorType === PARSING_ERROR) {
109         return c('Event invitation error').t`Attached ICS file could not be read`;
110     }
111     if (errorType === DECRYPTION_ERROR) {
112         return c('Event invitation error').t`Attached ICS file could not be decrypted`;
113     }
114     if (errorType === FETCHING_ERROR) {
115         return c('Event invitation error').t`We could not retrieve the event from your calendar`;
116     }
117     if (errorType === UPDATING_ERROR) {
118         return c('Event invitation error').t`We could not update the event in your calendar`;
119     }
120     if (errorType === EVENT_CREATION_ERROR) {
121         return isProtonInvite
122             ? c('Event invitation error').t`The event could not be added to your calendar. No answer was sent`
123             : c('Event invitation error').t`Your answer was sent, but the event could not be added to your calendar`;
124     }
125     if (errorType === EVENT_UPDATE_ERROR) {
126         return isProtonInvite
127             ? c('Event invitation error').t`The event could not be updated in your calendar. No answer was sent`
128             : c('Event invitation error').t`Your answer was sent, but the event could not be updated in your calendar`;
129     }
130     if (errorType === CANCELLATION_ERROR) {
131         return c('Event invitation error').t`We could not cancel the event in your calendar`;
132     }
133     if (errorType === UNEXPECTED_ERROR) {
134         return c('Event invitation error').t`Unexpected error`;
135     }
136     if (errorType === EXTERNAL_ERROR) {
137         return config?.externalError?.message || '';
138     }
139     return '';
142 interface EventInvitationErrorConfig {
143     hashedIcs?: string;
144     componentIdentifiers?: EventComponentIdentifiers;
145     extendedType?: EVENT_INVITATION_ERROR_TYPE;
146     externalError?: Error;
147     partstat?: ICAL_ATTENDEE_STATUS;
148     timestamp?: number;
149     isProtonInvite?: boolean;
150     method?: ICAL_METHOD;
151     originalUniqueIdentifier?: string;
154 export class EventInvitationError extends Error {
155     type: INVITATION_ERROR_TYPE;
157     hashedIcs: string;
159     componentIdentifiers?: EventComponentIdentifiers;
161     extendedType?: EVENT_INVITATION_ERROR_TYPE;
163     method?: ICAL_METHOD;
165     partstat?: ICAL_ATTENDEE_STATUS;
167     timestamp?: number;
169     isProtonInvite?: boolean;
171     externalError?: Error;
173     originalUniqueIdentifier?: string;
175     constructor(errorType: INVITATION_ERROR_TYPE, config?: EventInvitationErrorConfig) {
176         super(getErrorMessage(errorType, config));
177         this.type = errorType;
178         this.hashedIcs = config?.hashedIcs || '';
179         this.componentIdentifiers = config?.componentIdentifiers;
180         this.extendedType = config?.extendedType;
181         this.method = config?.method;
182         this.partstat = config?.partstat;
183         this.timestamp = config?.timestamp;
184         this.isProtonInvite = config?.isProtonInvite;
185         this.externalError = config?.externalError;
186         this.originalUniqueIdentifier = config?.originalUniqueIdentifier;
187         Object.setPrototypeOf(this, EventInvitationError.prototype);
188     }
190     getConfig() {
191         return {
192             type: this.type,
193             hashedIcs: this.hashedIcs,
194             extendedType: this.extendedType,
195             componentIdentifiers: this.componentIdentifiers,
196             partstat: this.partstat,
197             timestamp: this.timestamp,
198             isProtonInvite: this.isProtonInvite,
199             externalError: this.externalError,
200             method: this.method,
201             originalUniqueIdentifier: this.originalUniqueIdentifier,
202         };
203     }
206 export const cloneEventInvitationErrorWithConfig = (
207     error: EventInvitationError,
208     config: EventInvitationErrorConfig
209 ) => {
210     const newConfig = {
211         ...error.getConfig(),
212         ...config,
213     };
214     return new EventInvitationError(error.type, newConfig);