Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / packages / components / containers / account / PasswordsSection.tsx
blobd8a796ffd5b70a74a62c61cbe531a971cea22302
1 import { useState } from 'react';
3 import { c } from 'ttag';
5 import { useUser } from '@proton/account/user/hooks';
6 import { useUserSettings } from '@proton/account/userSettings/hooks';
7 import { Button } from '@proton/atoms';
8 import Info from '@proton/components/components/link/Info';
9 import Loader from '@proton/components/components/loader/Loader';
10 import useModalState from '@proton/components/components/modalTwo/useModalState';
11 import Toggle from '@proton/components/components/toggle/Toggle';
12 import ChangeBackupPasswordModal from '@proton/components/containers/account/ChangeBackupPasswordModal';
13 import useSearchParamsEffect from '@proton/components/hooks/useSearchParamsEffect';
14 import { getKnowledgeBaseUrl } from '@proton/shared/lib/helpers/url';
15 import { SETTINGS_PASSWORD_MODE } from '@proton/shared/lib/interfaces';
16 import { getIsGlobalSSOAccount } from '@proton/shared/lib/keys';
18 import { useAvailableRecoveryMethods, useIsSessionRecoveryInitiationAvailable } from '../../hooks/useSessionRecovery';
19 import ChangePasswordModal, { MODES } from './ChangePasswordModal';
20 import ReauthUsingRecoveryModal from './ReauthUsingRecoveryModal';
21 import SettingsLayout from './SettingsLayout';
22 import SettingsLayoutLeft from './SettingsLayoutLeft';
23 import SettingsLayoutRight from './SettingsLayoutRight';
24 import SettingsSection from './SettingsSection';
25 import InitiateSessionRecoveryModal from './sessionRecovery/InitiateSessionRecoveryModal';
26 import PasswordResetAvailableAccountModal from './sessionRecovery/PasswordResetAvailableAccountModal';
27 import { useSessionRecoveryLocalStorage } from './sessionRecovery/SessionRecoveryLocalStorageManager';
29 const PasswordsSection = () => {
30     const [user, loadingUser] = useUser();
31     const [userSettings, loadingUserSettings] = useUserSettings();
33     const [availableRecoveryMethods] = useAvailableRecoveryMethods();
34     const hasRecoveryMethod = availableRecoveryMethods.length > 0;
35     const isSessionRecoveryInitiationAvailable = useIsSessionRecoveryInitiationAvailable();
37     const [tmpPasswordMode, setTmpPasswordMode] = useState<MODES>();
38     const [changePasswordModal, setChangePasswordModalOpen, renderChangePasswordModal] = useModalState();
39     const [changeBackupPasswordModal, setChangeBackupPasswordModalOpen, renderChangeBackupPasswordModal] =
40         useModalState();
41     const [
42         changePasswordAfterReauthModal,
43         setChangePasswordAfterReauthModalOpen,
44         renderChangePasswordAfterReauthModal,
45     ] = useModalState();
46     const [sessionRecoveryModal, setSessionRecoveryModalOpen, renderSessionRecoveryModal] = useModalState();
47     const [recoveryModal, setRecoveryModalOpen, renderRecoveryModal] = useModalState();
48     const [
49         sessionRecoveryPasswordResetModal,
50         setSessionRecoveryPasswordResetModalOpen,
51         renderSessionRecoveryPasswordResetModal,
52     ] = useModalState();
54     const { dismissSessionRecoveryCancelled } = useSessionRecoveryLocalStorage();
55     const [skipInfoStep, setSkipInfoStep] = useState(false);
57     const isOnePasswordMode = userSettings?.Password?.Mode === SETTINGS_PASSWORD_MODE.ONE_PASSWORD_MODE;
58     const passwordLabel = isOnePasswordMode ? c('Title').t`Password` : c('Title').t`Main password`;
59     const passwordButtonLabel = isOnePasswordMode ? c('Title').t`Change password` : c('Title').t`Change login password`;
60     const changePasswordMode = isOnePasswordMode
61         ? MODES.CHANGE_ONE_PASSWORD_MODE
62         : MODES.CHANGE_TWO_PASSWORD_LOGIN_MODE;
63     const loading = loadingUserSettings || loadingUser;
65     const handleChangePassword = (mode: MODES) => {
66         setTmpPasswordMode(mode);
67         setChangePasswordModalOpen(true);
68     };
70     useSearchParamsEffect(
71         (params) => {
72             if (loading) {
73                 return;
74             }
75             const action = params.get('action');
77             if (!action) {
78                 return;
79             }
81             if (action === 'change-password') {
82                 handleChangePassword(changePasswordMode);
83                 params.delete('action');
84                 return params;
85             } else if (action === 'session-recovery-password-reset-available') {
86                 setSkipInfoStep(false);
87                 setSessionRecoveryPasswordResetModalOpen(true);
88                 params.delete('action');
89                 return params;
90             } else if (action === 'session-recovery-reset-password') {
91                 setSkipInfoStep(true);
92                 setSessionRecoveryPasswordResetModalOpen(true);
93                 params.delete('action');
94                 return params;
95             }
96         },
97         [loading]
98     );
100     if (loading) {
101         return <Loader />;
102     }
104     // Users without any keys setup are by default in two password mode, even if they have an address.
105     // Don't allow them to change two-password mode.
106     const hasTwoPasswordOption = user.Keys.length > 0;
108     const onRecoveryClick = (() => {
109         if (hasRecoveryMethod) {
110             return () => {
111                 changePasswordModal.onClose();
112                 setRecoveryModalOpen(true);
113             };
114         }
116         if (isSessionRecoveryInitiationAvailable) {
117             return () => {
118                 changePasswordModal.onClose();
119                 setSessionRecoveryModalOpen(true);
120             };
121         }
123         return undefined;
124     })();
126     return (
127         <>
128             {renderChangePasswordModal && tmpPasswordMode && (
129                 <ChangePasswordModal
130                     mode={tmpPasswordMode}
131                     onRecoveryClick={onRecoveryClick}
132                     onSuccess={() => {
133                         dismissSessionRecoveryCancelled();
134                     }}
135                     {...changePasswordModal}
136                 />
137             )}
138             {renderChangeBackupPasswordModal && <ChangeBackupPasswordModal {...changeBackupPasswordModal} />}
139             {renderSessionRecoveryModal && (
140                 <InitiateSessionRecoveryModal
141                     onUseRecoveryMethodClick={() => {
142                         sessionRecoveryModal.onClose();
143                         setRecoveryModalOpen(true);
144                     }}
145                     {...sessionRecoveryModal}
146                 />
147             )}
148             {renderRecoveryModal && (
149                 <ReauthUsingRecoveryModal
150                     availableRecoveryMethods={availableRecoveryMethods}
151                     onBack={() => {
152                         recoveryModal.onClose();
153                         setChangePasswordModalOpen(true);
154                     }}
155                     onInitiateSessionRecoveryClick={() => {
156                         recoveryModal.onClose();
157                         setSessionRecoveryModalOpen(true);
158                     }}
159                     onSuccess={() => setChangePasswordAfterReauthModalOpen(true)}
160                     {...recoveryModal}
161                 />
162             )}
163             {renderChangePasswordAfterReauthModal && (
164                 <ChangePasswordModal
165                     mode={MODES.CHANGE_ONE_PASSWORD_MODE}
166                     signedInRecoveryFlow
167                     {...changePasswordAfterReauthModal}
168                 />
169             )}
170             {renderSessionRecoveryPasswordResetModal && (
171                 <PasswordResetAvailableAccountModal
172                     skipInfoStep={skipInfoStep}
173                     {...sessionRecoveryPasswordResetModal}
174                 />
175             )}
176             {(() => {
177                 if (getIsGlobalSSOAccount(user)) {
178                     return (
179                         <SettingsSection>
180                             <SettingsLayout>
181                                 <SettingsLayoutLeft>
182                                     <label htmlFor="passwordChange" className="text-semibold">
183                                         {c('sso').t`Backup password`}
184                                     </label>
185                                 </SettingsLayoutLeft>
186                                 <SettingsLayoutRight>
187                                     <Button id="passwordChange" onClick={() => setChangeBackupPasswordModalOpen(true)}>
188                                         {c('sso').t`Change backup password`}
189                                     </Button>
190                                 </SettingsLayoutRight>
191                             </SettingsLayout>
192                         </SettingsSection>
193                     );
194                 }
196                 return (
197                     <SettingsSection>
198                         <SettingsLayout>
199                             <SettingsLayoutLeft>
200                                 <label htmlFor="passwordChange" className="text-semibold">
201                                     {passwordLabel}
202                                 </label>
203                             </SettingsLayoutLeft>
204                             <SettingsLayoutRight>
205                                 <Button onClick={() => handleChangePassword(changePasswordMode)}>
206                                     {passwordButtonLabel}
207                                 </Button>
208                             </SettingsLayoutRight>
209                         </SettingsLayout>
210                         {hasTwoPasswordOption && (
211                             <>
212                                 <SettingsLayout>
213                                     <SettingsLayoutLeft>
214                                         <label htmlFor="passwordModeToggle" className="text-semibold">
215                                             <span className="mr-2">{c('Label').t`Two-password mode`}</span>
216                                             <Info
217                                                 url={getKnowledgeBaseUrl('/single-password')}
218                                                 title={c('Info')
219                                                     .t`Two-password mode requires two passwords: one to sign in to your account and one to decrypt your data. (Advanced)`}
220                                             />
221                                         </label>
222                                     </SettingsLayoutLeft>
223                                     <SettingsLayoutRight isToggleContainer>
224                                         <Toggle
225                                             loading={loadingUserSettings}
226                                             checked={!isOnePasswordMode}
227                                             id="passwordModeToggle"
228                                             onChange={() =>
229                                                 handleChangePassword(
230                                                     isOnePasswordMode
231                                                         ? MODES.SWITCH_TWO_PASSWORD
232                                                         : MODES.SWITCH_ONE_PASSWORD
233                                                 )
234                                             }
235                                         />
236                                     </SettingsLayoutRight>
237                                 </SettingsLayout>
238                                 {!isOnePasswordMode && (
239                                     <SettingsLayout>
240                                         <SettingsLayoutLeft>
241                                             <label htmlFor="passwordModeToggle" className="text-semibold">
242                                                 <span className="mr-2">{c('Label').t`Second password`}</span>
243                                                 <Info url={getKnowledgeBaseUrl('/single-password')} />
244                                             </label>
245                                         </SettingsLayoutLeft>
246                                         <SettingsLayoutRight>
247                                             <Button
248                                                 onClick={() =>
249                                                     handleChangePassword(MODES.CHANGE_TWO_PASSWORD_MAILBOX_MODE)
250                                                 }
251                                             >
252                                                 {c('Action').t`Change second password`}
253                                             </Button>
254                                         </SettingsLayoutRight>
255                                     </SettingsLayout>
256                                 )}
257                             </>
258                         )}
259                     </SettingsSection>
260                 );
261             })()}
262         </>
263     );
266 export default PasswordsSection;