diff --git a/src/navigation/createKeysNavigator/index.tsx b/src/navigation/createKeysNavigator/index.tsx index 6a2d00a11..6e1ae97e8 100644 --- a/src/navigation/createKeysNavigator/index.tsx +++ b/src/navigation/createKeysNavigator/index.tsx @@ -10,7 +10,8 @@ import { SecurityInformation, RetryLogin, } from 'screens/createKeys' -import { selectIsUnlocked, selectKeysExist } from 'store/slices/settingsSlice' +import { selectIsUnlocked } from 'store/slices/settingsSlice' +import { selectKeysExist } from 'store/slices/persistentDataSlice' import { useAppSelector } from 'store/storeUtils' import { PinScreen } from 'screens/pinScreen' diff --git a/src/redux/rootReducer.ts b/src/redux/rootReducer.ts index 9fc2d90d0..9695cae91 100644 --- a/src/redux/rootReducer.ts +++ b/src/redux/rootReducer.ts @@ -1,17 +1,26 @@ import { combineReducers } from '@reduxjs/toolkit' -import { persistReducer, createMigrate, PersistConfig } from 'redux-persist' +import { + persistReducer, + createMigrate, + PersistConfig, + getStoredState, +} from 'redux-persist' import { reduxStorage } from 'storage/ReduxStorage' import { contactsReducer } from 'store/slices/contactsSlice' import { ProfileStatus } from 'navigation/profileNavigator/types' import { getCurrentChainId } from 'storage/ChainStorage' +import { + persistentDataReducer, + PersistentDataState, +} from 'store/slices/persistentDataSlice' import { accountsReducer } from './slices/accountsSlice' import { balancesReducer } from './slices/balancesSlice' import { profileReducer } from './slices/profileSlice' import { settingsSliceReducer } from './slices/settingsSlice' import { transactionsReducer } from './slices/transactionsSlice' -import { usdPriceReducer } from './slices/usdPricesSlice' +import { usdPriceReducer, UsdPricesState } from './slices/usdPricesSlice' import { SettingsSlice } from './slices/settingsSlice/types' const migrations = { @@ -29,13 +38,47 @@ const migrations = { }), } +const firstPersistentDataMigration = async state => { + // First migration, check if state already exists + if (!state) { + // First step is to get old settings storage - usually testnet storage has all the info + const oldStorage = await getStoredState({ + key: 'settings', + storage: reduxStorage(31), + }) + // If old storage exists, we will migrate it to the current state + if (oldStorage) { + return { + keysExist: oldStorage.keysExist, + isFirstLaunch: oldStorage.isFirstLaunch, + pin: oldStorage.pin, + } + } + } + return state +} + export const createRootReducer = () => { + const persistedReduxStorageAcrossChainSwitches = reduxStorage(0) + const settingsPersistConfig: PersistConfig = { key: 'settings', - whitelist: ['pin', 'keysExist', 'isFirstLaunch', 'usedBitcoinAddresses'], + whitelist: ['usedBitcoinAddresses'], storage: reduxStorage(getCurrentChainId()), } + const persistentDataPersistConfig: PersistConfig = { + key: 'persistentData', + whitelist: ['keysExist', 'isFirstLaunch', 'pin'], + storage: persistedReduxStorageAcrossChainSwitches, + migrate: firstPersistentDataMigration, + } + + const usdPricesPersistConfig: PersistConfig = { + key: 'usdPrices', + storage: persistedReduxStorageAcrossChainSwitches, + } + const rootPersistConfig = { key: 'root', version: 0, @@ -52,13 +95,17 @@ export const createRootReducer = () => { } const reducers = combineReducers({ - usdPrices: usdPriceReducer, + usdPrices: persistReducer(usdPricesPersistConfig, usdPriceReducer), balances: balancesReducer, transactions: transactionsReducer, settings: persistReducer(settingsPersistConfig, settingsSliceReducer), profile: profileReducer, accounts: accountsReducer, contacts: contactsReducer, + persistentData: persistReducer( + persistentDataPersistConfig, + persistentDataReducer, + ), }) return persistReducer(rootPersistConfig, reducers) diff --git a/src/redux/slices/persistentDataSlice/index.ts b/src/redux/slices/persistentDataSlice/index.ts new file mode 100644 index 000000000..f1ba756dc --- /dev/null +++ b/src/redux/slices/persistentDataSlice/index.ts @@ -0,0 +1,36 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit' + +import { PersistentDataState } from './types' + +const initialState: PersistentDataState = { + keysExist: false, + isFirstLaunch: true, + pin: null, +} + +/** + * Data that will be persisted across chain ID switches + */ +const persistentDataSlice = createSlice({ + name: 'persistentData', + initialState, + reducers: { + setKeysExist: (state, { payload }: PayloadAction) => { + state.keysExist = payload + }, + setIsFirstLaunch: (state, { payload }: PayloadAction) => { + state.isFirstLaunch = payload + }, + setPinState: (state, { payload }: PayloadAction) => { + state.pin = payload + }, + }, +}) + +export const { setKeysExist, setIsFirstLaunch, setPinState } = + persistentDataSlice.actions + +export const persistentDataReducer = persistentDataSlice.reducer + +export * from './types' +export * from './selectors' diff --git a/src/redux/slices/persistentDataSlice/selectors.ts b/src/redux/slices/persistentDataSlice/selectors.ts new file mode 100644 index 000000000..18fc40d38 --- /dev/null +++ b/src/redux/slices/persistentDataSlice/selectors.ts @@ -0,0 +1,6 @@ +import { RootState } from 'src/redux' + +export const selectKeysExist = ({ persistentData }: RootState) => + persistentData.keysExist + +export const selectPin = ({ persistentData }: RootState) => persistentData.pin diff --git a/src/redux/slices/persistentDataSlice/types.ts b/src/redux/slices/persistentDataSlice/types.ts new file mode 100644 index 000000000..95c0b8511 --- /dev/null +++ b/src/redux/slices/persistentDataSlice/types.ts @@ -0,0 +1,5 @@ +export interface PersistentDataState { + keysExist: boolean + isFirstLaunch: boolean + pin: string | null +} diff --git a/src/redux/slices/settingsSlice/index.ts b/src/redux/slices/settingsSlice/index.ts index c647eb80a..71d706461 100644 --- a/src/redux/slices/settingsSlice/index.ts +++ b/src/redux/slices/settingsSlice/index.ts @@ -31,6 +31,11 @@ import { } from 'shared/constants/chainConstants' import { getCurrentChainId } from 'storage/ChainStorage' import { resetReduxStorage } from 'storage/ReduxStorage' +import { + setIsFirstLaunch, + setKeysExist, + setPinState, +} from 'store/slices/persistentDataSlice' import { Bitcoin, @@ -145,7 +150,7 @@ export const unlockApp = createAsyncThunk< try { // check if it is a first launch, deleteKeys const { - settings: { isFirstLaunch }, + persistentData: { isFirstLaunch }, } = thunkAPI.getState() // if previously installed the app, remove stored encryted keys if (isFirstLaunch && !__DEV__) { @@ -171,7 +176,7 @@ export const unlockApp = createAsyncThunk< if (Platform.OS === 'android' && !supportedBiometry && !pinUnlocked) { const { - settings: { pin }, + persistentData: { pin }, } = thunkAPI.getState() // if there's no pin yet and biometrics removed @@ -314,8 +319,6 @@ export const resetApp = createAsyncThunk( // ) const initialState: SettingsSlice = { - keysExist: false, - isFirstLaunch: true, isSetup: false, topColor: sharedColors.primary, requests: [], @@ -329,7 +332,6 @@ const initialState: SettingsSlice = { previouslyUnlocked: false, fullscreen: false, hideBalance: false, - pin: null, bitcoin: null, chainId: 31, usedBitcoinAddresses: {}, @@ -345,12 +347,6 @@ const settingsSlice = createSlice({ name: 'settings', initialState: createInitialState, reducers: { - setKeysExist: (state, { payload }: PayloadAction) => { - state.keysExist = payload - }, - setIsFirstLaunch: (state, { payload }: PayloadAction) => { - state.isFirstLaunch = payload - }, setIsSetup: (state, { payload }: PayloadAction) => { state.isSetup = payload return state @@ -378,9 +374,6 @@ const settingsSlice = createSlice({ setPreviouslyUnlocked: (state, { payload }: PayloadAction) => { state.previouslyUnlocked = payload }, - setPinState: (state, { payload }: PayloadAction) => { - state.pin = payload - }, setWallet: ( state, { payload: { wallet, walletIsDeployed } }: PayloadAction, @@ -492,14 +485,11 @@ const settingsSlice = createSlice({ }) export const { - setKeysExist, - setIsFirstLaunch, setIsSetup, changeTopColor, onRequest, closeRequest, setWallet, - setPinState, setChainId, setAppIsActive, setUnlocked, diff --git a/src/redux/slices/settingsSlice/selectors.ts b/src/redux/slices/settingsSlice/selectors.ts index f482310e5..f42183c1a 100644 --- a/src/redux/slices/settingsSlice/selectors.ts +++ b/src/redux/slices/settingsSlice/selectors.ts @@ -1,10 +1,5 @@ import { RootState } from 'store/store' -export const selectKeysExist = ({ settings }: RootState) => settings.keysExist - -export const selectIsFirstLaunch = ({ settings }: RootState) => - settings.isFirstLaunch - export const selectRequests = ({ settings }: RootState) => settings.requests export const selectTopColor = ({ settings }: RootState) => settings.topColor @@ -44,7 +39,6 @@ export const selectFullscreen = ({ settings }: RootState) => settings.fullscreen export const selectHideBalance = ({ settings }: RootState) => settings.hideBalance -export const selectPin = ({ settings }: RootState) => settings.pin export const selectBitcoin = ({ settings }: RootState) => settings.bitcoin diff --git a/src/redux/slices/settingsSlice/types.ts b/src/redux/slices/settingsSlice/types.ts index ca9bd5cb3..37eb1674f 100644 --- a/src/redux/slices/settingsSlice/types.ts +++ b/src/redux/slices/settingsSlice/types.ts @@ -68,8 +68,6 @@ export interface Bitcoin { } export interface SettingsSlice { - keysExist: boolean - isFirstLaunch: boolean isSetup: boolean requests: RequestWithBitcoin[] topColor: ColorValue @@ -84,7 +82,6 @@ export interface SettingsSlice { previouslyUnlocked: boolean fullscreen: boolean hideBalance: boolean - pin: string | null bitcoin: Bitcoin | null usedBitcoinAddresses: { [key: string]: string } } diff --git a/src/screens/pinScreen/index.tsx b/src/screens/pinScreen/index.tsx index fa1df071b..7a4588b0a 100644 --- a/src/screens/pinScreen/index.tsx +++ b/src/screens/pinScreen/index.tsx @@ -23,12 +23,8 @@ import { Typography, } from 'components/index' import { useAppDispatch, useAppSelector } from 'store/storeUtils' -import { - selectPin, - setFullscreen, - setPinState, - unlockApp, -} from 'store/slices/settingsSlice' +import { setFullscreen, unlockApp } from 'store/slices/settingsSlice' +import { selectPin, setPinState } from 'store/slices/persistentDataSlice' import { sharedColors, sharedStyles } from 'shared/constants' import { castStyle } from 'shared/utils' import { diff --git a/src/screens/settings/SettingsScreen.tsx b/src/screens/settings/SettingsScreen.tsx index 638cfec1b..a30ea9a2d 100644 --- a/src/screens/settings/SettingsScreen.tsx +++ b/src/screens/settings/SettingsScreen.tsx @@ -16,9 +16,9 @@ import { castStyle } from 'shared/utils' import { selectChainId, selectChainType, - selectPin, selectWalletIsDeployed, } from 'store/slices/settingsSlice' +import { selectPin } from 'store/slices/persistentDataSlice' import { useAppSelector } from 'store/storeUtils' import { ChainTypeEnum, chainTypesById } from 'shared/constants/chainConstants' import { GlobalErrorHandlerContext } from 'components/GlobalErrorHandler/GlobalErrorHandlerContext'