diff --git a/lib/utils/keyserver-store-utils.js b/lib/utils/keyserver-store-utils.js new file mode 100644 --- /dev/null +++ b/lib/utils/keyserver-store-utils.js @@ -0,0 +1,25 @@ +// @flow + +import { ashoatKeyserverID } from './validation-utils.js'; +import type { KeyserverStore } from '../types/keyserver-types.js'; + +function wipeKeyserverStore(oldStore: KeyserverStore): KeyserverStore { + const keyserverInfos = { + [ashoatKeyserverID]: { + ...oldStore.keyserverInfos[ashoatKeyserverID], + connection: { + ...oldStore.keyserverInfos[ashoatKeyserverID].connection, + connectionIssue: null, + queuedActivityUpdates: [], + lateResponses: [], + }, + cookie: null, + }, + }; + return { + ...oldStore, + keyserverInfos, + }; +} + +export { wipeKeyserverStore }; diff --git a/lib/utils/reducers-utils.js b/lib/utils/reducers-utils.js --- a/lib/utils/reducers-utils.js +++ b/lib/utils/reducers-utils.js @@ -3,10 +3,7 @@ import type { BaseNavInfo } from '../types/nav-types.js'; import type { BaseAppState } from '../types/redux-types.js'; -function resetUserSpecificStateOnIdentityActions< - N: BaseNavInfo, - T: BaseAppState, ->( +function resetUserSpecificState>( state: T, defaultState: T, nonUserSpecificFields: $ReadOnlyArray<$Keys>, @@ -21,4 +18,4 @@ return newState; } -export { resetUserSpecificStateOnIdentityActions }; +export { resetUserSpecificState }; diff --git a/lib/utils/reducers-utils.test.js b/lib/utils/reducers-utils.test.js --- a/lib/utils/reducers-utils.test.js +++ b/lib/utils/reducers-utils.test.js @@ -1,14 +1,14 @@ // @flow import { defaultNotifPermissionAlertInfo } from './push-alerts.js'; -import { resetUserSpecificStateOnIdentityActions } from './reducers-utils.js'; +import { resetUserSpecificState } from './reducers-utils.js'; import { ashoatKeyserverID } from './validation-utils.js'; import { defaultWebEnabledApps } from '../types/enabled-apps.js'; import { defaultCalendarFilters } from '../types/filter-types.js'; import { defaultKeyserverInfo } from '../types/keyserver-types.js'; import { defaultGlobalThemeInfo } from '../types/theme-types.js'; -describe('resetUserSpecificStateOnIdentityActions', () => { +describe('resetUserSpecificState', () => { let defaultState; let state; beforeAll(() => { @@ -106,17 +106,13 @@ ]; expect( - resetUserSpecificStateOnIdentityActions( - state, - defaultState, - nonUserSpecificFields, - ), + resetUserSpecificState(state, defaultState, nonUserSpecificFields), ).toEqual(state); }); it('resets fields not named in nonUserSpecificFields', () => { - expect( - resetUserSpecificStateOnIdentityActions(state, defaultState, []), - ).toEqual(defaultState); + expect(resetUserSpecificState(state, defaultState, [])).toEqual( + defaultState, + ); }); }); diff --git a/native/redux/client-db-utils.js b/native/redux/client-db-utils.js --- a/native/redux/client-db-utils.js +++ b/native/redux/client-db-utils.js @@ -32,6 +32,7 @@ function updateClientDBThreadStoreThreadInfos( state: AppState, migrationFunc: MixedRawThreadInfos => MixedRawThreadInfos, + handleMigrationFailure?: AppState => AppState, ): AppState { // Get threads from SQLite `threads` table. const clientDBThreadInfos = commCoreModule.getAllThreadsSync(); @@ -47,15 +48,10 @@ commCoreModule.processThreadStoreOperationsSync(operations); } catch (exception) { console.log(exception); - const keyserverInfos = { ...state.keyserverStore.keyserverInfos }; - for (const key in keyserverInfos) { - keyserverInfos[key] = { ...keyserverInfos[key], cookie: null }; + if (handleMigrationFailure) { + return handleMigrationFailure(state); } - const keyserverStore = { ...state.keyserverStore, keyserverInfos }; - return { - ...state, - keyserverStore, - }; + return ({ ...state, cookie: null }: any); } return state; diff --git a/native/redux/persist.js b/native/redux/persist.js --- a/native/redux/persist.js +++ b/native/redux/persist.js @@ -73,6 +73,7 @@ LegacyRawThreadInfo, MixedRawThreadInfos, } from 'lib/types/thread-types.js'; +import { wipeKeyserverStore } from 'lib/utils/keyserver-store-utils.js'; import { translateClientDBMessageInfoToRawMessageInfo, translateRawMessageInfoToClientDBMessageInfo, @@ -83,6 +84,7 @@ convertThreadStoreThreadInfosToNewIDSchema, } from 'lib/utils/migration-utils.js'; import { defaultNotifPermissionAlertInfo } from 'lib/utils/push-alerts.js'; +import { resetUserSpecificState } from 'lib/utils/reducers-utils.js'; import { deprecatedConvertClientDBThreadInfoToRawThreadInfo, convertRawThreadInfoToClientDBThreadInfo, @@ -102,12 +104,41 @@ import { persistMigrationForManagePinsThreadPermission } from './manage-pins-permission-migration.js'; import { persistMigrationToRemoveSelectRolePermissions } from './remove-select-role-permissions.js'; import type { AppState } from './state-types.js'; +import { nonUserSpecificFieldsNative } from './state-types.js'; import { unshimClientDB } from './unshim-utils.js'; import { commCoreModule } from '../native-modules.js'; import { defaultDeviceCameraInfo } from '../types/camera.js'; import { isTaskCancelledError } from '../utils/error-handling.js'; import { defaultURLPrefix } from '../utils/url-utils.js'; +const persistBlacklist = [ + 'loadingStatuses', + 'lifecycleState', + 'dimensions', + 'draftStore', + 'connectivity', + 'deviceOrientation', + 'frozen', + 'threadStore', + 'storeLoaded', + 'connection', +]; + +function handleReduxMigrationFailure(oldState: AppState): AppState { + const persistedNonUserSpecificFields = nonUserSpecificFieldsNative.filter( + field => !persistBlacklist.includes(field) || field === '_persist', + ); + const stateAfterReset = resetUserSpecificState( + oldState, + defaultState, + persistedNonUserSpecificFields, + ); + return { + ...stateAfterReset, + keyserverStore: wipeKeyserverStore(stateAfterReset.keyserverStore), + }; +} + const migrations = { [1]: (state: AppState) => ({ ...state, @@ -829,7 +860,7 @@ if (isTaskCancelledError(exception)) { return state; } - return { ...state, cookie: null }; + return handleReduxMigrationFailure(state); } const { inconsistencyReports, ...newUserStore } = state.userStore; @@ -934,7 +965,7 @@ if (isTaskCancelledError(exception)) { return state; } - return { ...state, cookie: null }; + return handleReduxMigrationFailure(state); } return state; }, @@ -977,7 +1008,7 @@ commCoreModule.processThreadStoreOperationsSync(operations); } catch (exception) { console.log(exception); - return { ...state, cookie: null }; + return handleReduxMigrationFailure(state); } return state; @@ -986,6 +1017,7 @@ updateClientDBThreadStoreThreadInfos( state, legacyUpdateRolesAndPermissions, + handleReduxMigrationFailure, ), [61]: (state: AppState) => { const minimallyEncodeThreadInfosFunc = ( @@ -1009,6 +1041,7 @@ return updateClientDBThreadStoreThreadInfos( state, minimallyEncodeThreadInfosFunc, + handleReduxMigrationFailure, ); }, }; @@ -1076,18 +1109,7 @@ const persistConfig = { key: 'root', storage: AsyncStorage, - blacklist: [ - 'loadingStatuses', - 'lifecycleState', - 'dimensions', - 'draftStore', - 'connectivity', - 'deviceOrientation', - 'frozen', - 'threadStore', - 'storeLoaded', - 'connection', - ], + blacklist: persistBlacklist, debug: __DEV__, version: 61, transforms: [ 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 @@ -30,7 +30,7 @@ import { rehydrateActionType } from 'lib/types/redux-types.js'; import type { SetSessionPayload } from 'lib/types/session-types.js'; import { reduxLoggerMiddleware } from 'lib/utils/action-logger.js'; -import { resetUserSpecificStateOnIdentityActions } from 'lib/utils/reducers-utils.js'; +import { resetUserSpecificState } from 'lib/utils/reducers-utils.js'; import { updateDimensionsActiveType, @@ -173,13 +173,13 @@ }; } - state = resetUserSpecificStateOnIdentityActions( + state = resetUserSpecificState( state, defaultState, nonUserSpecificFieldsNative, ); } else if (action.type === identityRegisterActionTypes.success) { - state = resetUserSpecificStateOnIdentityActions( + state = resetUserSpecificState( state, defaultState, nonUserSpecificFieldsNative, diff --git a/web/redux/persist.js b/web/redux/persist.js --- a/web/redux/persist.js +++ b/web/redux/persist.js @@ -16,20 +16,50 @@ import { defaultGlobalThemeInfo } from 'lib/types/theme-types.js'; import { parseCookies } from 'lib/utils/cookie-utils.js'; import { isDev } from 'lib/utils/dev-utils.js'; +import { wipeKeyserverStore } from 'lib/utils/keyserver-store-utils.js'; import { generateIDSchemaMigrationOpsForDrafts, convertDraftStoreToNewIDSchema, } from 'lib/utils/migration-utils.js'; +import { resetUserSpecificState } from 'lib/utils/reducers-utils.js'; import { ashoatKeyserverID } from 'lib/utils/validation-utils.js'; import commReduxStorageEngine from './comm-redux-storage-engine.js'; +import { defaultWebState } from './default-state.js'; import type { AppState } from './redux-setup.js'; +import { nonUserSpecificFieldsWeb } from './redux-setup.js'; import { getDatabaseModule } from '../database/database-module-provider.js'; import { isSQLiteSupported } from '../database/utils/db-utils.js'; import { workerRequestMessageTypes } from '../types/worker-types.js'; declare var keyserverURL: string; +const persistWhitelist = [ + 'enabledApps', + 'cryptoStore', + 'notifPermissionAlertInfo', + 'commServicesAccessToken', + 'keyserverStore', + 'globalThemeInfo', + 'customServer', +]; + +// eslint-disable-next-line no-unused-vars +function handleReduxMigrationFailure(oldState: AppState): AppState { + const persistedNonUserSpecificFields = nonUserSpecificFieldsWeb.filter( + field => persistWhitelist.includes(field) || field === '_persist', + ); + const stateAfterReset = resetUserSpecificState( + oldState, + defaultWebState, + persistedNonUserSpecificFields, + ); + return { + ...stateAfterReset, + keyserverStore: wipeKeyserverStore(stateAfterReset.keyserverStore), + }; +} + const migrations = { [1]: async (state: any) => { const { @@ -194,16 +224,6 @@ }, }; -const persistWhitelist = [ - 'enabledApps', - 'cryptoStore', - 'notifPermissionAlertInfo', - 'commServicesAccessToken', - 'keyserverStore', - 'globalThemeInfo', - 'customServer', -]; - const rootKey = 'root'; const migrateStorageToSQLite: StorageMigrationFunction = async debug => { 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 @@ -47,7 +47,7 @@ import type { ThreadStore } from 'lib/types/thread-types.js'; import type { CurrentUserInfo, UserStore } from 'lib/types/user-types.js'; import type { NotifPermissionAlertInfo } from 'lib/utils/push-alerts.js'; -import { resetUserSpecificStateOnIdentityActions } from 'lib/utils/reducers-utils.js'; +import { resetUserSpecificState } from 'lib/utils/reducers-utils.js'; import { updateWindowActiveActionType, @@ -282,13 +282,13 @@ }; } - state = resetUserSpecificStateOnIdentityActions( + state = resetUserSpecificState( state, defaultWebState, nonUserSpecificFieldsWeb, ); } else if (action.type === identityRegisterActionTypes.success) { - state = resetUserSpecificStateOnIdentityActions( + state = resetUserSpecificState( state, defaultWebState, nonUserSpecificFieldsWeb,