Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / packages / pass / components / Lock / BiometricsUnlock.tsx
blob50b02ad5c20256b20f61b365b5b1d5f4278de328
1 import { type FC, useCallback, useEffect } from 'react';
2 import { useHistory } from 'react-router-dom';
4 import { c } from 'ttag';
6 import { Button } from '@proton/atoms';
7 import { useNotifications } from '@proton/components';
8 import Icon from '@proton/components/components/icon/Icon';
9 import { useAuthStore } from '@proton/pass/components/Core/AuthStoreProvider';
10 import { useConnectivity } from '@proton/pass/components/Core/ConnectivityProvider';
11 import { usePassCore } from '@proton/pass/components/Core/PassCoreProvider';
12 import type { AuthRouteState } from '@proton/pass/components/Navigation/routing';
13 import { useRequest } from '@proton/pass/hooks/useRequest';
14 import { useRerender } from '@proton/pass/hooks/useRerender';
15 import { useVisibleEffect } from '@proton/pass/hooks/useVisibleEffect';
16 import { LockMode } from '@proton/pass/lib/auth/lock/types';
17 import { unlock } from '@proton/pass/store/actions';
18 import type { MaybeNull } from '@proton/pass/types';
19 import { getBasename } from '@proton/shared/lib/authentication/pathnameHelper';
20 import { PASS_SHORT_APP_NAME } from '@proton/shared/lib/constants';
21 import { isMac } from '@proton/shared/lib/helpers/browser';
22 import noop from '@proton/utils/noop';
24 type Props = { offlineEnabled?: boolean };
26 export const BiometricsUnlock: FC<Props> = ({ offlineEnabled }) => {
27     const { createNotification } = useNotifications();
29     const online = useConnectivity();
30     const authStore = useAuthStore();
31     const history = useHistory<MaybeNull<AuthRouteState>>();
33     const biometricsUnlock = useRequest(unlock, { initial: true });
34     const disabled = !online && !offlineEnabled;
35     const [key, rerender] = useRerender();
36     const { getBiometricsKey } = usePassCore();
38     const onUnlock = useCallback(async () => {
39         /** As booting offline will not trigger the AuthService::login
40          * sequence we need to re-apply the redirection logic implemented
41          * in the service's `onLoginComplete` callback */
42         const secret = (await getBiometricsKey?.(authStore!).catch(noop)) ?? '';
43         const localID = authStore?.getLocalID();
44         history.replace(getBasename(localID) ?? '/', null);
45         biometricsUnlock.dispatch({ mode: LockMode.BIOMETRICS, secret });
46     }, []);
48     useEffect(() => {
49         if (!online) {
50             rerender();
52             if (offlineEnabled === false) {
53                 createNotification({
54                     type: 'error',
55                     text: c('Error')
56                         .t`You're currently offline. Please resume connectivity in order to unlock ${PASS_SHORT_APP_NAME}.`,
57                 });
58             }
59         }
60     }, [online, offlineEnabled]);
62     useVisibleEffect(
63         (visible) => {
64             /** if user has triggered the lock - don't auto-prompt.  */
65             const { userInitiatedLock = false } = history.location.state ?? {};
67             /** If page is hidden away - remove the `userInitiatedLock` flag
68              * to force biometrics prompt when re-opening the app */
69             if (!visible && userInitiatedLock) history.replace({ ...history.location, state: null });
71             /* Trigger unlock automatically on first render if the app is
72              * focused and the current lock was not user initiated */
73             if (!visible || biometricsUnlock.loading || !document.hasFocus()) return;
74             if (!userInitiatedLock) onUnlock().catch(noop);
75         },
76         [biometricsUnlock.loading]
77     );
79     return (
80         <Button
81             key={key}
82             pill
83             shape="solid"
84             color="norm"
85             className="w-full"
86             loading={biometricsUnlock.loading}
87             disabled={disabled}
88             onClick={onUnlock}
89         >
90             <Icon name={isMac() ? 'fingerprint' : 'pass-lockmode-biometrics'} className="mr-1" />
91             {c('Action').t`Unlock`}
92         </Button>
93     );