diff --git a/src/i18n/en-US.json b/src/i18n/en-US.json index 896c571388f..41edaf9d632 100644 --- a/src/i18n/en-US.json +++ b/src/i18n/en-US.json @@ -650,6 +650,7 @@ "dataSharingModalDecline": "Decline", "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in ourĀ [link]Privacy Policy[/link]. You can revoke your consent at any time.", "dataSharingModalTitle": "Consent to share user data", + "deletedUser": "Deleted User", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", diff --git a/src/script/user/UserRepository.ts b/src/script/user/UserRepository.ts index 0053823d01d..51d19deeb81 100644 --- a/src/script/user/UserRepository.ts +++ b/src/script/user/UserRepository.ts @@ -216,26 +216,36 @@ export class UserRepository extends TypedEventEmitter { // the entries we get back will be used to feed the availabilities of those users const nonQualifiedUsers = await this.userService.clearNonQualifiedUsers(); - const dbUsers = await this.userService.loadUserFromDb(); + const dbUsers = await this.userService.loadUsersFromDb(); // The self user doesn't need to be re-fetched const usersToFetch = users.filter(user => !matchQualifiedIds(selfUser.qualifiedId, user)); const {found, failed} = await this.fetchRawUsers(usersToFetch, selfUser.domain); - const userWithAvailability = found.map(user => { - const availability = [...dbUsers, ...nonQualifiedUsers].find(userRecord => userRecord.id === user.id); + const usersWithAvailability = found.map(user => { + const localUser = dbUsers.find( + dbUser => dbUser.id === user.id || matchQualifiedIds(dbUser.qualified_id, user.qualified_id), + ); + + const userWithAvailability = [...dbUsers, ...nonQualifiedUsers].find(userRecord => userRecord.id === user.id); + + const userWithEscapedDefaultName = this.replaceDeletedUserNameWithNameInDb(user, localUser); - if (availability) { - return {availability: availability.availability, ...user}; + if (userWithAvailability) { + return { + availability: userWithAvailability.availability, + ...userWithEscapedDefaultName, + }; } - return user; + + return userWithEscapedDefaultName; }); // Save all new users to the database - await Promise.all(userWithAvailability.map(user => this.saveUserInDb(user))); + await Promise.all(usersWithAvailability.map(user => this.saveUserInDb(user))); - const mappedUsers = this.mapUserResponse(userWithAvailability, failed, dbUsers); + const mappedUsers = this.mapUserResponse(usersWithAvailability, failed, dbUsers); // Assign connections to users mappedUsers.forEach(user => { @@ -323,6 +333,14 @@ export class UserRepository extends TypedEventEmitter { * Will update the user both in database and in memory. */ private async updateUser(userId: QualifiedId, user: Partial, isWebSocket = false): Promise { + if (user.deleted && user.name) { + const dbUser = await this.userService.loadUserFromDb(userId); + + if (dbUser && dbUser.name) { + user.name = dbUser?.name; + } + } + const selfUser = this.userState.self(); const isSelfUser = matchQualifiedIds(userId, selfUser.qualifiedId); const userEntity = isSelfUser ? selfUser : await this.getUserById(userId); @@ -575,23 +593,19 @@ export class UserRepository extends TypedEventEmitter { } // Replaces a deleted user name ("default") with the name from the local database. - private restoreDeletedUserNames(apiUsers: APIClientUser[], dbUsers: UserRecord[]): UserRecord[] { - return apiUsers.map(user => { - if (!user.deleted) { - return user; - } - - const localUser = dbUsers.find(dbUser => dbUser.id === user.id); + private replaceDeletedUserNameWithNameInDb(user: APIClientUser, localUser?: UserRecord): UserRecord { + if (!user.deleted) { + return user; + } - if (localUser && localUser.name) { - return { - ...user, - name: localUser.name, - }; - } + if (localUser && localUser.name) { + return { + ...user, + name: localUser.name, + }; + } - return user; - }); + return {...user, name: t('deletedUser')}; } private mapUserResponse(found: APIClientUser[], failed: QualifiedId[], dbUsers: UserRecord[]): User[] { @@ -615,9 +629,7 @@ export class UserRepository extends TypedEventEmitter { return new User(userId.id, userId.domain); }); - const users = this.restoreDeletedUserNames(found, dbUsers); - - const mappedUsers = this.userMapper.mapUsersFromJson(users, selfDomain).concat(failedToLoad); + const mappedUsers = this.userMapper.mapUsersFromJson(found, selfDomain).concat(failedToLoad); if (this.teamState.isTeam()) { this.mapGuestStatus(mappedUsers); @@ -633,7 +645,7 @@ export class UserRepository extends TypedEventEmitter { */ private async fetchUsers(userIds: QualifiedId[]): Promise { const {found, failed} = await this.fetchRawUsers(userIds, this.userState.self().domain); - const dbUsers = await this.userService.loadUserFromDb(); + const dbUsers = await this.userService.loadUsersFromDb(); const users = this.mapUserResponse(found, failed, dbUsers); let fetchedUserEntities = this.saveUsers(users); @@ -713,6 +725,7 @@ export class UserRepository extends TypedEventEmitter { if (localOnly) { const deletedUser = new User(userId.id, userId.domain); deletedUser.isDeleted = true; + deletedUser.name(t('deletedUser')); return deletedUser; } try { diff --git a/src/script/user/UserService.ts b/src/script/user/UserService.ts index 9d16b1350b0..812edb30aba 100644 --- a/src/script/user/UserService.ts +++ b/src/script/user/UserService.ts @@ -58,10 +58,20 @@ export class UserService { * @todo There might be more keys which are returned by this function * @returns Resolves with all the stored user states */ - loadUserFromDb(): Promise { + loadUsersFromDb(): Promise { return this.storageService.getAll(this.USER_STORE_NAME); } + /** + * Loads user from the local database + * @returns Resolves to the stored user, if it does not exists resolves to undefined + */ + loadUserFromDb(qualifiedId: QualifiedId): Promise { + const primaryKey = constructUserPrimaryKey(qualifiedId); + + return this.storageService.load(this.USER_STORE_NAME, primaryKey); + } + /** * Will remove all the non-qualified entries in the DB */ diff --git a/src/types/i18n.d.ts b/src/types/i18n.d.ts index 333c2c42d1c..e9a1f5280f7 100644 --- a/src/types/i18n.d.ts +++ b/src/types/i18n.d.ts @@ -443,11 +443,11 @@ declare module 'I18n/en-US.json' { 'conversationDetailsActionConversationParticipants': `Show all ({number})`; 'conversationDetailsActionCreateGroup': `Create group`; 'conversationDetailsActionDelete': `Delete group`; + 'conversationDetailsActionDeleteForMe': `Delete for Me`; 'conversationDetailsActionDevices': `Devices`; 'conversationDetailsActionGuestOptions': `Guests`; 'conversationDetailsActionLeave': `Leave group`; 'conversationDetailsActionNotifications': `Notifications`; - 'conversationDetailsActionDeleteForMe': `Delete for Me`; 'conversationDetailsActionServicesOptions': `Services`; 'conversationDetailsActionTimedMessages': `Self-deleting messages`; 'conversationDetailsActionUnblock': `Unblock`; @@ -654,6 +654,7 @@ declare module 'I18n/en-US.json' { 'dataSharingModalDecline': `Decline`; 'dataSharingModalDescription': `Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in ourĀ [link]Privacy Policy[/link]. You can revoke your consent at any time.`; 'dataSharingModalTitle': `Consent to share user data`; + 'deletedUser': `Deleted User`; 'downloadLatestMLS': `Download the latest MLS Wire version`; 'enumerationAnd': `, and `; 'ephemeralRemaining': `remaining`;