diff --git a/lib/shared/create-async-migrate.js b/lib/shared/create-async-migrate.js deleted file mode 100644 --- a/lib/shared/create-async-migrate.js +++ /dev/null @@ -1,127 +0,0 @@ -// @flow - -import { DEFAULT_VERSION } from 'redux-persist/es/constants.js'; - -import type { BaseNavInfo } from '../types/nav-types.js'; -import type { BaseAppState } from '../types/redux-types.js'; -import type { StoreOperations } from '../types/store-ops-types.js'; -import { syncedMetadataNames } from '../types/synced-metadata-types.js'; -import { getConfig } from '../utils/config.js'; - -type LegacyMigrationManifest> = { - +[number | string]: (T) => Promise, -}; -type PersistedState> = T | void; -type ConfigType = { - +debug: boolean, -}; - -export type StorageMigrationFunction> = ( - debug: boolean, -) => Promise>; - -type MigrationManifest> = { - +[number | string]: (PersistedState) => Promise<{ - +state: T, - +ops: StoreOperations, - }>, -}; - -function createAsyncMigrate>( - legacyMigrations: LegacyMigrationManifest, - config: ConfigType, - migrations: MigrationManifest, - handleException: (error: Error, state: T) => T, - storageMigration: ?StorageMigrationFunction, -): ( - state: PersistedState, - currentVersion: number, -) => Promise> { - const debug = process.env.NODE_ENV !== 'production' && !!config?.debug; - return async function ( - state: ?PersistedState, - currentVersion: number, - ): Promise> { - if (!state && storageMigration) { - state = await storageMigration(debug); - } - if (!state) { - if (debug) { - console.log('redux-persist: no inbound state, skipping migration'); - } - return undefined; - } - - const inboundVersion: number = state?._persist?.version ?? DEFAULT_VERSION; - - if (inboundVersion === currentVersion) { - if (debug) { - console.log('redux-persist: versions match, noop migration'); - } - return state; - } - if (inboundVersion > currentVersion) { - if (debug) { - console.error('redux-persist: downgrading version is not supported'); - } - return state; - } - - const migrationKeys = [ - ...Object.keys(legacyMigrations), - ...Object.keys(migrations), - ] - .map(ver => parseInt(ver)) - .filter(key => currentVersion >= key && key > inboundVersion); - const sortedMigrationKeys = migrationKeys.sort((a, b) => a - b); - - if (debug) { - console.log('redux-persist: migrationKeys', sortedMigrationKeys); - } - - let migratedState = state; - for (const versionKey of sortedMigrationKeys) { - if (debug) { - console.log( - 'redux-persist: running migration for versionKey', - versionKey, - ); - } - - if (!versionKey) { - continue; - } - - if (legacyMigrations[versionKey]) { - migratedState = await legacyMigrations[versionKey](migratedState); - } else { - const { state: newState, ops } = - await migrations[versionKey](migratedState); - migratedState = newState; - const versionUpdateOp = { - type: 'replace_synced_metadata_entry', - payload: { - name: syncedMetadataNames.DB_VERSION, - data: versionKey.toString(), - }, - }; - const dbOps = { - ...ops, - syncedMetadataStoreOperations: [ - ...(ops.syncedMetadataStoreOperations ?? []), - versionUpdateOp, - ], - }; - try { - await getConfig().sqliteAPI.processDBStoreOperations(dbOps); - } catch (exception) { - return handleException(exception, state); - } - } - } - - return migratedState; - }; -} - -export { createAsyncMigrate }; diff --git a/lib/utils/migration-utils.js b/lib/utils/migration-utils.js --- a/lib/utils/migration-utils.js +++ b/lib/utils/migration-utils.js @@ -3,6 +3,7 @@ import invariant from 'invariant'; import { authoritativeKeyserverID } from './authoritative-keyserver.js'; +import { getConfig } from './config.js'; import type { TranslatedThreadMessageInfos } from './message-ops-utils.js'; import { entries } from './objects.js'; import { @@ -20,6 +21,10 @@ DraftStore, } from '../types/draft-types'; import type { RawMessageInfo } from '../types/message-types.js'; +import type { BaseNavInfo } from '../types/nav-types.js'; +import type { BaseAppState } from '../types/redux-types.js'; +import type { StoreOperations } from '../types/store-ops-types.js'; +import { syncedMetadataNames } from '../types/synced-metadata-types.js'; import { threadPermissions, threadPermissionPropagationPrefixes, @@ -187,6 +192,122 @@ threadPermissions.MANAGE_INVITE_LINKS, ]; +type LegacyMigrationManifest> = { + +[number | string]: (T) => Promise, +}; +type PersistedState> = T | void; +type ConfigType = { + +debug: boolean, +}; + +export type StorageMigrationFunction> = ( + debug: boolean, +) => Promise>; + +type MigrationManifest> = { + +[number | string]: (PersistedState) => Promise<{ + +state: T, + +ops: StoreOperations, + }>, +}; + +function createAsyncMigrate>( + legacyMigrations: LegacyMigrationManifest, + config: ConfigType, + migrations: MigrationManifest, + handleException: (error: Error, state: T) => T, + storageMigration: ?StorageMigrationFunction, +): ( + state: PersistedState, + currentVersion: number, +) => Promise> { + const debug = process.env.NODE_ENV !== 'production' && !!config?.debug; + return async function ( + state: ?PersistedState, + currentVersion: number, + ): Promise> { + if (!state && storageMigration) { + state = await storageMigration(debug); + } + if (!state) { + if (debug) { + console.log('redux-persist: no inbound state, skipping migration'); + } + return undefined; + } + + const inboundVersion: number = state?._persist?.version ?? -1; + + if (inboundVersion === currentVersion) { + if (debug) { + console.log('redux-persist: versions match, noop migration'); + } + return state; + } + if (inboundVersion > currentVersion) { + if (debug) { + console.error('redux-persist: downgrading version is not supported'); + } + return state; + } + + const migrationKeys = [ + ...Object.keys(legacyMigrations), + ...Object.keys(migrations), + ] + .map(ver => parseInt(ver)) + .filter(key => currentVersion >= key && key > inboundVersion); + const sortedMigrationKeys = migrationKeys.sort((a, b) => a - b); + + if (debug) { + console.log('redux-persist: migrationKeys', sortedMigrationKeys); + } + + let migratedState = state; + for (const versionKey of sortedMigrationKeys) { + if (debug) { + console.log( + 'redux-persist: running migration for versionKey', + versionKey, + ); + } + + if (!versionKey) { + continue; + } + + if (legacyMigrations[versionKey]) { + migratedState = await legacyMigrations[versionKey](migratedState); + } else { + const { state: newState, ops } = + await migrations[versionKey](migratedState); + migratedState = newState; + const versionUpdateOp = { + type: 'replace_synced_metadata_entry', + payload: { + name: syncedMetadataNames.DB_VERSION, + data: versionKey.toString(), + }, + }; + const dbOps = { + ...ops, + syncedMetadataStoreOperations: [ + ...(ops.syncedMetadataStoreOperations ?? []), + versionUpdateOp, + ], + }; + try { + await getConfig().sqliteAPI.processDBStoreOperations(dbOps); + } catch (exception) { + return handleException(exception, state); + } + } + } + + return migratedState; + }; +} + export { convertDraftKeyToNewIDSchema, convertDraftStoreToNewIDSchema, @@ -197,4 +318,5 @@ convertIDToNewSchema, convertNotificationMessageInfoToNewIDSchema, permissionsToRemoveInMigration, + createAsyncMigrate, }; diff --git a/native/redux/persist.js b/native/redux/persist.js --- a/native/redux/persist.js +++ b/native/redux/persist.js @@ -57,7 +57,6 @@ import { patchRawThreadInfosWithSpecialRole } from 'lib/permissions/special-roles.js'; import { filterThreadIDsInFilterList } from 'lib/reducers/calendar-filters-reducer.js'; import { highestLocalIDSelector } from 'lib/selectors/local-id-selectors.js'; -import { createAsyncMigrate } from 'lib/shared/create-async-migrate.js'; import { inconsistencyResponsesToReports } from 'lib/shared/report-utils.js'; import { getContainingThreadID, @@ -108,6 +107,7 @@ generateIDSchemaMigrationOpsForDrafts, convertMessageStoreThreadsToNewIDSchema, convertThreadStoreThreadInfosToNewIDSchema, + createAsyncMigrate, } from 'lib/utils/migration-utils.js'; import { entries } from 'lib/utils/objects.js'; import { diff --git a/web/redux/persist.js b/web/redux/persist.js --- a/web/redux/persist.js +++ b/web/redux/persist.js @@ -13,10 +13,6 @@ import type { ClientDBMessageStoreOperation } from 'lib/ops/message-store-ops.js'; import type { ClientDBThreadStoreOperation } from 'lib/ops/thread-store-ops.js'; import { patchRawThreadInfoWithSpecialRole } from 'lib/permissions/special-roles.js'; -import { - createAsyncMigrate, - type StorageMigrationFunction, -} from 'lib/shared/create-async-migrate.js'; import { keyserverStoreTransform } from 'lib/shared/transforms/keyserver-store-transform.js'; import { messageStoreMessagesBlocklistTransform } from 'lib/shared/transforms/message-store-transform.js'; import { defaultAlertInfos } from 'lib/types/alert-types.js'; @@ -35,6 +31,8 @@ import { generateIDSchemaMigrationOpsForDrafts, convertDraftStoreToNewIDSchema, + createAsyncMigrate, + type StorageMigrationFunction, } from 'lib/utils/migration-utils.js'; import { entries } from 'lib/utils/objects.js'; import {