Remove payments API routing initialization
[ProtonMail-WebClient.git] / packages / components / containers / contacts / hooks / useApplyGroups.tsx
blob03b34a19ac0e80ad0bc7438430b73031c14eca59
1 import { useCallback } from 'react';
3 import { c } from 'ttag';
5 import { useModalTwoStatic } from '@proton/components/components/modalTwo/useModalTwo';
6 import useApi from '@proton/components/hooks/useApi';
7 import useEventManager from '@proton/components/hooks/useEventManager';
8 import useNotifications from '@proton/components/hooks/useNotifications';
9 import { useContactGroups } from '@proton/mail';
10 import { useContactEmails } from '@proton/mail/contactEmails/hooks';
11 import { useContacts } from '@proton/mail/contacts/hooks';
12 import { useMailSettings } from '@proton/mail/mailSettings/hooks';
13 import { labelContactEmails, unLabelContactEmails } from '@proton/shared/lib/api/contacts';
14 import { hasReachedContactGroupMembersLimit } from '@proton/shared/lib/contacts/helpers/contactGroup';
15 import type { Contact, ContactEmail } from '@proton/shared/lib/interfaces/contacts';
17 import ContactGroupLimitReachedModal from '../modals/ContactGroupLimitReachedModal';
18 import type { SelectEmailsProps } from '../modals/SelectEmailsModal';
20 /**
21  * Collect contacts having multiple emails
22  * Used for <SelectEmailsModal />
23  */
24 export const collectContacts = (contactEmails: ContactEmail[] = [], contacts: Contact[]) => {
25     return contactEmails.reduce(
26         (acc, { ContactID }) => {
27             acc.duplicate[ContactID] = (acc.duplicate[ContactID] || 0) + 1;
29             if (acc.duplicate[ContactID] === 2) {
30                 const contact = contacts.find(({ ID }: { ID: string }) => ID === ContactID);
31                 if (contact) {
32                     acc.contacts.push(contact);
33                 }
34             }
36             return acc;
37         },
38         {
39             contacts: [] as Contact[],
40             duplicate: Object.create(null),
41         }
42     );
45 /**
46  * Returns a reusable action to apply or remove groups to a list of contact emails
47  */
48 const useApplyGroups = (
49     onLock?: (lock: boolean) => void,
50     setLoading?: (loading: boolean) => void,
51     onSelectEmails?: (props: SelectEmailsProps) => Promise<ContactEmail[]>
52 ) => {
53     const [mailSettings] = useMailSettings();
54     const { createNotification } = useNotifications();
55     const { call } = useEventManager();
56     const api = useApi();
57     const contacts = useContacts()[0] || [];
58     const userContactEmails = useContactEmails()[0] || [];
59     const [groups = []] = useContactGroups();
61     const [contactGroupLimitReachedModal, handleShowContactGroupLimitReachedModal] =
62         useModalTwoStatic(ContactGroupLimitReachedModal);
64     const applyGroups = useCallback(
65         async (contactEmails: ContactEmail[], changes: { [groupID: string]: boolean }, preventNotification = false) => {
66             const { contacts: collectedContacts } = collectContacts(contactEmails, contacts);
68             // contact emails in contacts with only one email (and then, skipping the modal)
69             const simpleEmails = contactEmails.filter(
70                 (contactEmail) => !collectedContacts.find((contact) => contactEmail.ContactID === contact.ID)
71             );
73             // contact emails in contacts with multiple email (and then, passing through the modal)
74             let selectedEmails: ContactEmail[] = [];
76             if (collectedContacts.length) {
77                 const groupIDs = Object.entries(changes)
78                     .filter(([, isChecked]) => isChecked)
79                     .map(([groupID]) => groupID);
81                 if (groupIDs.length) {
82                     setLoading?.(false);
83                     selectedEmails = (await onSelectEmails?.({ groupIDs, contacts: collectedContacts, onLock })) || [];
84                     setLoading?.(true);
85                 }
86             }
88             // When removing a group, we remove it for all emails selected
89             const listForRemoving = [...contactEmails];
91             // When adding a group, we do it only for the selected ones
92             const listForAdding = [...simpleEmails, ...selectedEmails];
94             const groupEntries = Object.entries(changes);
96             const cannotAddContactInGroupIDs: string[] = [];
98             await Promise.all(
99                 groupEntries.map(([groupID, isChecked]) => {
100                     const contactGroup = groups.find((group) => group.ID === groupID);
101                     const contactGroupName = contactGroup?.Name;
103                     if (isChecked) {
104                         const groupExistingMembers =
105                             groupID &&
106                             userContactEmails.filter(({ LabelIDs = [] }: { LabelIDs: string[] }) =>
107                                 LabelIDs.includes(groupID)
108                             );
110                         const toLabel = listForAdding
111                             .filter(({ LabelIDs = [] }) => !LabelIDs.includes(groupID))
112                             .map(({ ID }) => ID);
114                         if (!toLabel.length) {
115                             return Promise.resolve();
116                         }
118                         // Cannot add more than 100 contacts in a contact group
119                         const canAddContact = hasReachedContactGroupMembersLimit(
120                             groupExistingMembers.length + toLabel.length,
121                             mailSettings,
122                             false
123                         );
125                         if (!canAddContact) {
126                             cannotAddContactInGroupIDs.push(groupID);
127                             return Promise.resolve();
128                         }
130                         if (!preventNotification && contactGroupName) {
131                             const notificationText = c('Info').t`Contact assigned to group ${contactGroupName}`;
132                             createNotification({ text: notificationText });
133                         }
134                         return api(labelContactEmails({ LabelID: groupID, ContactEmailIDs: toLabel }));
135                     }
137                     const toUnlabel = listForRemoving
138                         .filter(({ LabelIDs = [] }) => LabelIDs.includes(groupID))
139                         .map(({ ID }) => ID);
141                     if (!toUnlabel.length) {
142                         return Promise.resolve();
143                     }
145                     if (!preventNotification && contactGroupName) {
146                         const notificationText = c('Info').t`Contact unassigned from group ${contactGroupName}`;
147                         createNotification({ text: notificationText });
148                     }
150                     return api(unLabelContactEmails({ LabelID: groupID, ContactEmailIDs: toUnlabel }));
151                 })
152             );
154             if (cannotAddContactInGroupIDs.length > 0) {
155                 void handleShowContactGroupLimitReachedModal({ groupIDs: cannotAddContactInGroupIDs });
156             }
158             await call();
159         },
160         [contacts, userContactEmails]
161     );
163     return { applyGroups, contactGroupLimitReachedModal };
166 export default useApplyGroups;