diff --git a/lib/shared/dm-ops/dm-op-utils.js b/lib/shared/dm-ops/dm-op-utils.js --- a/lib/shared/dm-ops/dm-op-utils.js +++ b/lib/shared/dm-ops/dm-op-utils.js @@ -8,7 +8,10 @@ import { type ProcessDMOperationUtilities } from './dm-op-spec.js'; import { dmOpSpecs } from './dm-op-specs.js'; import { useProcessAndSendDMOperation } from './process-dm-ops.js'; -import { setMissingDeviceListsActionType } from '../../actions/aux-user-actions.js'; +import { + setMissingDeviceListsActionType, + removePeerUsersActionType, +} from '../../actions/aux-user-actions.js'; import { useFindUserIdentities } from '../../actions/find-user-identities-actions.js'; import { useLoggedInUserInfo } from '../../hooks/account-hooks.js'; import { useGetLatestMessageEdit } from '../../hooks/latest-message-edit.js'; @@ -43,11 +46,15 @@ userActionsP2PMessageTypes, } from '../../types/tunnelbroker/user-actions-peer-to-peer-message-types.js'; import { updateTypes } from '../../types/update-types-enum.js'; -import type { ClientUpdateInfo } from '../../types/update-types.js'; +import type { + AccountDeletionUpdateInfo, + ClientUpdateInfo, +} from '../../types/update-types.js'; import { getContentSigningKey } from '../../utils/crypto-utils.js'; import { useSelector, useDispatch } from '../../utils/redux-utils.js'; import { messageSpecs } from '../messages/message-specs.js'; import { + expectedAccountDeletionUpdateTimeout, userHasDeviceList, deviceListCanBeRequestedForUser, } from '../thread-utils.js'; @@ -167,18 +174,42 @@ const missingUsers: $ReadOnlyArray = missingDeviceListsUserIDs.filter(id => !deviceLists[id]); - if (missingUsers.length > 0) { + const time = Date.now(); + const shouldDeleteUser = (userID: string) => + !!auxUserInfos[userID]?.accountMissingStatus && + auxUserInfos[userID].accountMissingStatus.missingSince < + time - expectedAccountDeletionUpdateTimeout; + + const nonDeletedUsers = missingUsers.filter( + userID => !shouldDeleteUser(userID), + ); + if (nonDeletedUsers.length > 0) { dispatch({ type: setMissingDeviceListsActionType, payload: { usersMissingFromIdentity: { - userIDs: missingUsers, - time: Date.now(), + userIDs: nonDeletedUsers, + time, }, }, }); } + const deletedUsers = missingUsers.filter(shouldDeleteUser); + if (deletedUsers.length > 0) { + const deleteUserUpdates: $ReadOnlyArray = + deletedUsers.map(deletedUserID => ({ + type: updateTypes.DELETE_ACCOUNT, + time, + id: uuid.v4(), + deletedUserID, + })); + dispatch({ + type: removePeerUsersActionType, + payload: { updatesResult: { newUpdates: deleteUserUpdates } }, + }); + } + const updatedPeers: Array = []; for (const userID of missingDeviceListsUserIDs) { if (deviceLists[userID] && deviceLists[userID].devices.length > 0) { @@ -192,7 +223,12 @@ } return updatedPeers; }, - [dispatch, getAndUpdateDeviceListsForUsers, getUsersWithoutDeviceList], + [ + auxUserInfos, + dispatch, + getAndUpdateDeviceListsForUsers, + getUsersWithoutDeviceList, + ], ); return React.useCallback( diff --git a/lib/shared/thread-utils.js b/lib/shared/thread-utils.js --- a/lib/shared/thread-utils.js +++ b/lib/shared/thread-utils.js @@ -1833,6 +1833,7 @@ } const deviceListRequestTimeout = 20 * 1000; // twenty seconds +const expectedAccountDeletionUpdateTimeout = 24 * 60 * 60 * 1000; // one day function deviceListCanBeRequestedForUser( userID: string, @@ -1915,4 +1916,5 @@ userHasDeviceList, deviceListIsNonEmpty, deviceListCanBeRequestedForUser, + expectedAccountDeletionUpdateTimeout, };