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 });
52 if (offlineEnabled === false) {
56 .t`You're currently offline. Please resume connectivity in order to unlock ${PASS_SHORT_APP_NAME}.`,
60 }, [online, offlineEnabled]);
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);
76 [biometricsUnlock.loading]
86 loading={biometricsUnlock.loading}
90 <Icon name={isMac() ? 'fingerprint' : 'pass-lockmode-biometrics'} className="mr-1" />
91 {c('Action').t`Unlock`}