1 import { type FC, useMemo } from 'react';
2 import { useSelector } from 'react-redux';
4 import { Field, Form, type FormikContextType, FormikProvider } from 'formik';
5 import { c } from 'ttag';
7 import { Button } from '@proton/atoms';
8 import { Icon } from '@proton/components';
9 import { RadioGroupField } from '@proton/pass/components/Form/Field/RadioGroupField';
10 import { PasswordField } from '@proton/pass/components/Form/legacy/PasswordField';
11 import { Card } from '@proton/pass/components/Layout/Card/Card';
12 import { useOrganization } from '@proton/pass/components/Organization/OrganizationProvider';
13 import { type ExportFormValues, ExportFormat } from '@proton/pass/lib/export/types';
14 import { selectNonOwnedVaults } from '@proton/pass/store/selectors';
15 import { BitField } from '@proton/pass/types';
16 import { truthy } from '@proton/pass/utils/fp/predicates';
18 type ExporterProps = { form: FormikContextType<ExportFormValues>; loading: boolean };
20 export const ExportForm: FC<ExporterProps> = ({ form, loading = false }) => {
21 const hasNonOwnedVaults = useSelector(selectNonOwnedVaults).length > 0;
22 const org = useOrganization({ sync: true });
23 const orgExportDisabled = !org?.b2bAdmin && org?.settings.ExportMode === BitField.ACTIVE;
25 const warnings = useMemo(
28 /* Safari ZIP warning */
29 BUILD_TARGET === 'safari' &&
30 form.values.format === ExportFormat.ZIP &&
32 .t`Before exporting your data with this format, please open Safari settings -> "General" tab -> disable the option "Open safe files after downloading". This will prevent Safari from incorrectly extracting the exported file.`,
34 /* Safari PGP warning */
35 BUILD_TARGET === 'safari' &&
36 form.values.format === ExportFormat.PGP &&
38 .t`Due to a limitation of Safari browser extensions, after exporting the data the file extension will be missing ".pgp". Please rename the file to include the .pgp extension (e.g file.pgp) after exporting.`,
41 form.values.format === ExportFormat.CSV &&
43 .t`CSV offers a convenient format to view your data. However due to its simplicity, some data will not be included (custom fields, passkeys...). For a complete export, we recommend using a different format.`,
45 /* non-encrypted warning */
46 form.values.format !== ExportFormat.PGP &&
48 .t`This export will be unencrypted and anyone with access to your exported file will be able to see your passwords. For security, please delete it after you are done using it.`,
50 /* Owned vault warning */
51 hasNonOwnedVaults && c('Info').t`The export will only contain vaults that you own.`,
53 [form.values.format, hasNonOwnedVaults]
57 <FormikProvider value={form}>
58 <Form className="modal-two-dialog-container">
59 <div className="flex align-center items-center gap-4 mb-4">
62 className="flex flex-nowrap ml-2 mt-2 mb-2"
63 component={RadioGroupField}
66 value: ExportFormat.PGP,
67 label: c('Label').t`PGP-encrypted JSON (recommended)`,
70 value: ExportFormat.ZIP,
71 label: c('Label').t`JSON`,
74 value: ExportFormat.CSV,
78 checked={form.values.format}
79 label={c('Label').t`File format`}
80 disabled={orgExportDisabled}
84 {warnings.length > 0 && (
85 <Card className="mb-4 p-1 flex flex-column flex-nowrap gap-2 text-sm" type="primary">
86 {warnings.map((text, idx) => (
87 <div key={`warning-${idx}`} className={'flex items-start flex-nowrap w-full gap-2'}>
88 <Icon name="info-circle-filled" size={3} className="shrink-0 mt-0.5" />
95 {form.values.format === ExportFormat.PGP && (
97 <hr className="mt-2 mb-4 border-weak shrink-0" />
103 {c('Label').t`Passphrase`}{' '}
104 <em className="block text-normal text-sm color-weak my-1">
106 .t`The exported file will be encrypted using PGP and requires a strong passphrase.`}
110 component={PasswordField}
111 autoComplete="new-password"
116 {orgExportDisabled && (
117 <Card className="mb-4 p-1 text-sm" type="primary">
118 <div>{c('Info').t`This setting is disabled on the organization level`}</div>
126 disabled={!form.isValid || loading || orgExportDisabled}
127 className="mt-5 w-full"
129 {c('Action').t`Export`}