Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / packages / shared / lib / contacts / properties.ts
blob73e62163f7c4df6a84787a8b124f92241d3db2a8
1 import generateUID from '@proton/utils/generateUID';
3 import type { ContactValue } from '../interfaces/contacts';
4 import type { VCardContact, VCardProperty } from '../interfaces/contacts/VCard';
5 import { UID_PREFIX } from './constants';
6 import { isMultiValue } from './vcard';
8 export const FIELDS_WITH_PREF = ['fn', 'email', 'tel', 'adr', 'key', 'photo'];
10 export const getStringContactValue = (value: ContactValue): string => {
11     if (Array.isArray(value)) {
12         return getStringContactValue(value[0]);
13     }
14     // Shouldnt really happen but some boolean gets there as boolean instead of strings
15     return String(value);
18 /**
19  * Given a vCard field, return true if we take into consideration its PREF parameters
20  */
21 export const hasPref = (field: string) => FIELDS_WITH_PREF.includes(field);
23 /**
24  * For a vCard contact, check if it contains categories
25  */
26 export const hasCategories = (vcardContact: VCardProperty[]) => {
27     return vcardContact.some(({ field, value }) => value && field === 'categories');
30 /**
31  * Extract categories from a vCard contact
32  */
33 export const getContactCategories = (contact: VCardContact) => {
34     return (contact.categories || [])
35         .map(({ value, group }) => {
36             if (Array.isArray(value)) {
37                 return group
38                     ? value.map((singleValue) => ({ name: getStringContactValue(singleValue), group }))
39                     : value.map((singleValue) => ({ name: getStringContactValue(singleValue) }));
40             }
41             return group ? { name: value, group } : { name: value };
42         })
43         .flat();
46 /**
47  * Generate new group name that doesn't exist
48  */
49 export const generateNewGroupName = (existingGroups: string[] = []): string => {
50     let index = 1;
51     let found = false;
53     while (!found) {
54         if (existingGroups.includes(`item${index}`)) {
55             index++;
56         } else {
57             found = true;
58         }
59     }
61     return `item${index}`;
64 /**
65  * Extract emails from a vCard contact
66  */
67 export const getContactEmails = (contact: VCardContact) => {
68     return (contact.email || []).map(({ value, group }) => {
69         if (!group) {
70             throw new Error('Email properties should have a group');
71         }
72         return {
73             email: getStringContactValue(value),
74             group,
75         };
76     });
79 export const createContactPropertyUid = () => generateUID(UID_PREFIX);
81 export const getContactPropertyUid = (uid: string) => Number(uid.replace(`${UID_PREFIX}-`, ''));
83 // TODO: Deprecate this function. See VcardProperty interface
84 export const getVCardProperties = (vCardContact: VCardContact): VCardProperty[] => {
85     return Object.values(vCardContact).flatMap((property) => {
86         if (Array.isArray(property)) {
87             return property;
88         } else {
89             return [property];
90         }
91     });
94 export const fromVCardProperties = (vCardProperties: VCardProperty[]): VCardContact => {
95     const vCardContact = {} as VCardContact;
97     vCardProperties.forEach((property) => {
98         const field = property.field as keyof VCardContact;
100         if (isMultiValue(field)) {
101             if (!vCardContact[field]) {
102                 vCardContact[field] = [] as any;
103             }
104             (vCardContact[field] as VCardProperty[]).push(property);
105         } else {
106             vCardContact[field] = property as any;
107         }
108     });
110     return vCardContact;
113 export const mergeVCard = (vCardContacts: VCardContact[]): VCardContact => {
114     return fromVCardProperties(vCardContacts.flatMap(getVCardProperties));
117 export const updateVCardContact = (vCardContact: VCardContact, vCardProperty: VCardProperty) => {
118     const properties = getVCardProperties(vCardContact);
119     const newProperties = properties.map((property) => (property.uid === vCardProperty.uid ? vCardProperty : property));
120     return fromVCardProperties(newProperties);
123 export const addVCardProperty = (vCardContact: VCardContact, vCardProperty: VCardProperty) => {
124     const properties = getVCardProperties(vCardContact);
125     const newVCardProperty = { ...vCardProperty, uid: createContactPropertyUid() };
126     properties.push(newVCardProperty);
127     const newVCardContact = fromVCardProperties(properties);
128     return { newVCardProperty, newVCardContact };
131 export const removeVCardProperty = (vCardContact: VCardContact, uid: string) => {
132     let properties = getVCardProperties(vCardContact);
134     const match = properties.find((property) => property.uid === uid);
136     if (!match) {
137         return vCardContact;
138     }
140     properties = properties.filter((property) => property.uid !== uid);
142     // If we remove an email with groups attached to it, remove all groups properties too
143     if (match.field === 'email' && match.group !== undefined) {
144         properties = properties.filter((property) => property.group !== match.group);
145     }
147     // Never remove the last photo property
148     if (match.field === 'photo') {
149         const photoCount = properties.filter((property) => property.field === 'photo').length;
150         if (photoCount === 0) {
151             properties.push({ field: 'photo', value: '', uid: generateUID(UID_PREFIX) });
152         }
153     }
155     return fromVCardProperties(properties);
158 export const compareVCardPropertyByUid = (a: VCardProperty, b: VCardProperty) => {
159     const aUid = getContactPropertyUid(a.uid);
160     const bUid = getContactPropertyUid(b.uid);
161     return aUid > bUid ? 1 : -1;
164 export const compareVCardPropertyByPref = (a: VCardProperty, b: VCardProperty) => {
165     const aPref = Number(a.params?.pref);
166     const bPref = Number(b.params?.pref);
167     if (!isNaN(aPref) && !isNaN(bPref) && aPref !== bPref) {
168         return aPref > bPref ? 1 : -1;
169     }
170     return compareVCardPropertyByUid(a, b);
173 export const getSortedProperties = (vCardContact: VCardContact, field: string) => {
174     return getVCardProperties(vCardContact)
175         .filter((property) => property.field === field)
176         .sort(compareVCardPropertyByPref);