Skip to content

Commit 47283cb

Browse files
committed
fix: show correct read status on the message status component for a message
1 parent 70e6c70 commit 47283cb

File tree

3 files changed

+185
-112
lines changed

3 files changed

+185
-112
lines changed

Diff for: package/src/components/Message/MessageSimple/MessageStatus.tsx

+22-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22
import { StyleSheet, Text, View } from 'react-native';
33

4+
import { useChannelContext } from '../../../contexts/channelContext/ChannelContext';
45
import {
56
MessageContextValue,
67
useMessageContext,
@@ -23,6 +24,7 @@ const MessageStatusWithContext = <
2324
>(
2425
props: MessageStatusPropsWithContext<StreamChatGenerics>,
2526
) => {
27+
const { channel } = useChannelContext<StreamChatGenerics>();
2628
const { message, threadList } = props;
2729

2830
const {
@@ -47,18 +49,35 @@ const MessageStatusWithContext = <
4749
}
4850

4951
if (isMessageWithStylesReadByAndDateSeparator(message)) {
52+
const members = channel?.state.members;
53+
const otherMembers = Object.values(members).filter(
54+
(member) => member.user_id !== message.user?.id,
55+
);
56+
const hasOtherMembersGreaterThanOne = otherMembers.length > 1;
57+
const hasReadByGreaterThanOne = typeof message.readBy === 'number' && message.readBy > 1;
58+
const shouldDisplayReadByCount = hasOtherMembersGreaterThanOne && hasReadByGreaterThanOne;
59+
const countOfReadBy =
60+
typeof message.readBy === 'number' && hasOtherMembersGreaterThanOne ? message.readBy - 1 : 0;
61+
const showDoubleCheck = hasReadByGreaterThanOne || message.readBy === true;
62+
63+
console.log({
64+
shouldDisplayReadByCount,
65+
hasOtherMembersGreaterThanOne,
66+
hasReadByGreaterThanOne,
67+
});
68+
5069
return (
5170
<View style={[styles.statusContainer, statusContainer]}>
52-
{typeof message.readBy === 'number' ? (
71+
{shouldDisplayReadByCount ? (
5372
<Text
5473
style={[styles.readByCount, { color: accent_blue }, readByCount]}
5574
testID='read-by-container'
5675
>
57-
{message.readBy}
76+
{countOfReadBy}
5877
</Text>
5978
) : null}
6079
{message.type !== 'error' ? (
61-
typeof message.readBy === 'number' || message.readBy === true ? (
80+
showDoubleCheck ? (
6281
<CheckAll pathFill={accent_blue} {...checkAllIcon} />
6382
) : (
6483
<Check pathFill={grey_dark} {...checkIcon} />

Diff for: package/src/components/Message/MessageSimple/__tests__/MessageStatus.test.js

+58-22
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,63 @@ import { getTestClientWithUser } from '../../../../mock-builders/mock';
88
import { Streami18n } from '../../../../utils/i18n/Streami18n';
99
import { Chat } from '../../../Chat/Chat';
1010
import { MessageStatus } from '../MessageStatus';
11+
import { ChannelsStateProvider } from '../../../../contexts/channelsStateContext/ChannelsStateContext';
12+
import { Channel } from '../../..';
13+
import { generateChannelResponse } from '../../../../mock-builders/generator/channel';
14+
import { generateMember } from '../../../../mock-builders/generator/member';
15+
import { useMockedApis } from '../../../../mock-builders/api/useMockedApis';
16+
import { getOrCreateChannelApi } from '../../../../mock-builders/api/getOrCreateChannel';
1117

1218
let chatClient;
1319
let id;
1420
let i18nInstance;
15-
21+
let channel;
1622
describe('MessageStatus', () => {
23+
const user1 = generateUser({ id: 'id1', name: 'name1' });
24+
const user2 = generateUser({ id: 'id2', name: 'name2' });
25+
const user3 = generateUser({ id: 'id3', name: 'name3' });
26+
const messages = [generateMessage({ user: user1 })];
27+
const members = [
28+
generateMember({ user: user1 }),
29+
generateMember({ user: user2 }),
30+
generateMember({ user: user3 }),
31+
];
1732
beforeAll(async () => {
1833
id = 'testID';
19-
chatClient = await getTestClientWithUser({ id });
2034
i18nInstance = new Streami18n();
2135
});
36+
beforeEach(async () => {
37+
jest.clearAllMocks();
38+
const mockedChannel = generateChannelResponse({
39+
members,
40+
messages,
41+
});
42+
43+
chatClient = await getTestClientWithUser(user1);
44+
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
45+
channel = chatClient.channel('messaging', mockedChannel.id);
46+
});
2247
afterEach(cleanup);
2348

49+
renderMessageStatus = (options, channelProps) =>
50+
render(
51+
<ChannelsStateProvider>
52+
<Chat client={chatClient}>
53+
<Channel channel={channel} {...channelProps}>
54+
<MessageStatus {...options} />
55+
</Channel>
56+
</Chat>
57+
</ChannelsStateProvider>,
58+
);
59+
2460
it('should render message status with delivered container', async () => {
2561
const user = generateUser();
2662
const message = generateMessage({ user });
2763

28-
const { getByTestId } = render(
29-
<Chat client={chatClient} i18nInstance={i18nInstance}>
30-
<MessageStatus lastReceivedId={message.id} message={{ ...message, status: 'received' }} />
31-
</Chat>,
32-
);
64+
const { getByTestId } = renderMessageStatus({
65+
lastReceivedId: message.id,
66+
message: { ...message, status: 'received' },
67+
});
3368

3469
await waitFor(() => {
3570
expect(getByTestId('delivered-container')).toBeTruthy();
@@ -40,42 +75,43 @@ describe('MessageStatus', () => {
4075
const user = generateUser();
4176
const message = generateMessage({ readBy: 2, user });
4277

43-
const { getByTestId, getByText, rerender, toJSON } = render(
44-
<Chat client={chatClient} i18nInstance={i18nInstance}>
45-
<MessageStatus lastReceivedId={message.id} message={message} />
46-
</Chat>,
47-
);
78+
const { getByTestId, getByText, rerender, toJSON } = renderMessageStatus({
79+
lastReceivedId: message.id,
80+
message,
81+
});
4882

4983
await waitFor(() => {
5084
expect(getByTestId('read-by-container')).toBeTruthy();
51-
expect(getByText(message.readBy.toString())).toBeTruthy();
85+
expect(getByText((message.readBy - 1).toString())).toBeTruthy();
5286
});
5387

5488
const staticUser = generateStaticUser(0);
5589
const staticMessage = generateMessage({ readBy: 2, staticUser });
5690

5791
rerender(
58-
<Chat client={chatClient} i18nInstance={i18nInstance}>
59-
<MessageStatus lastReceivedId={staticMessage.id} message={staticMessage} />
60-
</Chat>,
92+
<ChannelsStateProvider>
93+
<Chat client={chatClient} i18nInstance={i18nInstance}>
94+
<Channel channel={channel}>
95+
<MessageStatus lastReceivedId={staticMessage.id} message={staticMessage} />
96+
</Channel>
97+
</Chat>
98+
</ChannelsStateProvider>,
6199
);
62100

63101
await waitFor(() => {
64102
expect(toJSON()).toMatchSnapshot();
65103
expect(getByTestId('read-by-container')).toBeTruthy();
66-
expect(getByText(staticMessage.readBy.toString())).toBeTruthy();
104+
expect(getByText((staticMessage.readBy - 1).toString())).toBeTruthy();
67105
});
68106
});
69107

70108
it('should render message status with sending container', async () => {
71109
const user = generateUser();
72110
const message = generateMessage({ user });
73111

74-
const { getByTestId } = render(
75-
<Chat client={chatClient} i18nInstance={i18nInstance}>
76-
<MessageStatus message={{ ...message, status: 'sending' }} />
77-
</Chat>,
78-
);
112+
const { getByTestId } = renderMessageStatus({
113+
message: { ...message, status: 'sending' },
114+
});
79115

80116
await waitFor(() => {
81117
expect(getByTestId('sending-container')).toBeTruthy();

Diff for: package/src/components/Message/MessageSimple/__tests__/__snapshots__/MessageStatus.test.js.snap

+105-87
Original file line numberDiff line numberDiff line change
@@ -2,106 +2,124 @@
22

33
exports[`MessageStatus should render message status with read by container 1`] = `
44
<View
5+
keyboardVerticalOffset={86.5}
6+
onLayout={[Function]}
57
style={
6-
[
7-
{
8-
"alignItems": "flex-end",
9-
"flexDirection": "row",
10-
"justifyContent": "center",
11-
"paddingRight": 3,
12-
},
13-
{},
14-
]
8+
{
9+
"paddingBottom": 0,
10+
}
1511
}
1612
>
17-
<Text
18-
style={
19-
[
20-
{
21-
"fontSize": 11,
22-
"fontWeight": "700",
23-
"paddingRight": 3,
24-
},
25-
{
26-
"color": "#005FFF",
27-
},
28-
{},
29-
]
30-
}
31-
testID="read-by-container"
32-
>
33-
2
34-
</Text>
35-
<RNSVGSvgView
36-
align="xMidYMid"
37-
bbHeight={16}
38-
bbWidth={16}
39-
focusable={false}
40-
height={16}
41-
meetOrSlice={0}
42-
minX={0}
43-
minY={0}
44-
pathFill="#005FFF"
13+
<View
4514
style={
46-
[
47-
{
48-
"backgroundColor": "transparent",
49-
"borderWidth": 0,
50-
},
51-
{
52-
"flex": 0,
53-
"height": 16,
54-
"width": 16,
55-
},
56-
]
15+
{
16+
"height": "100%",
17+
}
5718
}
58-
vbHeight={24}
59-
vbWidth={24}
60-
width={16}
6119
>
62-
<RNSVGGroup
63-
fill={
64-
{
65-
"payload": 4278190080,
66-
"type": 0,
67-
}
20+
<View
21+
style={
22+
[
23+
{
24+
"alignItems": "flex-end",
25+
"flexDirection": "row",
26+
"justifyContent": "center",
27+
"paddingRight": 3,
28+
},
29+
{},
30+
]
6831
}
6932
>
70-
<RNSVGPath
71-
clipRule={0}
72-
d="M2.293 11.293a1 1 0 011.414 0l4 4a1 1 0 11-1.414 1.414l-4-4a1 1 0 010-1.414zM9.293 12.293a1 1 0 011.414 0l3 3a1 1 0 01-1.414 1.414l-3-3a1 1 0 010-1.414z"
73-
fill={
74-
{
75-
"payload": 4278214655,
76-
"type": 0,
77-
}
78-
}
79-
fillRule={0}
80-
propList={
33+
<Text
34+
style={
8135
[
82-
"fill",
83-
"fillRule",
36+
{
37+
"fontSize": 11,
38+
"fontWeight": "700",
39+
"paddingRight": 3,
40+
},
41+
{
42+
"color": "#005FFF",
43+
},
44+
{},
8445
]
8546
}
86-
/>
87-
<RNSVGPath
88-
clipRule={0}
89-
d="M15.707 7.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414-1.414l8-8a1 1 0 011.414 0zM21.707 7.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414-1.414l8-8a1 1 0 011.414 0z"
90-
fill={
91-
{
92-
"payload": 4278214655,
93-
"type": 0,
94-
}
95-
}
96-
fillRule={0}
97-
propList={
47+
testID="read-by-container"
48+
>
49+
1
50+
</Text>
51+
<RNSVGSvgView
52+
align="xMidYMid"
53+
bbHeight={16}
54+
bbWidth={16}
55+
focusable={false}
56+
height={16}
57+
meetOrSlice={0}
58+
minX={0}
59+
minY={0}
60+
pathFill="#005FFF"
61+
style={
9862
[
99-
"fill",
100-
"fillRule",
63+
{
64+
"backgroundColor": "transparent",
65+
"borderWidth": 0,
66+
},
67+
{
68+
"flex": 0,
69+
"height": 16,
70+
"width": 16,
71+
},
10172
]
10273
}
103-
/>
104-
</RNSVGGroup>
105-
</RNSVGSvgView>
74+
vbHeight={24}
75+
vbWidth={24}
76+
width={16}
77+
>
78+
<RNSVGGroup
79+
fill={
80+
{
81+
"payload": 4278190080,
82+
"type": 0,
83+
}
84+
}
85+
>
86+
<RNSVGPath
87+
clipRule={0}
88+
d="M2.293 11.293a1 1 0 011.414 0l4 4a1 1 0 11-1.414 1.414l-4-4a1 1 0 010-1.414zM9.293 12.293a1 1 0 011.414 0l3 3a1 1 0 01-1.414 1.414l-3-3a1 1 0 010-1.414z"
89+
fill={
90+
{
91+
"payload": 4278214655,
92+
"type": 0,
93+
}
94+
}
95+
fillRule={0}
96+
propList={
97+
[
98+
"fill",
99+
"fillRule",
100+
]
101+
}
102+
/>
103+
<RNSVGPath
104+
clipRule={0}
105+
d="M15.707 7.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414-1.414l8-8a1 1 0 011.414 0zM21.707 7.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414-1.414l8-8a1 1 0 011.414 0z"
106+
fill={
107+
{
108+
"payload": 4278214655,
109+
"type": 0,
110+
}
111+
}
112+
fillRule={0}
113+
propList={
114+
[
115+
"fill",
116+
"fillRule",
117+
]
118+
}
119+
/>
120+
</RNSVGGroup>
121+
</RNSVGSvgView>
122+
</View>
123+
</View>
106124
</View>
107125
`;

0 commit comments

Comments
 (0)