From 24377dd6805f7c15cd3b52f3623104f788e73717 Mon Sep 17 00:00:00 2001 From: Tudor Morar Date: Tue, 16 Jul 2024 16:42:55 +0300 Subject: [PATCH] Update logout --- src/core/methods/account/getIsLoggedIn.ts | 4 +- .../actions/loginInfo/loginInfoActions.ts | 12 ----- .../actions/sharedActions/sharedActions.ts | 36 ++------------- src/store/middleware/applyMiddleware.ts | 7 +++ src/store/middleware/index.ts | 1 + src/store/middleware/logoutMiddleware.ts | 46 +++++++++++++++++++ src/store/selectors/accountSelectors.ts | 6 +++ src/store/slices/loginInfo/loginInfo.types.ts | 1 - src/store/slices/loginInfo/loginInfoSlice.ts | 1 - src/store/store.ts | 36 ++++++--------- 10 files changed, 81 insertions(+), 69 deletions(-) create mode 100644 src/store/middleware/applyMiddleware.ts create mode 100644 src/store/middleware/index.ts create mode 100644 src/store/middleware/logoutMiddleware.ts diff --git a/src/core/methods/account/getIsLoggedIn.ts b/src/core/methods/account/getIsLoggedIn.ts index 110d470..9316522 100644 --- a/src/core/methods/account/getIsLoggedIn.ts +++ b/src/core/methods/account/getIsLoggedIn.ts @@ -1,5 +1,7 @@ +import { isLoggedInSelector } from 'store/selectors/accountSelectors'; import { getAddress } from './getAddress'; +import { getState } from 'store/store'; export function getIsLoggedIn() { - return Boolean(getAddress()); + return isLoggedInSelector(getState()); } diff --git a/src/store/actions/loginInfo/loginInfoActions.ts b/src/store/actions/loginInfo/loginInfoActions.ts index cca6409..e15d021 100644 --- a/src/store/actions/loginInfo/loginInfoActions.ts +++ b/src/store/actions/loginInfo/loginInfoActions.ts @@ -50,15 +50,3 @@ export const setIsWalletConnectV2Initialized = (isInitialized: boolean) => store.setState(({ loginInfo: state }) => { state.isWalletConnectV2Initialized = isInitialized; }); - -// reset by passing null -export const updateLoginExpiresAt = (data?: null) => - store.setState(({ loginInfo: state }) => { - if (data === null) { - state.loginExpiresAt = null; - return; - } - - const loginExpiresAt = new Date().setHours(new Date().getHours() + 24); - state.loginExpiresAt = loginExpiresAt; - }); diff --git a/src/store/actions/sharedActions/sharedActions.ts b/src/store/actions/sharedActions/sharedActions.ts index 33bed3d..594c4e1 100644 --- a/src/store/actions/sharedActions/sharedActions.ts +++ b/src/store/actions/sharedActions/sharedActions.ts @@ -1,38 +1,9 @@ import { Address } from '@multiversx/sdk-core/out'; -import { initialState as initialAccountState } from 'store/slices/account/accountSlice'; -import { initialState as initialLoginInfoState } from 'store/slices/loginInfo/loginInfoSlice'; -import { getState, store } from '../../store'; +import { store } from '../../store'; import { LoginMethodsEnum } from 'types/enums.types'; -import { updateLoginExpiresAt } from '../loginInfo/loginInfoActions'; - -store.subscribe( - (state) => ({ account: state.account, network: state.network }), // only listen to changes in account and network - ({ account }) => { - const isLoggedIn = Boolean(account.address); - const loginTimestamp = getState().loginInfo.loginExpiresAt; - - if (!isLoggedIn || loginTimestamp == null) { - return; - } - - const isExpired = loginTimestamp - Date.now() < 0; - - if (isExpired) { - logoutAction(); - return; - } - - updateLoginExpiresAt(); - } -); - -export const logoutAction = () => - store.setState((store) => { - store.account = initialAccountState; - store.loginInfo = initialLoginInfoState; - updateLoginExpiresAt(null); - }); +import { resetStore } from 'store/middleware/logoutMiddleware'; +export const logoutAction = () => store.setState(resetStore); export interface LoginActionPayloadType { address: string; loginMethod: LoginMethodsEnum; @@ -43,5 +14,4 @@ export const loginAction = ({ address, loginMethod }: LoginActionPayloadType) => account.address = address; account.publicKey = new Address(address).hex(); loginInfo.loginMethod = loginMethod; - updateLoginExpiresAt(); }); diff --git a/src/store/middleware/applyMiddleware.ts b/src/store/middleware/applyMiddleware.ts new file mode 100644 index 0000000..ab0d0dd --- /dev/null +++ b/src/store/middleware/applyMiddleware.ts @@ -0,0 +1,7 @@ +import { StoreType } from '../store.types'; +import { StoreApi } from 'zustand/vanilla'; +import { logoutMiddleware } from './logoutMiddleware'; + +export const applyMiddleware = (store: StoreApi) => { + store.subscribe(logoutMiddleware); +}; diff --git a/src/store/middleware/index.ts b/src/store/middleware/index.ts new file mode 100644 index 0000000..6f49a51 --- /dev/null +++ b/src/store/middleware/index.ts @@ -0,0 +1 @@ +export * from './applyMiddleware'; diff --git a/src/store/middleware/logoutMiddleware.ts b/src/store/middleware/logoutMiddleware.ts new file mode 100644 index 0000000..303703c --- /dev/null +++ b/src/store/middleware/logoutMiddleware.ts @@ -0,0 +1,46 @@ +import { storage } from 'storage'; +import { WritableDraft } from 'immer'; +import { initialState as initialAccountState } from 'store/slices/account/accountSlice'; +import { initialState as initialLoginInfoState } from 'store/slices/loginInfo/loginInfoSlice'; +import { localStorageKeys } from 'storage/local'; +import { isLoggedInSelector } from 'store/selectors'; +import { StoreType } from '../store.types'; + +export const resetStore = (store: WritableDraft) => { + store.account = initialAccountState; + store.loginInfo = initialLoginInfoState; +}; + +export function getNewLoginExpiresTimestamp() { + return new Date().setHours(new Date().getHours() + 24); +} + +export function setLoginExpiresAt(expiresAt: number) { + storage.local.setItem({ + key: localStorageKeys.loginExpiresAt, + data: expiresAt, + expires: expiresAt + }); +} + +export const logoutMiddleware = (newStore: StoreType) => { + const isLoggedIn = isLoggedInSelector(newStore); + const loginTimestamp = storage.local.getItem(localStorageKeys.loginExpiresAt); + + if (!isLoggedIn) { + return; + } + + if (loginTimestamp == null) { + setLoginExpiresAt(getNewLoginExpiresTimestamp()); + return; + } + + const now = Date.now(); + const isExpired = loginTimestamp - now < 0; + + if (isExpired) { + // logout + resetStore(newStore); + } +}; diff --git a/src/store/selectors/accountSelectors.ts b/src/store/selectors/accountSelectors.ts index 75d6fbf..beeed30 100644 --- a/src/store/selectors/accountSelectors.ts +++ b/src/store/selectors/accountSelectors.ts @@ -8,3 +8,9 @@ export const addressSelector = ({ account: { address } }: StoreType) => address; export const accountNonceSelector = (store: StoreType) => accountSelector(store)?.nonce || 0; + +export const isLoggedInSelector = (store: StoreType) => { + const address = addressSelector(store); + const account = accountSelector(store); + return address && account.address === address; +}; diff --git a/src/store/slices/loginInfo/loginInfo.types.ts b/src/store/slices/loginInfo/loginInfo.types.ts index 325bef1..4201306 100644 --- a/src/store/slices/loginInfo/loginInfo.types.ts +++ b/src/store/slices/loginInfo/loginInfo.types.ts @@ -22,7 +22,6 @@ export interface LoginInfoSliceType { walletConnectLogin: WalletConnectLoginType | null; ledgerLogin: LedgerLoginType | null; tokenLogin: TokenLoginType | null; - loginExpiresAt: number | null; walletLogin: LoginInfoType | null; extensionLogin: LoginInfoType | null; operaLogin: LoginInfoType | null; diff --git a/src/store/slices/loginInfo/loginInfoSlice.ts b/src/store/slices/loginInfo/loginInfoSlice.ts index fe9a01b..fb455d0 100644 --- a/src/store/slices/loginInfo/loginInfoSlice.ts +++ b/src/store/slices/loginInfo/loginInfoSlice.ts @@ -8,7 +8,6 @@ export const initialState: LoginInfoSliceType = { walletConnectLogin: null, ledgerLogin: null, tokenLogin: null, - loginExpiresAt: null, walletLogin: null, extensionLogin: null, operaLogin: null, diff --git a/src/store/store.ts b/src/store/store.ts index f3b80a6..8f963f3 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -1,49 +1,43 @@ import { createStore } from 'zustand/vanilla'; -import { - createJSONStorage, - devtools, - persist, - subscribeWithSelector -} from 'zustand/middleware'; +import { createJSONStorage, devtools, persist } from 'zustand/middleware'; import { immer } from 'zustand/middleware/immer'; import { networkSlice } from './slices/network/networkSlice'; import { accountSlice } from './slices/account/accountSlice'; import { createBoundedUseStore } from './createBoundedStore'; import { loginInfoSlice } from './slices/loginInfo'; import { StoreType } from './store.types'; +import { applyMiddleware } from './middleware/applyMiddleware'; export type MutatorsIn = [ - ['zustand/subscribeWithSelector', never], ['zustand/devtools', never], ['zustand/persist', unknown], ['zustand/immer', never] ]; export type MutatorsOut = [ - ['zustand/subscribeWithSelector', never], ['zustand/devtools', never], ['zustand/persist', StoreType], ['zustand/immer', never] ]; export const store = createStore( - subscribeWithSelector( - devtools( - persist( - immer((...args) => ({ - network: networkSlice(...args), - account: accountSlice(...args), - loginInfo: loginInfoSlice(...args) - })), - { - name: 'sdk-dapp-store', - storage: createJSONStorage(() => localStorage) - } - ) + devtools( + persist( + immer((...args) => ({ + network: networkSlice(...args), + account: accountSlice(...args), + loginInfo: loginInfoSlice(...args) + })), + { + name: 'sdk-dapp-store', + storage: createJSONStorage(() => localStorage) + } ) ) ); +applyMiddleware(store); + export const getState = () => store.getState(); export const useStore = createBoundedUseStore(store);