1 import { isValid } from 'date-fns';
2 import { c } from 'ttag';
4 import isTruthy from '@proton/utils/isTruthy';
6 import type { VCardContact, VCardProperty } from '../interfaces/contacts/VCard';
9 compareVCardPropertyByPref,
10 createContactPropertyUid,
14 } from './properties';
16 export const getFallbackFNValue = () => {
17 return c('Default display name vcard').t`Unknown`;
20 export const prepareForEdition = (vCardContact: VCardContact) => {
21 const result = { ...vCardContact };
23 if (!result.fn || result.fn.length === 0) {
24 result.fn = [{ field: 'fn', value: '', uid: createContactPropertyUid() }];
27 if (!result.photo || result.photo.length === 0) {
28 result.photo = [{ field: 'photo', value: '', uid: createContactPropertyUid() }];
31 if (!result.email || result.email.length === 0) {
32 result.email = [{ field: 'email', value: '', uid: createContactPropertyUid() }];
41 additionalNames: [''],
42 honorificPrefixes: [''],
43 honorificSuffixes: [''],
45 uid: createContactPropertyUid(),
52 export const prepareForSaving = (vCardContact: VCardContact) => {
53 const properties = getVCardProperties(vCardContact);
54 const newProperties = properties.filter((property) => {
55 if (property.field === 'adr') {
56 return property.value && Object.values(property.value).some(isTruthy);
58 if (property.field === 'bday' || property.field === 'aniversary') {
59 return property.value.text || (property.value.date && isValid(property.value.date));
61 if (property.field === 'gender') {
62 return isTruthy(property.value?.text);
64 return isTruthy(property.value);
66 const result = fromVCardProperties(newProperties);
68 if (result.categories) {
69 // Array-valued categories pose problems to ICAL (even though a vcard with CATEGORIES:ONE,TWO
70 // will be parsed into a value ['ONE', 'TWO'], ICAL.js fails to transform it back). So we convert
71 // an array-valued category into several properties
72 result.categories = result.categories.flatMap((category) => {
73 if (Array.isArray(category.value)) {
74 return category.value.map((value) => ({ ...category, value }));
81 // Add `pref` to email, adr, tel, key to save order
82 (FIELDS_WITH_PREF as (keyof VCardContact)[]).forEach((field) => {
83 if (result[field] && result[field]) {
84 result[field] = (result[field] as VCardProperty[])
85 .sort(compareVCardPropertyByPref)
86 .map((property, index) => ({ ...property, params: { ...property.params, pref: index + 1 } })) as any;
90 // Add `group` if missing for email.
92 const existingGroups = result.email.map(({ group }) => group).filter(isTruthy);
94 result.email = result.email.map((property) => {
98 const group = generateNewGroupName(existingGroups);
99 existingGroups.push(group);
100 return { ...property, group };
109 * Some contacts might miss the "FN" property, but we expect one for the vCard to be valid.
110 * In that case, use the contact email as FN instead.
111 * If no email is found, return undefined
113 export const getSupportedContactName = (contact: VCardContact) => {
114 return contact.email?.[0]?.value;