diff --git a/native/redux/redux-setup.js b/native/redux/redux-setup.js --- a/native/redux/redux-setup.js +++ b/native/redux/redux-setup.js @@ -13,8 +13,6 @@ logInActionTypes, keyserverAuthActionTypes, deleteKeyserverAccountActionTypes, - identityRegisterActionTypes, - identityLogInActionTypes, } from 'lib/actions/user-actions.js'; import { setNewSessionActionType } from 'lib/keyserver-conn/keyserver-conn-types.js'; import type { ThreadStoreOperation } from 'lib/ops/thread-store-ops.js'; @@ -22,6 +20,7 @@ import { queueDBOps } from 'lib/reducers/db-ops-reducer.js'; import { reduceLoadingStatuses } from 'lib/reducers/loading-reducer.js'; import baseReducer from 'lib/reducers/master-reducer.js'; +import { reduceCurrentUserInfo } from 'lib/reducers/user-reducer.js'; import { invalidSessionDowngrade, invalidSessionRecovery, @@ -49,8 +48,8 @@ import { remoteReduxDevServerConfig } from './dev-tools.js'; import { persistConfig, setPersistor } from './persist.js'; import { onStateDifference } from './redux-debug-utils.js'; -import { nonUserSpecificFieldsNative } from './state-types.js'; import type { AppState } from './state-types.js'; +import { nonUserSpecificFieldsNative } from './state-types.js'; import { getGlobalNavContext } from '../navigation/icky-global.js'; import { activeMessageListSelector } from '../navigation/nav-selectors.js'; import reactotron from '../reactotron.js'; @@ -198,20 +197,6 @@ return state; } - if ( - action.type === logOutActionTypes.success || - action.type === deleteAccountActionTypes.success || - action.type === identityRegisterActionTypes.success || - (action.type === identityLogInActionTypes.success && - action.payload.userID !== action.payload.preRequestUserState?.id) - ) { - state = resetUserSpecificState( - state, - defaultState, - nonUserSpecificFieldsNative, - ); - } - if (action.type === updateDimensionsActiveType) { return { ...state, @@ -275,6 +260,37 @@ } } + // We're calling this reducer twice: here and in the baseReducer. This call + // is only used to determine the new current user ID. We don't want to use + // the remaining part of the current user info, because it is possible that + // the reducer returned a modified ID without cleared remaining parts of + // the current user info - this would be a bug, but we want to be extra + // careful when clearing the state. + // When newCurrentUserInfo has the same ID as state.currentUserInfo the state + // won't be cleared and the current user info determined in baseReducer will + // be equal to the newCurrentUserInfo. + // When newCurrentUserInfo has different ID than state.currentUserInfo, we + // reset the state and pass it to the baseReducer. Then, in baseReducer, + // reduceCurrentUserInfo acts on the cleared state and may return a different + // result than newCurrentUserInfo. + // Overall, the solution is a little wasteful, but makes us sure that we never + // keep the info of the user when the current user ID changes. + const newCurrentUserInfo = reduceCurrentUserInfo( + state.currentUserInfo, + action, + ); + if ( + state.currentUserInfo && + !state.currentUserInfo?.anonymous && + newCurrentUserInfo?.id !== state.currentUserInfo?.id + ) { + state = resetUserSpecificState( + state, + defaultState, + nonUserSpecificFieldsNative, + ); + } + const baseReducerResult = baseReducer( state, (action: BaseAction), diff --git a/web/redux/redux-setup.js b/web/redux/redux-setup.js --- a/web/redux/redux-setup.js +++ b/web/redux/redux-setup.js @@ -7,8 +7,6 @@ logOutActionTypes, deleteKeyserverAccountActionTypes, deleteAccountActionTypes, - identityRegisterActionTypes, - identityLogInActionTypes, keyserverAuthActionTypes, } from 'lib/actions/user-actions.js'; import { setNewSessionActionType } from 'lib/keyserver-conn/keyserver-conn-types.js'; @@ -23,6 +21,7 @@ import { queueDBOps } from 'lib/reducers/db-ops-reducer.js'; import { reduceLoadingStatuses } from 'lib/reducers/loading-reducer.js'; import baseReducer from 'lib/reducers/master-reducer.js'; +import { reduceCurrentUserInfo } from 'lib/reducers/user-reducer.js'; import { mostRecentlyReadThreadSelector } from 'lib/selectors/thread-selectors.js'; import { isLoggedIn } from 'lib/selectors/user-selectors.js'; import { @@ -334,21 +333,38 @@ }; } - if ( - action.type === logOutActionTypes.success || - action.type === deleteAccountActionTypes.success || - action.type === identityRegisterActionTypes.success || - (action.type === identityLogInActionTypes.success && - action.payload.userID !== action.payload.preRequestUserState?.id) - ) { - state = resetUserSpecificState( - state, - defaultWebState, - nonUserSpecificFieldsWeb, + if (action.type !== updateNavInfoActionType) { + // We're calling this reducer twice: here and in the baseReducer. This call + // is only used to determine the new current user ID. We don't want to use + // the remaining part of the current user info, because it is possible that + // the reducer returned a modified ID without cleared remaining parts of + // the current user info - this would be a bug, but we want to be extra + // careful when clearing the state. + // When newCurrentUserInfo has the same ID as state.currentUserInfo + // the state won't be cleared and the current user info determined in + // baseReducer will be equal to the newCurrentUserInfo. + // When newCurrentUserInfo has different ID than state.currentUserInfo, we + // reset the state and pass it to the baseReducer. Then, in baseReducer, + // reduceCurrentUserInfo acts on the cleared state and may return + // a different result than newCurrentUserInfo. + // Overall, the solution is a little wasteful, but makes us sure that we + // never keep the info of the user when the current user ID changes. + const newCurrentUserInfo = reduceCurrentUserInfo( + state.currentUserInfo, + action, ); - } + if ( + state.currentUserInfo && + !state.currentUserInfo?.anonymous && + newCurrentUserInfo?.id !== state.currentUserInfo?.id + ) { + state = resetUserSpecificState( + state, + defaultWebState, + nonUserSpecificFieldsWeb, + ); + } - if (action.type !== updateNavInfoActionType) { const baseReducerResult = baseReducer(state, action, onStateDifference); state = baseReducerResult.state; storeOperations = {