1 import isTruthy from '@proton/utils/isTruthy';
3 import { validateEmailAddress } from '../helpers/email';
4 import type { Recipient } from '../interfaces';
5 import type { ContactEmail } from '../interfaces/contacts';
6 import { unescapeFromString } from '../sanitize/escape';
8 export const REGEX_RECIPIENT = /(.*?)\s*<([^>]*)>/;
10 const SEPARATOR_REGEX = /[,;]/;
13 * Trim and remove surrounding chevrons
15 export const clearValue = (value: string) => {
16 const trimmed = value.trim();
17 if (trimmed.startsWith('<') && trimmed.endsWith('>')) {
18 return trimmed.slice(1, -1);
24 * Split input content by comma or semicolon
26 export const splitBySeparator = (input: string) => {
28 .split(SEPARATOR_REGEX)
29 .map((value) => clearValue(value))
33 * Find in a string potential recipients separated by a space
35 export const findRecipientsWithSpaceSeparator = (input: string) => {
36 // Do nothing if the input does not contain a space
37 // Otherwise, executing the logic might break the behaviour we have
38 // For example it will detect as valid input "address@pm.m", but the user might want to type "address@pm.me"
39 if (!input.includes(' ')) {
43 const emails = input.split(' ');
45 const clearedEmails = emails.map((email) => email.trim()).filter((email) => email.length > 0);
47 const isValid = clearedEmails.every((email) => validateEmailAddress(email));
49 return isValid ? clearedEmails : [];
52 export const inputToRecipient = (input: string) => {
53 // Remove potential unwanted HTML entities such as '­' from the string
54 const cleanInput = unescapeFromString(input);
55 const trimmedInput = cleanInput.trim();
56 const match = REGEX_RECIPIENT.exec(trimmedInput);
58 if (match !== null && (match[1] || match[2])) {
59 const trimmedMatches = match.map((match) => match.trim());
61 Name: trimmedMatches[1],
62 Address: trimmedMatches[2] || trimmedMatches[1],
67 Address: trimmedInput,
72 * Used in AddressesAutocomplete and AddressesAutocomplete v2 to detect recipients strings inside the input
74 export const handleRecipientInputChange = (
76 hasEmailPasting: boolean,
77 onAddRecipients: (recipients: Recipient[]) => void,
78 setInput: (val: string) => void
80 if (newValue === ';' || newValue === ',') {
84 if (!hasEmailPasting) {
89 const values = newValue.split(SEPARATOR_REGEX).map((value) => clearValue(value));
90 if (values.length > 1) {
91 onAddRecipients(values.map(inputToRecipient));
96 // Try to find recipients with space separator e.g. "address1@pm.me address2@pm.me"
97 const valuesWithSpaceBar = newValue.split(' ').map((val) => val.trim());
98 if (valuesWithSpaceBar.length > 0) {
99 const recipientsWithSpaceSeparator = findRecipientsWithSpaceSeparator(newValue);
101 if (recipientsWithSpaceSeparator.length > 0) {
102 onAddRecipients(recipientsWithSpaceSeparator.map(inputToRecipient));
111 export const contactToRecipient = (contact: ContactEmail, groupPath?: string) => ({
113 Address: contact.Email,
114 ContactID: contact.ContactID,
118 export const majorToRecipient = (email: string) => ({
123 export const recipientToInput = (recipient: Recipient): string => {
124 if (recipient.Address && recipient.Name && recipient.Address !== recipient.Name) {
125 return `${recipient.Name} <${recipient.Address}>`;
128 if (recipient.Address === recipient.Name) {
129 return recipient.Address || '';
132 return `${recipient.Name} ${recipient.Address}`;
135 export const contactToInput = (contact: ContactEmail): string => recipientToInput(contactToRecipient(contact));