Update selected item color in Pass menu
[ProtonMail-WebClient.git] / packages / pass / lib / validation / vault-invite.ts
blob12af70c8757e0f21322b9d62ef65c77a312cce9e
1 import type { MutableRefObject, RefObject } from 'react';
3 import { type FormikErrors } from 'formik';
5 import { validateVaultValues } from '@proton/pass/lib/validation/vault';
6 import type { InviteFormValues } from '@proton/pass/types';
7 import { validateEmailAddress } from '@proton/shared/lib/helpers/email';
9 export enum InviteEmailsError {
10     DUPLICATE = 'DUPLICATE' /* duplicate members */,
11     EMPTY = 'EMPTY' /* empty members */,
12     INVALID_EMAIL = 'INVALID_EMAIL' /* invalid email string */,
13     INVALID_ORG = 'INVALID_ORG' /* invalid organization member */,
16 type ValidateShareInviteOptions = {
17     emailField: RefObject<HTMLInputElement>;
18     validateAddresses: boolean;
19     validationMap: MutableRefObject<Map<string, boolean>>;
22 export const validateShareInviteValues =
23     ({ emailField, validateAddresses, validationMap }: ValidateShareInviteOptions) =>
24     (values: InviteFormValues) => {
25         if (values.step === 'vault' && values.withVaultCreation) return validateVaultValues(values);
27         let errors: FormikErrors<InviteFormValues> = {};
29         if (values.step === 'members') {
30             const emails = values.members.reduce<{
31                 errors: string[];
32                 pass: boolean;
33                 seen: Set<string>;
34             }>(
35                 (acc, { value }) => {
36                     if (acc.seen.has(value.email)) {
37                         acc.errors.push(InviteEmailsError.DUPLICATE);
38                         acc.pass = false;
39                     } else if (!validateEmailAddress(value.email)) {
40                         acc.pass = false;
41                         acc.errors.push(InviteEmailsError.INVALID_EMAIL);
42                     } else if (validateAddresses && validationMap.current.get(value.email) === false) {
43                         acc.errors.push(InviteEmailsError.INVALID_ORG);
44                         acc.pass = false;
45                     } else acc.errors.push('');
47                     acc.seen.add(value.email);
49                     return acc;
50                 },
51                 { errors: [], pass: true, seen: new Set() }
52             );
54             errors.members = emails.errors;
56             /** Determine conditions for displaying trailing input
57              * value errors: If the field isn't focused, only show
58              * an error if the field is empty and there are no other
59              * members in the form. Adapt validation accordingly.  */
60             const trailingOnly = values.members.length === 0;
61             const trailingFocused = emailField.current === document.activeElement;
62             const trailingValue = emailField.current?.value?.trim() ?? '';
63             const trailingEmpty = trailingValue.length === 0;
64             const trailingValid = !trailingEmpty && validateEmailAddress(trailingValue);
66             /* If the trailing input is focused, trigger errors if the trailing
67              * value is not a valid email address. If it's not focused, flag errors
68              * only when the trailing value is invalid and either the field is
69              * empty or there are no other members in the form. */
70             if (trailingFocused) {
71                 emails.pass = emails.pass && (trailingOnly ? trailingValid : trailingValid || trailingEmpty);
72             } else if (!trailingValid) {
73                 if (trailingEmpty && trailingOnly) {
74                     emails.pass = false;
75                     errors.members.push(InviteEmailsError.EMPTY);
76                 } else if (!trailingEmpty) {
77                     emails.pass = false;
78                     errors.members.push(InviteEmailsError.INVALID_EMAIL);
79                 }
80             }
82             /* If no errors are found, delete any existing error
83              * messages related to members. */
84             if (emails.pass) delete errors.members;
85         }
87         return errors;
88     };