Skip to content

Commit 65a79d0

Browse files
authored
fix: indicate when a 1:1 conversation involves a deleted user [WPB-18869] (#19295)
* fix: lock 1:1 conversations with deleted users * disable 1:1 conversation with deleted user * add deleted label to deleted user * add messageHeader style file * add localisation key * Update src/script/components/MessagesList/Message/ContentMessage/MessageHeader.styles.ts
1 parent 7932f26 commit 65a79d0

File tree

6 files changed

+89
-33
lines changed

6 files changed

+89
-33
lines changed

src/i18n/en-US.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,7 @@
689689
"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. <br /> Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.",
690690
"dataSharingModalTitle": "Consent to share user data",
691691
"deletedUser": "Deleted User",
692+
"deletedUserBadge": "Deleted",
692693
"downloadLatestMLS": "Download the latest MLS Wire version",
693694
"enumerationAnd": ", and ",
694695
"ephemeralRemaining": "remaining",
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Wire
3+
* Copyright (C) 2025 Wire Swiss GmbH
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see http://www.gnu.org/licenses/.
17+
*
18+
*/
19+
20+
import {CSSObject} from '@emotion/react';
21+
22+
export const headerIconBadge: CSSObject = {
23+
display: 'inline-flex',
24+
marginTop: '-2px',
25+
marginLeft: '8px',
26+
svg: {
27+
fill: 'var(--background-fade-40)',
28+
},
29+
};
30+
31+
export const headerLabelBadge: CSSObject = {
32+
fontSize: 'var(--font-size-small)',
33+
fontWeight: 'var(--font-weight-regular)',
34+
color: 'var(--text-input-placeholder)',
35+
marginLeft: '4px',
36+
};
37+
38+
export const headerIconSizeS: CSSObject = {
39+
width: '14px',
40+
height: '14px',
41+
};
42+
43+
export const headerIconSizeM: CSSObject = {
44+
width: '16px',
45+
height: '16px',
46+
};

src/script/components/MessagesList/Message/ContentMessage/MessageHeader.tsx

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import {ServiceEntity} from 'src/script/integration/ServiceEntity';
3030
import {useKoSubscribableChildren} from 'Util/ComponentUtil';
3131
import {t} from 'Util/LocalizerUtil';
3232

33+
import {headerIconBadge, headerLabelBadge, headerIconSizeM, headerIconSizeS} from './MessageHeader.styles';
34+
3335
type MessageHeaderParams = {
3436
message: ContentMessage | DeleteMessage;
3537
onClickAvatar: (user: User | ServiceEntity) => void;
@@ -46,30 +48,47 @@ function BadgeSection({sender}: {sender: User}) {
4648
return (
4749
<>
4850
{sender.isService && (
49-
<span className="message-header-icon-service">
50-
<Icon.ServiceIcon />
51+
<span className="message-header-icon-service" css={headerIconBadge}>
52+
<Icon.ServiceIcon css={headerIconSizeS} />
5153
</span>
5254
)}
5355

5456
{sender.isExternal() && (
55-
<Tooltip body={t('rolePartner')} data-uie-name="sender-external" className="message-header-icon-external">
56-
<Icon.ExternalIcon />
57+
<Tooltip
58+
body={t('rolePartner')}
59+
data-uie-name="sender-external"
60+
className="message-header-icon-external"
61+
css={headerIconBadge}
62+
>
63+
<Icon.ExternalIcon css={headerIconSizeM} />
5764
</Tooltip>
5865
)}
5966

6067
{sender.isFederated && (
61-
<Tooltip className="message-header-icon-guest" body={sender.handle} data-uie-name="sender-federated">
62-
<Icon.FederationIcon />
68+
<Tooltip
69+
className="message-header-icon-guest"
70+
body={sender.handle}
71+
data-uie-name="sender-federated"
72+
css={headerIconBadge}
73+
>
74+
<Icon.FederationIcon css={headerIconSizeM} />
6375
</Tooltip>
6476
)}
6577

78+
{sender.isDeleted && (
79+
<p data-uie-name="sender-deleted" css={headerLabelBadge}>
80+
{t('deletedUserBadge')}
81+
</p>
82+
)}
83+
6684
{sender.isDirectGuest() && !sender.isFederated && (
6785
<Tooltip
6886
className="message-header-icon-guest"
6987
body={t('conversationGuestIndicator')}
7088
data-uie-name="sender-guest"
89+
css={headerIconBadge}
7190
>
72-
<Icon.GuestIcon />
91+
<Icon.GuestIcon css={headerIconSizeS} />
7392
</Tooltip>
7493
)}
7594
</>

src/script/entity/Conversation.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ export class Conversation {
9090
private readonly incomingMessages: ko.ObservableArray<Message>;
9191
public readonly isProteusTeam1to1: ko.PureComputed<boolean>;
9292
public readonly isConversationWithBlockedUser: ko.PureComputed<boolean>;
93+
public readonly isConversationWithDeletedUser: ko.PureComputed<boolean>;
94+
public readonly is1to1ConversationWithDeletedUser: ko.PureComputed<boolean>;
9395
public readonly isReadOnlyConversation: ko.PureComputed<boolean>;
9496
public readonly last_server_timestamp: ko.Observable<number>;
9597
private readonly logger: Logger;
@@ -250,8 +252,20 @@ export class Conversation {
250252

251253
this.isConversationWithBlockedUser = ko.pureComputed(() => !!this.connection()?.isBlocked());
252254

255+
this.isConversationWithDeletedUser = ko.pureComputed(() => {
256+
const hasDeletedUser = this.participating_user_ets().some(userEntity => userEntity.isDeleted);
257+
return hasDeletedUser;
258+
});
259+
260+
this.is1to1ConversationWithDeletedUser = ko.pureComputed(
261+
() => !!this.is1to1() && this.isConversationWithDeletedUser(),
262+
);
263+
253264
this.isReadOnlyConversation = ko.pureComputed(
254-
() => this.isConversationWithBlockedUser() || this.readOnlyState() !== null,
265+
() =>
266+
this.isConversationWithBlockedUser() ||
267+
this.is1to1ConversationWithDeletedUser() ||
268+
this.readOnlyState() !== null,
255269
);
256270

257271
this.isGroup = ko.pureComputed(() => {

src/style/content/conversation/message-list.less

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -265,29 +265,6 @@
265265
font-size: var(--font-size-xxs);
266266
}
267267

268-
.message-header-icon-guest,
269-
.message-header-icon-service,
270-
.message-header-icon-external {
271-
display: inline-flex;
272-
margin-top: -2px;
273-
margin-left: 8px;
274-
svg path {
275-
fill: var(--background-fade-40);
276-
}
277-
}
278-
.message-header-icon-guest,
279-
.message-header-icon-service {
280-
svg {
281-
width: 14px;
282-
height: 14px;
283-
}
284-
}
285-
286-
.message-header-icon-external svg {
287-
width: 16px;
288-
height: 16px;
289-
}
290-
291268
.message-mention {
292269
outline-offset: 0.4rem;
293270
}

src/types/i18n.d.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -507,8 +507,6 @@ declare module 'I18n/en-US.json' {
507507
'conversationFileUploadFailedTooLargeFilesHeading': `Files too large`;
508508
'conversationFileUploadFailedTooLargeFilesMessage': `Please select files smaller than {maxSize}MB.`;
509509
'conversationFileUploadFailedTooLargeImagesMessage': `Please select images smaller than {maxSize}MB.`;
510-
'conversationFileUploadFailedTooLargeFilesAndImagesMessage': `Please select files smaller than {maxImageSize}MB and images smaller than {maxFileSize}MB.`;
511-
'conversationFileUploadOverlayTitle': `Upload files`;
512510
'conversationFilePreviewErrorMoreOptions': `More options`;
513511
'conversationFilePreviewErrorRetry': `Retry`;
514512
'conversationFilePreviewErrorRemove': `Remove`;
@@ -695,6 +693,7 @@ declare module 'I18n/en-US.json' {
695693
'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. <br /> Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.`;
696694
'dataSharingModalTitle': `Consent to share user data`;
697695
'deletedUser': `Deleted User`;
696+
'deletedUserBadge': `Deleted`;
698697
'downloadLatestMLS': `Download the latest MLS Wire version`;
699698
'enumerationAnd': `, and `;
700699
'ephemeralRemaining': `remaining`;

0 commit comments

Comments
 (0)