1 import type { ReactNode } from 'react';
2 import { createContext, useContext, useMemo } from 'react';
4 import { useContactGroups } from '@proton/mail';
5 import { useContactEmails } from '@proton/mail/contactEmails/hooks';
6 import { canonicalizeEmail } from '@proton/shared/lib/helpers/email';
7 import type { ContactEmail, ContactGroup } from '@proton/shared/lib/interfaces/contacts';
8 import type { SimpleMap } from '@proton/shared/lib/interfaces/utils';
10 export type GroupWithContacts = { group: ContactGroup; contacts: ContactEmail[] };
11 export type GroupsWithContactsMap = SimpleMap<GroupWithContacts>;
13 export type ContactEmailsCache = {
14 contactEmails: ContactEmail[];
15 contactGroups: ContactGroup[];
16 contactEmailsMap: SimpleMap<ContactEmail>;
17 contactEmailsMapWithDuplicates: SimpleMap<ContactEmail[]>;
18 groupsWithContactsMap: GroupsWithContactsMap;
21 const ContactEmailsContext = createContext<ContactEmailsCache | null>(null);
23 const computeGroupsMap = (contacts: ContactEmail[], contactGroups: ContactGroup[]) =>
24 contacts.reduce<GroupsWithContactsMap>((acc, contact) => {
25 contact.LabelIDs?.forEach((labelID) => {
27 acc[labelID]?.contacts.push(contact);
29 const group = contactGroups.find((group) => group.ID === labelID);
31 acc[labelID] = { group, contacts: [contact] };
38 export const useContactEmailsCache = () => {
39 const state = useContext(ContactEmailsContext);
41 throw new Error('Trying to use uninitialized ContactEmailsProvider');
46 const toMapWithDuplicates = (contacts: ContactEmail[]) => {
47 const contactEmailsMapWithDuplicates = contacts.reduce<SimpleMap<ContactEmail[]>>((acc, contact) => {
48 const email = canonicalizeEmail(contact.Email);
49 const contacts = acc[email];
51 acc[email] = [contact];
53 contacts.push(contact);
54 contacts.sort((a, b) => a.Order - b.Order);
59 const contactEmailsMap = Object.keys(contactEmailsMapWithDuplicates).reduce<SimpleMap<ContactEmail>>((acc, key) => {
60 acc[key] = contactEmailsMapWithDuplicates[key]?.[0];
64 return { contactEmailsMap, contactEmailsMapWithDuplicates };
70 const ContactEmailsProvider = ({ children }: Props) => {
71 const [contactEmails = []] = useContactEmails();
72 const [contactGroups = []] = useContactGroups();
73 const cache = useMemo(() => {
77 ...toMapWithDuplicates(contactEmails),
78 groupsWithContactsMap: computeGroupsMap(contactEmails, contactGroups),
80 }, [contactEmails, contactGroups]);
82 return <ContactEmailsContext.Provider value={cache}>{children}</ContactEmailsContext.Provider>;
85 export default ContactEmailsProvider;