Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / packages / pass / components / Settings / Import.tsx
blobdabcd103da3ce3d9964b3320eceb175b90e41696
1 import { type FC, useCallback, useRef, useState } from 'react';
2 import { Provider as ReduxProvider, useStore } from 'react-redux';
4 import { Form, FormikProvider } from 'formik';
5 import { c, msgid } from 'ttag';
7 import { Button } from '@proton/atoms';
8 import { useNotifications } from '@proton/components';
9 import { usePassCore } from '@proton/pass/components/Core/PassCoreProvider';
10 import { ImportForm } from '@proton/pass/components/Import/ImportForm';
11 import { ImportProgress } from '@proton/pass/components/Import/ImportProgress';
12 import { ImportVaultsPickerModal } from '@proton/pass/components/Import/ImportVaultsPickerModal';
13 import {
14     type UseImportFormBeforeSubmit,
15     type UseImportFormBeforeSubmitValue,
16     useImportForm,
17 } from '@proton/pass/hooks/useImportForm';
18 import type { ImportPayload } from '@proton/pass/lib/import/types';
19 import { PROVIDER_INFO_MAP } from '@proton/pass/lib/import/types';
20 import { itemsImportRequest } from '@proton/pass/store/actions/requests';
21 import type { MaybeNull } from '@proton/pass/types';
22 import { pipe, tap } from '@proton/pass/utils/fp/pipe';
23 import { PASS_APP_NAME } from '@proton/shared/lib/constants';
25 import { SettingsPanel } from './SettingsPanel';
26 import { getItemsText } from './helper';
28 export const Import: FC = () => {
29     const store = useStore();
30     const { endpoint } = usePassCore();
31     const { createNotification } = useNotifications();
32     const [importData, setImportData] = useState<MaybeNull<ImportPayload>>(null);
33     const beforeSubmitResolver = useRef<(value: UseImportFormBeforeSubmitValue) => void>();
35     const beforeSubmit = useCallback<UseImportFormBeforeSubmit>(
36         async (payload) =>
37             new Promise((resolve) => {
38                 setImportData(payload);
39                 beforeSubmitResolver.current = pipe(
40                     resolve,
41                     tap(() => {
42                         beforeSubmitResolver.current = undefined;
43                         setImportData(null);
44                     })
45                 );
46             }),
47         []
48     );
50     const { form, dropzone, busy, result } = useImportForm({
51         beforeSubmit,
52         onSubmit: (payload) => {
53             const total = payload.vaults.reduce((count, vault) => count + vault.items.length, 0);
54             createNotification({
55                 key: itemsImportRequest(),
56                 showCloseButton: false,
57                 expiration: -1,
58                 text: (
59                     <ReduxProvider store={store}>
60                         <ImportProgress total={total} />
61                     </ReduxProvider>
62                 ),
63             });
64         },
65     });
67     const showResultDetails = (result?.ignored.length ?? 0) > 0 || (result?.warnings?.length ?? 0) > 0;
68     const totalImportedItems = result?.total ?? 0;
69     const totalItems = totalImportedItems + (result?.ignored.length ?? 0);
71     return (
72         <>
73             {result && (
74                 <SettingsPanel title={c('Label').t`Latest import`}>
75                     <div className="flex flex-column gap-y-1 text-sm">
76                         <div>
77                             <span className="color-weak">{c('Label').t`Imported from: `}</span>
78                             <span className="rounded bg-primary px-1 user-select-none">
79                                 {PROVIDER_INFO_MAP[result.provider].title}
80                             </span>
81                         </div>
83                         <div>
84                             <span className="color-weak">{c('Label').t`Imported on : `}</span>
85                             <span>{new Date(result.importedAt * 1000).toLocaleString()}</span>
86                         </div>
88                         <div>
89                             <span className="color-weak">{c('Label').t`Total items: `}</span>
90                             <span>{getItemsText(totalItems)}</span>
91                         </div>
93                         <div>
94                             <span className="color-weak">{c('Label').t`Total imported items: `}</span>
95                             <span>{getItemsText(totalImportedItems)}</span>
96                         </div>
98                         {showResultDetails && (
99                             <div className="bg-norm rounded-sm p-3 mt-2">
100                                 {result.ignored.length > 0 && (
101                                     <span className="mb-2 block">
102                                         {c('Info').ngettext(
103                                             msgid`The following ${result.ignored.length} item could not be imported:`,
104                                             `The following ${result.ignored.length} items could not be imported:`,
105                                             result.ignored.length
106                                         )}
107                                     </span>
108                                 )}
109                                 <div className="color-weak overflow-auto" style={{ maxHeight: 150 }}>
110                                     {result.ignored.map((description, idx) => (
111                                         <span className="block" key={`ignored-${idx}`}>
112                                             {description}
113                                         </span>
114                                     ))}
115                                     {result.warnings?.map((warning, idx) => (
116                                         <span className="block" key={`warning-${idx}`}>
117                                             {warning}
118                                         </span>
119                                     ))}
120                                 </div>
121                             </div>
122                         )}
124                         {endpoint === 'page' && (
125                             <div className="mt-2">
126                                 {c('Info')
127                                     .t`To review your imported data, click on the ${PASS_APP_NAME} icon in your browser toolbar.`}
128                             </div>
129                         )}
130                     </div>
131                 </SettingsPanel>
132             )}
134             <SettingsPanel
135                 title={c('Label').t`Import`}
136                 subTitle={c('Info')
137                     .t`To migrate data from another password manager, go to the password manager, export your data, then upload it to ${PASS_APP_NAME}. Once your data has been imported, delete the exported file.`}
138             >
139                 <FormikProvider value={form}>
140                     <Form>
141                         <ImportForm form={form} dropzone={dropzone} busy={busy} />
142                         {form.values.provider && (
143                             <Button
144                                 className="w-full mt-2"
145                                 type="submit"
146                                 disabled={busy || !form.isValid}
147                                 loading={busy}
148                                 color="norm"
149                             >
150                                 {busy ? c('Action').t`Importing` : c('Action').t`Import`}
151                             </Button>
152                         )}
153                     </Form>
154                 </FormikProvider>
156                 {importData !== null && (
157                     <ImportVaultsPickerModal
158                         onClose={() => beforeSubmitResolver.current?.({ ok: false })}
159                         payload={importData}
160                         onSubmit={(payload) =>
161                             beforeSubmitResolver?.current?.(
162                                 payload.vaults.length === 0 ? { ok: false } : { ok: true, payload }
163                             )
164                         }
165                     />
166                 )}
167             </SettingsPanel>
168         </>
169     );