diff --git a/lib/permissions/special-roles.js b/lib/permissions/special-roles.js --- a/lib/permissions/special-roles.js +++ b/lib/permissions/special-roles.js @@ -65,4 +65,8 @@ return _mapValues(patchRawThreadInfoWithSpecialRole)(rawThreadInfos); } -export { patchRoleInfoWithSpecialRole, patchRawThreadInfosWithSpecialRole }; +export { + patchRoleInfoWithSpecialRole, + patchRawThreadInfoWithSpecialRole, + patchRawThreadInfosWithSpecialRole, +}; diff --git a/web/redux/persist.js b/web/redux/persist.js --- a/web/redux/persist.js +++ b/web/redux/persist.js @@ -10,6 +10,8 @@ keyserverStoreOpsHandlers, type ReplaceKeyserverOperation, } from 'lib/ops/keyserver-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, @@ -19,6 +21,7 @@ import { cookieTypes } from 'lib/types/session-types.js'; import { defaultConnectionInfo } from 'lib/types/socket-types.js'; import { defaultGlobalThemeInfo } from 'lib/types/theme-types.js'; +import type { ClientDBThreadInfo } from 'lib/types/thread-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'; @@ -28,6 +31,10 @@ } from 'lib/utils/migration-utils.js'; import { entries } from 'lib/utils/objects.js'; import { resetUserSpecificState } from 'lib/utils/reducers-utils.js'; +import { + convertClientDBThreadInfoToRawThreadInfo, + convertRawThreadInfoToClientDBThreadInfo, +} from 'lib/utils/thread-ops-utils.js'; import commReduxStorageEngine from './comm-redux-storage-engine.js'; import { defaultWebState } from './default-state.js'; @@ -262,6 +269,52 @@ return handleReduxMigrationFailure(state); } }, + [12]: async (state: AppState) => { + // 1. Check if `databaseModule` is supported and early-exit if not. + const databaseModule = await getDatabaseModule(); + const isDatabaseSupported = await databaseModule.isDatabaseSupported(); + + if (!isDatabaseSupported) { + return state; + } + + // 2. Get existing `stores` from SQLite. + const stores = await databaseModule.schedule({ + type: workerRequestMessageTypes.GET_CLIENT_STORE, + }); + invariant(stores?.store, 'Stores should exist'); + const threads: $ReadOnlyArray = stores.store.threads; + + if (threads.length === 0) { + return state; + } + + // 3. Convert to `RawThreadInfo`, patch in `specialRole`, and convert back. + const patchedClientDBThreadInfos: $ReadOnlyArray = + threads + .map(convertClientDBThreadInfoToRawThreadInfo) + .map(patchRawThreadInfoWithSpecialRole) + .map(convertRawThreadInfoToClientDBThreadInfo); + + // 4. Construct operations to remove existing threads and replace them + // with threads that have the `specialRole` field patched in. + const threadStoreOperations: ClientDBThreadStoreOperation[] = []; + threadStoreOperations.push({ type: 'remove_all' }); + for (const clientDBThreadInfo: ClientDBThreadInfo of patchedClientDBThreadInfos) { + threadStoreOperations.push({ + type: 'replace', + payload: clientDBThreadInfo, + }); + } + + // 5. Process the constructed `threadStoreOperations`. + await databaseModule.schedule({ + type: workerRequestMessageTypes.PROCESS_STORE_OPERATIONS, + storeOperations: { threadStoreOperations }, + }); + + return state; + }, }; const migrateStorageToSQLite: StorageMigrationFunction = async debug => {