1 import { reformatApiErrorMessage } from '@proton/shared/lib/calendar/api';
2 import { getAttendeeEmail } from '@proton/shared/lib/calendar/attendees';
3 import { getHasRecurrenceId } from '@proton/shared/lib/calendar/vcalHelper';
4 import type { SimpleMap } from '@proton/shared/lib/interfaces';
5 import type { VcalVeventComponent } from '@proton/shared/lib/interfaces/calendar';
7 import type { AugmentedSendPreferences } from '../containers/calendar/interface';
8 import type { InviteActions } from '../interfaces/Invite';
9 import { INVITE_ACTION_TYPES } from '../interfaces/Invite';
11 const { CHANGE_PARTSTAT, DECLINE_INVITATION, NONE, SEND_INVITATION, CANCEL_INVITATION, SEND_UPDATE } =
14 export const getCleanSendDataFromSendPref = ({
21 sendPreferencesMap: SimpleMap<AugmentedSendPreferences>;
22 inviteActions: InviteActions;
23 vevent: VcalVeventComponent;
24 cancelVevent?: VcalVeventComponent;
25 emailsWithError: string[];
27 const { type, addedAttendees = [], removedAttendees = [] } = inviteActions;
28 const hasAddedAttendees = !!addedAttendees.length;
29 const hasRemovedAttendees = !!removedAttendees.length;
30 const addedCleanAttendees = addedAttendees.filter(
31 (attendee) => !emailsWithError.includes(getAttendeeEmail(attendee))
33 const invitedEmails = (vevent?.attendee || []).map((attendee) => getAttendeeEmail(attendee));
34 const addedEmails = addedAttendees.map((attendee) => getAttendeeEmail(attendee));
35 const addedEmailsWithError = addedEmails.filter((email) => emailsWithError.includes(email));
36 const existingEmails = invitedEmails.filter((email) => !addedEmails.includes(email));
37 const removedCleanAttendees = removedAttendees.filter(
38 (attendee) => !emailsWithError.includes(getAttendeeEmail(attendee))
40 const removedEmails = removedAttendees.map((attendee) => getAttendeeEmail(attendee));
41 const removedEmailsWithError = removedEmails.filter((email) => emailsWithError.includes(email));
42 const invitedCleanAttendees = vevent?.attendee?.filter(
43 (attendee) => !emailsWithError.includes(getAttendeeEmail(attendee))
45 const invitedCleanOfAddedAttendees = vevent?.attendee?.filter((attendee) => {
46 const email = getAttendeeEmail(attendee);
47 return existingEmails.includes(email) || !emailsWithError.includes(email);
49 const invitedEmailsWithError = (vevent?.attendee || [])
50 .map((attendee) => getAttendeeEmail(attendee))
51 .filter((email) => emailsWithError.includes(email));
52 const cancelledCleanAttendees = cancelVevent?.attendee?.filter(
53 (attendee) => !emailsWithError.includes(getAttendeeEmail(attendee))
55 const cancelledEmailsWithError = (cancelVevent?.attendee || [])
56 .map((attendee) => getAttendeeEmail(attendee))
57 .filter((email) => emailsWithError.includes(email));
58 const organizerEmail = vevent?.organizer ? getAttendeeEmail(vevent.organizer) : undefined;
59 const organizerEmailWithError =
60 organizerEmail && emailsWithError.includes(organizerEmail) ? organizerEmail : undefined;
65 addedAttendees: addedCleanAttendees,
69 attendee: invitedCleanOfAddedAttendees || [],
76 if (type === SEND_INVITATION) {
78 throw new Error('Cannot create event without vevent component');
80 if (!hasAddedAttendees && !hasRemovedAttendees) {
81 if (getHasRecurrenceId(vevent)) {
82 // we're creating a single edit, in that case we want to keep attendees with errors
86 ...cleanData.inviteActions,
88 emailsWithError: [...invitedEmailsWithError, ...addedEmailsWithError, ...removedEmailsWithError],
91 // it's a new invitation
96 attendee: invitedCleanAttendees,
99 ...cleanData.inviteActions,
100 type: invitedCleanAttendees?.length
101 ? INVITE_ACTION_TYPES.SEND_INVITATION
102 : INVITE_ACTION_TYPES.NONE,
104 emailsWithError: invitedEmailsWithError,
107 // the event exists already
108 const hasAddedOrRemoved = addedCleanAttendees.length || removedCleanAttendees.length;
112 ...cleanData.inviteActions,
113 type: hasAddedOrRemoved ? SEND_INVITATION : NONE,
115 emailsWithError: [...addedEmailsWithError, ...removedEmailsWithError],
118 if (type === SEND_UPDATE) {
119 const cannotNotify = !invitedCleanAttendees?.length && !removedCleanAttendees?.length;
123 ...cleanData.inviteActions,
124 type: cannotNotify ? NONE : SEND_UPDATE,
126 emailsWithError: [...invitedEmailsWithError, ...addedEmailsWithError, ...removedEmailsWithError],
129 if (type === CANCEL_INVITATION) {
133 ...cleanData.inviteActions,
134 type: cancelledCleanAttendees?.length ? CANCEL_INVITATION : NONE,
136 emailsWithError: cancelledEmailsWithError,
139 if ([CHANGE_PARTSTAT, DECLINE_INVITATION].includes(type)) {
148 emailsWithError: [organizerEmailWithError],
154 export const getSendPrefErrorMap = (sendPreferencesMap: SimpleMap<AugmentedSendPreferences>) => {
155 return Object.entries(sendPreferencesMap).reduce<SimpleMap<string>>((acc, [email, sendPrefs]) => {
156 const error = sendPrefs?.error;
158 acc[email] = reformatApiErrorMessage(error.message);