1 import { useEffect, useState } from 'react';
3 import { differenceInMilliseconds } from 'date-fns';
5 import { useAddresses } from '@proton/account/addresses/hooks';
6 import { useUser } from '@proton/account/user/hooks';
7 import { useUserSettings } from '@proton/account/userSettings/hooks';
8 import { useSessionRecoveryLocalStorage } from '@proton/components/containers/account/sessionRecovery/SessionRecoveryLocalStorageManager';
9 import { useInterval } from '@proton/hooks';
10 import { APPS, DAY, HOUR, MINUTE, SECOND } from '@proton/shared/lib/constants';
11 import { MNEMONIC_STATUS, SessionRecoveryState } from '@proton/shared/lib/interfaces';
12 import { getHasMigratedAddressKeys } from '@proton/shared/lib/keys';
13 import isTruthy from '@proton/utils/isTruthy';
15 import useAuthentication from './useAuthentication';
16 import useConfig from './useConfig';
18 export const useIsSessionRecoveryInitiatedByCurrentSession = () => {
19 const [user] = useUser();
20 const authentication = useAuthentication();
22 if (!user?.AccountRecovery) {
26 return user.AccountRecovery.UID === authentication.getUID();
29 export const useSessionRecoveryState = () => {
30 const [user] = useUser();
32 if (!user?.AccountRecovery) {
33 return SessionRecoveryState.NONE;
36 return user.AccountRecovery.State;
39 export const useIsSessionRecoveryEnabled = () => {
40 const [userSettings] = useUserSettings();
42 return !!userSettings?.SessionAccountRecovery;
45 export const useAvailableRecoveryMethods = () => {
46 const [user] = useUser();
47 const [userSettings, loadingUserSettings] = useUserSettings();
49 const recoveryPhrase = user.MnemonicStatus === MNEMONIC_STATUS.SET;
50 const recoveryEmail = !!userSettings?.Email.Reset && !!userSettings?.Email.Value;
51 const recoveryPhone = !!userSettings?.Phone.Reset && !!userSettings?.Phone.Value;
53 const availableRecoveryMethods = [
54 recoveryEmail && ('email' as const),
55 recoveryPhone && ('sms' as const),
56 recoveryPhrase && ('mnemonic' as const),
59 return [availableRecoveryMethods, loadingUserSettings] as const;
62 export const useIsSessionRecoveryAvailable = () => {
63 const [user] = useUser();
64 const [addresses = [], loadingAddresses] = useAddresses();
65 const { APP_NAME } = useConfig();
67 const hasMigratedKeys = getHasMigratedAddressKeys(addresses);
68 const isPrivateUser = user?.isPrivate;
71 APP_NAME !== APPS.PROTONVPN_SETTINGS && !loadingAddresses && hasMigratedKeys && isPrivateUser,
76 export const useIsSessionRecoveryInitiationAvailable = () => {
77 const [isSessionRecoveryAvailable] = useIsSessionRecoveryAvailable();
78 const isSessionRecoveryEnabled = useIsSessionRecoveryEnabled();
79 const sessionRecoveryState = useSessionRecoveryState();
81 const sessionRecoveryInitiated =
82 sessionRecoveryState === SessionRecoveryState.GRACE_PERIOD ||
83 sessionRecoveryState === SessionRecoveryState.INSECURE;
85 return isSessionRecoveryAvailable && isSessionRecoveryEnabled && !sessionRecoveryInitiated;
89 * Determines whether applications should display session recovery in progress "notifications".
90 * Notifications here means banners or modals and not the browser notifications.
92 export const useShouldNotifySessionRecoveryInProgress = () => {
93 const [isSessionRecoveryAvailable] = useIsSessionRecoveryAvailable();
94 const sessionRecoveryState = useSessionRecoveryState();
95 const isSessionRecoveryInitiatedByCurrentSession = useIsSessionRecoveryInitiatedByCurrentSession();
96 const { hasConfirmedSessionRecoveryInProgress } = useSessionRecoveryLocalStorage();
99 isSessionRecoveryAvailable &&
100 sessionRecoveryState === SessionRecoveryState.GRACE_PERIOD &&
101 !hasConfirmedSessionRecoveryInProgress &&
102 !isSessionRecoveryInitiatedByCurrentSession
107 * Determines whether applications should display password reset available "notifications".
108 * Notifications here means banners or modals and not the browser notifications.
110 export const useShouldNotifyPasswordResetAvailable = () => {
111 const [isSessionRecoveryAvailable] = useIsSessionRecoveryAvailable();
112 const sessionRecoveryState = useSessionRecoveryState();
114 return isSessionRecoveryAvailable && sessionRecoveryState === SessionRecoveryState.INSECURE;
117 export const useSessionRecoveryGracePeriodHoursRemaining = () => {
118 const [user] = useUser();
120 if (!user.AccountRecovery || user.AccountRecovery.State !== SessionRecoveryState.GRACE_PERIOD) {
124 const msRemaining = user.AccountRecovery.EndTime * 1000 - Date.now();
126 return Math.ceil(msRemaining / HOUR);
129 export const useSessionRecoveryInsecureTimeRemaining = () => {
130 const [user] = useUser();
132 const [now, setNow] = useState(() => new Date());
133 const [interval, setInterval] = useState(HOUR);
135 const [timeRemaining, setTimeRemaining] = useState<{
142 const diff = user?.AccountRecovery?.EndTime
143 ? differenceInMilliseconds(user.AccountRecovery.EndTime * 1000, now)
150 diff < 0 ? null : interval
154 const inDays = Math.floor(diff / DAY);
155 const inHours = Math.floor(diff / HOUR);
156 const inMinutes = Math.floor(diff / MINUTE);
157 const inSeconds = Math.floor(diff / SECOND);
159 if (inMinutes <= 1) {
161 } else if (inHours <= 1) {
175 if (!user.AccountRecovery || user.AccountRecovery.State !== SessionRecoveryState.INSECURE) {
179 if (diff <= 0 || !timeRemaining) {
183 return timeRemaining;
186 export const useShouldNotifySessionRecoveryCancelled = () => {
187 const [isSessionRecoveryAvailable] = useIsSessionRecoveryAvailable();
188 const sessionRecoveryState = useSessionRecoveryState();
189 const { hasDismissedSessionRecoveryCancelled } = useSessionRecoveryLocalStorage();
192 isSessionRecoveryAvailable &&
193 sessionRecoveryState === SessionRecoveryState.CANCELLED &&
194 !hasDismissedSessionRecoveryCancelled