diff --git a/lib/backup/use-user-data-utils.js b/lib/backup/use-user-data-utils.js --- a/lib/backup/use-user-data-utils.js +++ b/lib/backup/use-user-data-utils.js @@ -4,9 +4,13 @@ type AddLogCallback, logTypes, } from '../components/debug-logs-context.js'; +import type { AuxUserStoreOperation } from '../ops/aux-user-store-ops.js'; +import type { AuxUserInfo } from '../types/aux-user-types.js'; import { databaseIdentifier } from '../types/database-identifier-types.js'; +import type { IdentityServiceClient } from '../types/identity-service-types.js'; import { syncedMetadataNames } from '../types/synced-metadata-types.js'; import { getConfig } from '../utils/config.js'; +import { rawDeviceListFromSignedList } from '../utils/device-list-utils.js'; export type StoreAndDatabaseVersions = { +mainDatabaseVersion: number, @@ -58,4 +62,48 @@ }; } -export { getStoreAndDatabaseVersions }; +async function generateAuxUserOpsToUpdatePeers( + peerUserIDs: $ReadOnlyArray, + identityClient: IdentityServiceClient, +): Promise<$ReadOnlyArray> { + const [newAuxUserInfos, userIdentities] = await Promise.all([ + identityClient.getDeviceListsForUsers(peerUserIDs), + identityClient.findUserIdentities(peerUserIDs), + ]); + + const userIDsToRemove: Array = []; + const auxUserStoreOperations: Array = []; + for (const userID of peerUserIDs) { + const userIdentity = userIdentities.identities[userID]; + const deviceList = newAuxUserInfos.usersSignedDeviceLists[userID]; + const platformDetails = + newAuxUserInfos.usersDevicesPlatformDetails[userID] ?? {}; + + // when peer doesn't exist on Identity, we should remove it + const userExists = !!deviceList && !!userIdentity; + if (!userExists) { + userIDsToRemove.push(userID); + continue; + } + + const auxUserInfo: AuxUserInfo = { + deviceList: rawDeviceListFromSignedList(deviceList), + devicesPlatformDetails: platformDetails, + fid: userIdentity?.farcasterID, + }; + auxUserStoreOperations.push({ + type: 'replace_aux_user_info', + payload: { id: userID, auxUserInfo }, + }); + } + + if (userIDsToRemove.length > 0) { + auxUserStoreOperations.push({ + type: 'remove_aux_user_infos', + payload: { ids: userIDsToRemove }, + }); + } + return auxUserStoreOperations; +} + +export { getStoreAndDatabaseVersions, generateAuxUserOpsToUpdatePeers }; diff --git a/lib/backup/user-data-restore-context.js b/lib/backup/user-data-restore-context.js --- a/lib/backup/user-data-restore-context.js +++ b/lib/backup/user-data-restore-context.js @@ -6,12 +6,14 @@ import { runRestoredBackupMigrations } from './restored-migrations.js'; import { getStoreAndDatabaseVersions, + generateAuxUserOpsToUpdatePeers, type StoreAndDatabaseVersions, } from './use-user-data-utils.js'; import { restoreUserDataStepActionTypes } from '../actions/backup-actions.js'; import { setClientDBStoreActionType } from '../actions/client-db-store-actions.js'; import { generateOpsToEstablishHoldersOnDevice } from '../actions/holder-actions.js'; import { logTypes, useDebugLogs } from '../components/debug-logs-context.js'; +import { IdentityClientContext } from '../shared/identity-client-context.js'; import { restoreUserDataStepsOrder } from '../types/backup-types.js'; import { databaseIdentifier } from '../types/database-identifier-types.js'; import type { QRAuthBackupData } from '../types/tunnelbroker/qr-code-auth-message-types.js'; @@ -44,6 +46,8 @@ const dispatchActionPromise = useDispatchActionPromise(); const { addLog } = useDebugLogs(); const restoreBackupState = useSelector(state => state.restoreBackupState); + const identityContext = React.useContext(IdentityClientContext); + invariant(identityContext, 'Identity client context not set'); const executeRestoreUserData = React.useCallback( async ( @@ -75,7 +79,7 @@ } // if all steps from last restore succeeded, start from scratch - if (startStepIndex > 5) { + if (startStepIndex > restoreUserDataStepsOrder.length - 1) { startStepIndex = 0; } @@ -244,8 +248,32 @@ await removeLocalMessageInfosPromise; } - // 6. Copy content to main database + // 6. Update peer device lists if (startStepIndex <= 5) { + const updatePeersPromise = (async () => { + const peerUserIDs = await sqliteAPI.getAuxUserIDs( + databaseIdentifier.RESTORED, + ); + const auxUserStoreOperations = await generateAuxUserOpsToUpdatePeers( + peerUserIDs, + identityContext.identityClient, + ); + await sqliteAPI.processDBStoreOperations( + { auxUserStoreOperations }, + databaseIdentifier.RESTORED, + ); + })(); + void dispatchActionPromise( + restoreUserDataStepActionTypes, + updatePeersPromise, + undefined, + { step: 'update_peers' }, + ); + await updatePeersPromise; + } + + // 7. Copy content to main database + if (startStepIndex <= 6) { const copyContentPromise = sqliteAPI.copyContentFromBackupDatabase(); void dispatchActionPromise( restoreUserDataStepActionTypes, @@ -256,7 +284,7 @@ await copyContentPromise; } - // 7. Populate store + // 8. Populate store const clientDBStore = await sqliteAPI.getClientDBStore( databaseIdentifier.MAIN, userID, @@ -267,7 +295,13 @@ payload: clientDBStore, }); }, - [addLog, dispatch, dispatchActionPromise, restoreBackupState], + [ + addLog, + dispatch, + dispatchActionPromise, + identityContext, + restoreBackupState, + ], ); const userDataRestore = React.useCallback( diff --git a/lib/types/backup-types.js b/lib/types/backup-types.js --- a/lib/types/backup-types.js +++ b/lib/types/backup-types.js @@ -89,6 +89,7 @@ RUN_RESTORED_BACKUP_MIGRATIONS: 'run_restored_backup_migrations', ASSIGN_NEW_HOLDERS: 'assign_new_holders', REMOVE_LOCAL_MESSAGE_INFOS: 'remove_local_message_infos', + UPDATE_PEERS: 'update_peers', COPY_CONTENT_FROM_BACKUP_DB: 'copy_content_from_backup_db', }); export type RestoreUserDataStep = $Values; @@ -99,6 +100,7 @@ restoreUserDataSteps.RUN_RESTORED_BACKUP_MIGRATIONS, restoreUserDataSteps.ASSIGN_NEW_HOLDERS, restoreUserDataSteps.REMOVE_LOCAL_MESSAGE_INFOS, + restoreUserDataSteps.UPDATE_PEERS, restoreUserDataSteps.COPY_CONTENT_FROM_BACKUP_DB, ];