diff --git a/lib/backup/use-user-data-restore.js b/lib/backup/use-user-data-restore.js --- a/lib/backup/use-user-data-restore.js +++ b/lib/backup/use-user-data-restore.js @@ -8,6 +8,9 @@ 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 type { AuxUserStoreOperation } from '../ops/aux-user-store-ops.js'; +import { IdentityClientContext } from '../shared/identity-client-context.js'; +import type { AuxUserInfo } from '../types/aux-user-types.js'; import { restoreUserDataSteps, type RestoreUserDataStep, @@ -17,6 +20,7 @@ import { syncedMetadataNames } from '../types/synced-metadata-types.js'; import type { QRAuthBackupData } from '../types/tunnelbroker/qr-code-auth-message-types.js'; import { getConfig } from '../utils/config.js'; +import { rawDeviceListFromSignedList } from '../utils/device-list-utils.js'; import { useDispatchActionPromise } from '../utils/redux-promise-utils.js'; import { useSelector, useDispatch } from '../utils/redux-utils.js'; @@ -28,6 +32,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'); return React.useCallback( async ( @@ -42,6 +48,7 @@ restoreUserDataSteps.MIGRATE_BACKUP_SCHEMA, restoreUserDataSteps.RUN_RESTORED_BACKUP_MIGRATIONS, restoreUserDataSteps.ASSIGN_NEW_HOLDERS, + restoreUserDataSteps.UPDATE_PEERS, restoreUserDataSteps.COPY_CONTENT_FROM_BACKUP_DB, ]; let startStepIndex = 0; @@ -207,8 +214,67 @@ await assignHoldersPromise; } - // 5. Copy content to main database + // 5. Update peer device lists if (startStepIndex <= 4) { + const updatePeersPromise = (async () => { + const peerUserIDs = await sqliteAPI.getAuxUserIDs( + databaseIdentifier.RESTORED, + ); + const { identityClient } = identityContext; + 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 }, + }); + } + + await sqliteAPI.processDBStoreOperations( + { auxUserStoreOperations }, + databaseIdentifier.RESTORED, + ); + })(); + void dispatchActionPromise( + restoreUserDataStepActionTypes, + updatePeersPromise, + undefined, + { step: 'update_peers' }, + ); + await updatePeersPromise; + } + + // 6. Copy content to main database + if (startStepIndex <= 5) { const copyContentPromise = sqliteAPI.copyContentFromBackupDatabase(); void dispatchActionPromise( restoreUserDataStepActionTypes, @@ -219,7 +285,7 @@ await copyContentPromise; } - // 6. Populate store + // 7. Populate store const clientDBStore = await sqliteAPI.getClientDBStore( databaseIdentifier.MAIN, identityAuthResult.userID, @@ -230,7 +296,13 @@ payload: clientDBStore, }); }, - [addLog, dispatch, dispatchActionPromise, restoreBackupState], + [ + addLog, + dispatch, + dispatchActionPromise, + identityContext, + restoreBackupState, + ], ); } diff --git a/lib/reducers/backup-reducer.js b/lib/reducers/backup-reducer.js --- a/lib/reducers/backup-reducer.js +++ b/lib/reducers/backup-reducer.js @@ -163,6 +163,7 @@ restoreUserDataSteps.MIGRATE_BACKUP_SCHEMA, restoreUserDataSteps.RUN_RESTORED_BACKUP_MIGRATIONS, restoreUserDataSteps.ASSIGN_NEW_HOLDERS, + restoreUserDataSteps.UPDATE_PEERS, restoreUserDataSteps.COPY_CONTENT_FROM_BACKUP_DB, ]; function validateUserDataRestoreStepOrder( 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 @@ -87,6 +87,7 @@ MIGRATE_BACKUP_SCHEMA: 'migrate_backup_schema', RUN_RESTORED_BACKUP_MIGRATIONS: 'run_restored_backup_migrations', ASSIGN_NEW_HOLDERS: 'assign_new_holders', + UPDATE_PEERS: 'update_peers', COPY_CONTENT_FROM_BACKUP_DB: 'copy_content_from_backup_db', }); export type RestoreUserDataStep = $Values;