Skip to content

Commit 29dde79

Browse files
authored
feat: Improvements for Delivered state and add tests (#17611)
* runfix: Missing OutlineCheck Icon (#17609) * feat: add dataUieValue for delivered state * fix cr changes and add tests
1 parent 74d04e5 commit 29dde79

File tree

9 files changed

+146
-59
lines changed

9 files changed

+146
-59
lines changed

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

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ import {QualifiedId} from '@wireapp/api-client/lib/user';
2323
import cx from 'classnames';
2424
import ko from 'knockout';
2525

26-
import {OutlineCheck} from '@wireapp/react-ui-kit';
27-
26+
import {DeliveredMessage} from 'Components/MessagesList/Message/DeliveredMessage';
2827
import {ReadIndicator} from 'Components/MessagesList/Message/ReadIndicator';
2928
import {Conversation} from 'src/script/entity/Conversation';
3029
import {CompositeMessage} from 'src/script/entity/message/CompositeMessage';
@@ -33,8 +32,6 @@ import {useRelativeTimestamp} from 'src/script/hooks/useRelativeTimestamp';
3332
import {StatusType} from 'src/script/message/StatusType';
3433
import {useKoSubscribableChildren} from 'Util/ComponentUtil';
3534
import {getMessageAriaLabel} from 'Util/conversationMessages';
36-
import {t} from 'Util/LocalizerUtil';
37-
import {checkIsMessageDelivered} from 'Util/util';
3835

3936
import {ContentAsset} from './asset';
4037
import {deliveredMessageIndicator, messageBodyWrapper} from './ContentMessage.styles';
@@ -167,8 +164,6 @@ export const ContentMessageComponent = ({
167164
}
168165
};
169166

170-
const showDeliveredMessageIcon = checkIsMessageDelivered(isLastDeliveredMessage, readReceipts);
171-
172167
// Closing another ActionMenu on outside click
173168
useClickOutside(messageRef, hideActionMenuVisibility);
174169

@@ -269,11 +264,7 @@ export const ContentMessageComponent = ({
269264
</div>
270265

271266
<div css={deliveredMessageIndicator}>
272-
{showDeliveredMessageIcon && (
273-
<div data-uie-name="status-message-read-receipt-delivered" title={t('conversationMessageDelivered')}>
274-
<OutlineCheck />
275-
</div>
276-
)}
267+
<DeliveredMessage isLastDeliveredMessage={isLastDeliveredMessage} readReceipts={readReceipts} />
277268
</div>
278269
</div>
279270

src/script/components/MessagesList/Message/ContentMessage/MessageActions/MessageActions.styles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export const messageBodyActions: CSSObject = {
3131
minHeight: '32px',
3232
minWidth: '40px',
3333
position: 'absolute',
34-
right: '16px',
34+
right: '-40px',
3535
top: '-34px',
3636
userSelect: 'none',
3737
'@media (max-width: @screen-md-min)': {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Wire
3+
* Copyright (C) 2024 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 {render} from '@testing-library/react';
21+
22+
import {DeliveredMessage} from './DeliveredMessage';
23+
24+
import {withTheme} from '../../../../auth/util/test/TestUtil';
25+
import {ReadReceipt} from '../../../../storage';
26+
27+
describe('DeliveredMessage', () => {
28+
it('should render null if isLastDeliveredMessage is false', () => {
29+
const {queryByTestId} = render(withTheme(<DeliveredMessage isLastDeliveredMessage={false} />));
30+
expect(queryByTestId('status-message-read-receipt-delivered')).toBeNull();
31+
});
32+
33+
it('should render null if readReceipts is not empty', () => {
34+
const readReceipts = [{time: '123', userId: 'userId-1'}] as ReadReceipt[];
35+
const {queryByTestId} = render(
36+
withTheme(<DeliveredMessage isLastDeliveredMessage={true} readReceipts={readReceipts} />),
37+
);
38+
expect(queryByTestId('status-message-read-receipt-delivered')).toBeNull();
39+
});
40+
41+
it('should render the delivered message icon if isLastDeliveredMessage is true and readReceipts is empty', () => {
42+
const {queryByTestId} = render(withTheme(<DeliveredMessage isLastDeliveredMessage={true} readReceipts={[]} />));
43+
expect(queryByTestId('status-message-read-receipt-delivered')).not.toBeNull();
44+
});
45+
});
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Wire
3+
* Copyright (C) 2024 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 {OutlineCheck} from '@wireapp/react-ui-kit';
21+
22+
import {t} from 'Util/LocalizerUtil';
23+
import {formatTimeShort} from 'Util/TimeUtil';
24+
25+
import {ReadReceipt} from '../../../../storage';
26+
27+
interface DeliveredMessageProps {
28+
isLastDeliveredMessage?: boolean;
29+
readReceipts?: ReadReceipt[];
30+
}
31+
32+
export const DeliveredMessage = ({isLastDeliveredMessage = false, readReceipts = []}: DeliveredMessageProps) => {
33+
const readReceiptText = readReceipts.length ? formatTimeShort(readReceipts[0].time) : '';
34+
const showDeliveredMessageIcon = isLastDeliveredMessage && readReceiptText === '';
35+
36+
if (!showDeliveredMessageIcon) {
37+
return null;
38+
}
39+
40+
return (
41+
<div
42+
data-uie-name="status-message-read-receipt-delivered"
43+
title={t('conversationMessageDelivered')}
44+
className="delivered-message-icon"
45+
>
46+
<OutlineCheck />
47+
</div>
48+
);
49+
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Wire
3+
* Copyright (C) 2024 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+
export * from './DeliveredMessage';

src/script/components/MessagesList/Message/PingMessage.tsx

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,8 @@
1919

2020
import cx from 'classnames';
2121

22-
import {OutlineCheck} from '@wireapp/react-ui-kit';
23-
22+
import {DeliveredMessage} from 'Components/MessagesList/Message/DeliveredMessage';
2423
import {useKoSubscribableChildren} from 'Util/ComponentUtil';
25-
import {t} from 'Util/LocalizerUtil';
26-
import {checkIsMessageDelivered} from 'Util/util';
2724

2825
import {ReadReceiptStatus} from './ReadReceiptStatus';
2926

@@ -46,17 +43,14 @@ const PingMessage = ({message, is1to1Conversation, isLastDeliveredMessage}: Ping
4643
'readReceipts',
4744
]);
4845

49-
const showDeliveredMessageIcon = checkIsMessageDelivered(isLastDeliveredMessage, readReceipts);
50-
5146
return (
5247
<div className="message-header" data-uie-name="element-message-ping">
5348
<div className="message-header-icon">
5449
<div className={`icon-ping ${get_icon_classes}`} />
5550
</div>
5651
<div
57-
className={cx('message-header-label', {
52+
className={cx('message-header-label message-header-ping', {
5853
'ephemeral-message-obfuscated': isObfuscated,
59-
'message-header-ping-delivered': showDeliveredMessageIcon,
6054
})}
6155
title={ephemeral_caption}
6256
data-uie-name="element-message-ping-text"
@@ -66,13 +60,8 @@ const PingMessage = ({message, is1to1Conversation, isLastDeliveredMessage}: Ping
6660
<span className="ellipsis">{caption}</span>
6761
</p>
6862

69-
{showDeliveredMessageIcon ? (
70-
<div className="message-ping-delivered-icon" title={t('conversationMessageDelivered')}>
71-
<OutlineCheck />
72-
</div>
73-
) : (
74-
<ReadReceiptStatus message={message} is1to1Conversation={is1to1Conversation} />
75-
)}
63+
<DeliveredMessage isLastDeliveredMessage={isLastDeliveredMessage} readReceipts={readReceipts} />
64+
<ReadReceiptStatus message={message} is1to1Conversation={is1to1Conversation} />
7665
</div>
7766
</div>
7867
);

src/script/components/MessagesList/Message/ReadReceiptStatus.tsx

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -46,29 +46,29 @@ export const ReadReceiptStatus = ({message, is1to1Conversation, onClickDetails}:
4646

4747
const showEyeIndicator = !!readReceiptText;
4848

49+
if (!showEyeIndicator) {
50+
return null;
51+
}
52+
4953
return (
50-
<>
51-
{showEyeIndicator && (
52-
<button
53-
className={cx(
54-
'message-status-read',
55-
is1to1Conversation && 'message-status-read__one-on-one',
56-
!!onClickDetails && 'message-status-read__clickable',
57-
)}
58-
data-uie-name="status-message-read-receipts"
59-
aria-label={t('accessibility.messageDetailsReadReceipts', readReceiptText)}
60-
{...(!is1to1Conversation && {
61-
onClick: () => {
62-
onClickDetails?.(message);
63-
},
64-
})}
65-
>
66-
<Icon.Read />
67-
<span className="message-status-read__count" data-uie-name="status-message-read-receipt-count">
68-
{readReceiptText}
69-
</span>
70-
</button>
54+
<button
55+
className={cx(
56+
'message-status-read',
57+
is1to1Conversation && 'message-status-read__one-on-one',
58+
!!onClickDetails && 'message-status-read__clickable',
7159
)}
72-
</>
60+
data-uie-name="status-message-read-receipts"
61+
aria-label={t('accessibility.messageDetailsReadReceipts', readReceiptText)}
62+
{...(!is1to1Conversation && {
63+
onClick: () => {
64+
onClickDetails?.(message);
65+
},
66+
})}
67+
>
68+
<Icon.Read />
69+
<span className="message-status-read__count" data-uie-name="status-message-read-receipt-count">
70+
{readReceiptText}
71+
</span>
72+
</button>
7373
);
7474
};

src/script/util/util.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,11 @@ import {StatusCodes as HTTP_STATUS} from 'http-status-codes';
2222

2323
import {Runtime} from '@wireapp/commons';
2424

25-
import {formatTimeShort} from 'Util/TimeUtil';
26-
2725
import {isTabKey} from './KeyboardUtil';
2826

2927
import {Config} from '../Config';
3028
import type {Conversation} from '../entity/Conversation';
3129
import {AuthError} from '../error/AuthError';
32-
import {ReadReceipt} from '../storage';
3330

3431
export const checkIndexedDb = (): Promise<void> => {
3532
if (!Runtime.isSupportingIndexedDb()) {
@@ -319,8 +316,3 @@ export const removeAnimationsClass = (element: HTMLElement | null) => {
319316
});
320317
}
321318
};
322-
323-
export const checkIsMessageDelivered = (isLastDeliveredMessage: boolean, readReceipts?: ReadReceipt[]) => {
324-
const readReceiptText = readReceipts?.length ? formatTimeShort(readReceipts[0].time) : '';
325-
return isLastDeliveredMessage && readReceiptText === '';
326-
};

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@
176176
}
177177
}
178178

179-
.message-ping-delivered-icon {
179+
.message-ping-delivered-icon,
180+
.message-header-ping .delivered-message-icon {
180181
display: flex;
181182
width: var(--delivered-state-width);
182183
align-items: center;
@@ -191,7 +192,7 @@
191192
font-weight: @font-weight-regular;
192193
white-space: normal;
193194

194-
&.message-header-ping-delivered {
195+
&:has(.delivered-message-icon) {
195196
width: 100%;
196197
justify-content: space-between;
197198
}

0 commit comments

Comments
 (0)