Skip to content

Commit b942e01

Browse files
Fetch read receipt when participant join the chat, also not show read receipt when having more than 20 participants (#1639)
* fetch read receipt * Change files * comments * Update composite automation snapshots * Update composite automation snapshots * Update js bundle automation snapshots * Update js bundle automation snapshots * read receipt * fix flaky test * add comment * changelog * fetch read receipt * Change files * comments * read receipt * Update composite automation snapshots * fix flaky test * add comment * changelog * fix lint error * Update ChatComposite.test.ts * Update ChatComposite.test.ts * Update ChatComposite.test.ts * Update composite automation snapshots * Update composite automation snapshots * Update composite automation snapshots * fix flaky test Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 9d54cde commit b942e01

12 files changed

Lines changed: 53 additions & 4 deletions
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "fetch read receipt when participant join the chat, also not show read receipt info when having more than 20 participants",
4+
"packageName": "@internal/chat-component-bindings",
5+
"email": "carolinecao@microsoft.com",
6+
"dependentChangeType": "none"
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "fetch read receipt when participant join the chat, also not show read receipt info when having more than 20 participant",
4+
"packageName": "@internal/react-components",
5+
"email": "carolinecao@microsoft.com",
6+
"dependentChangeType": "none"
7+
}

packages/chat-component-bindings/src/handlers/createHandlers.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { PagedAsyncIterableIterator } from '@azure/core-paging';
55
import { ReactElement } from 'react';
66
import { Common, fromFlatCommunicationIdentifier } from '@internal/acs-ui-common';
77
import { StatefulChatClient } from '@internal/chat-stateful-client';
8-
import { ChatMessage, ChatThreadClient, SendMessageOptions } from '@azure/communication-chat';
8+
import { ChatMessage, ChatMessageReadReceipt, ChatThreadClient, SendMessageOptions } from '@azure/communication-chat';
99
import memoizeOne from 'memoize-one';
1010

1111
/**
@@ -40,6 +40,7 @@ export type ChatHandlers = {
4040
export const createDefaultChatHandlers = memoizeOne(
4141
(chatClient: StatefulChatClient, chatThreadClient: ChatThreadClient): ChatHandlers => {
4242
let messageIterator: PagedAsyncIterableIterator<ChatMessage> | undefined = undefined;
43+
let readReceiptIterator: PagedAsyncIterableIterator<ChatMessageReadReceipt> | undefined = undefined;
4344
return {
4445
onSendMessage: async (content: string, options?: SendMessageOptions) => {
4546
const sendMessageRequest = {
@@ -73,20 +74,36 @@ export const createDefaultChatHandlers = memoizeOne(
7374
// Also allows recovery via retries in case of transient errors.
7475
messageIterator = chatThreadClient.listMessages({ maxPageSize: 50 });
7576
}
76-
77+
if (readReceiptIterator === undefined) {
78+
readReceiptIterator = chatThreadClient.listReadReceipts();
79+
}
80+
// get the earliest message time
7781
let remainingMessagesToGet = messagesToLoad;
7882
let isAllChatMessagesLoaded = false;
83+
let earliestTime = Number.MAX_SAFE_INTEGER;
7984
while (remainingMessagesToGet >= 1) {
8085
const message = await messageIterator.next();
86+
if (message?.value?.id) {
87+
if (parseInt(message.value.id) < earliestTime) {
88+
earliestTime = parseInt(message.value.id);
89+
}
90+
}
91+
8192
if (message.value?.type && message.value.type === 'text') {
8293
remainingMessagesToGet--;
8394
}
95+
8496
// We have traversed all messages in this thread
8597
if (message.done) {
8698
isAllChatMessagesLoaded = true;
8799
break;
88100
}
89101
}
102+
// keep fetching read receipts until read receipt time < earlist message time
103+
let readReceipt = await readReceiptIterator.next();
104+
while (!readReceipt.done && parseInt(readReceipt?.value?.chatMessageId) >= earliestTime) {
105+
readReceipt = await readReceiptIterator.next();
106+
}
90107
return isAllChatMessagesLoaded;
91108
}
92109
};

packages/react-components/src/components/ChatMessage/ChatMessageActionsFlyout.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ export interface ChatMessageActionFlyoutProps {
3232
onDismiss: () => void;
3333
messageReadBy?: { id: string; name: string }[];
3434
remoteParticipantsCount?: number;
35+
/**
36+
* Whether the status indicator for each message is displayed or not.
37+
*/
38+
showMessageStatus?: boolean;
3539
/**
3640
* Increase the height of the flyout items.
3741
* Recommended when interacting with the chat message using touch.
@@ -103,6 +107,7 @@ export const ChatMessageActionFlyout = (props: ChatMessageActionFlyoutProps): JS
103107
props.remoteParticipantsCount &&
104108
messageReadByCount !== undefined &&
105109
props.remoteParticipantsCount >= 2 &&
110+
props.showMessageStatus &&
106111
props.strings.messageReadCount
107112
) {
108113
items.push({
@@ -154,6 +159,7 @@ export const ChatMessageActionFlyout = (props: ChatMessageActionFlyoutProps): JS
154159
props.onEditClick,
155160
props.onRemoveClick,
156161
props.remoteParticipantsCount,
162+
props.showMessageStatus,
157163
messageReadByCount,
158164
theme.palette.neutralPrimary,
159165
theme.palette.neutralTertiary,

packages/react-components/src/components/ChatMessage/ChatMessageComponent.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ type ChatMessageComponentProps = {
1818
onUpdateMessage?: (messageId: string, content: string) => Promise<void>;
1919
onDeleteMessage?: (messageId: string) => Promise<void>;
2020
strings: MessageThreadStrings;
21+
/**
22+
* Whether the status indicator for each message is displayed or not.
23+
*/
24+
showMessageStatus?: boolean;
2125
/**
2226
* Inline the accept and reject edit buttons when editing a message.
2327
* Setting to false will mean they are on a new line inside the editable chat message.

packages/react-components/src/components/ChatMessage/ChatMessageComponentAsMessageBubble.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ type ChatMessageComponentAsMessageBubbleProps = {
2525
onRemoveClick?: () => void;
2626
strings: MessageThreadStrings;
2727
userId: string;
28+
/**
29+
* Whether the status indicator for each message is displayed or not.
30+
*/
31+
showMessageStatus?: boolean;
2832
/**
2933
* Optional callback to render uploaded files in the message component.
3034
*/
@@ -52,7 +56,8 @@ export const ChatMessageComponentAsMessageBubble = (props: ChatMessageComponentA
5256
strings,
5357
onEditClick,
5458
remoteParticipantsCount = 0,
55-
onRenderAvatar
59+
onRenderAvatar,
60+
showMessageStatus
5661
} = props;
5762

5863
// Track if the action menu was opened by touch - if so we increase the touch targets for the items
@@ -137,6 +142,7 @@ export const ChatMessageComponentAsMessageBubble = (props: ChatMessageComponentA
137142
messageReadBy={message.readBy ?? []}
138143
remoteParticipantsCount={remoteParticipantsCount}
139144
onRenderAvatar={onRenderAvatar}
145+
showMessageStatus={showMessageStatus}
140146
/>
141147
)}
142148
</>

packages/react-components/src/components/MessageThread.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,7 @@ export const MessageThread = (props: MessageThreadProps): JSX.Element => {
880880
remoteParticipantsCount={messageThreadParticipantCount ? messageThreadParticipantCount - 1 : 0}
881881
inlineAcceptRejectEditButtons={!isNarrow}
882882
onRenderAvatar={onRenderAvatar}
883+
showMessageStatus={showMessageStatus}
883884
/>
884885
);
885886
}
-57 Bytes
Loading
Loading

packages/react-composites/tests/browser/chat/ChatComposite.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ test.describe('Chat Composite E2E Tests', () => {
5656
test('page[0] can view typing indicator within 10s', async ({ pages, users }) => {
5757
const page0 = pages[0];
5858
const page1 = pages[1];
59-
6059
await page1.type(dataUiId(IDS.sendboxTextField), 'I am not superstitious. Just a little stitious.');
6160
await waitForSelector(page0, dataUiId(IDS.typingIndicator));
6261
const indicator0 = await page0.$(dataUiId(IDS.typingIndicator));
@@ -94,6 +93,8 @@ test.describe('Chat Composite E2E Tests', () => {
9493
// await waitForSelector(page1, `[data-ui-status="seen"]`);
9594
await waitForMessageWithContent(page1, testMessageText);
9695
await stubMessageTimestamps(page1);
96+
// we are getting read receipt for previous messages, so the message here should be seen, otherwise it could cause flaky test
97+
await waitForMessageSeen(page1);
9798
expect(await page1.screenshot()).toMatchSnapshot('rejoin-thread.png');
9899
});
99100
});

0 commit comments

Comments
 (0)