Skip to content

Commit 8877838

Browse files
committed
fix: mark retryable duplicated messages as received
1 parent 17f6b0d commit 8877838

File tree

2 files changed

+70
-8
lines changed

2 files changed

+70
-8
lines changed

Diff for: src/components/Channel/Channel.tsx

+30-8
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ import debounce from 'lodash.debounce';
1313
import defaultsDeep from 'lodash.defaultsdeep';
1414
import throttle from 'lodash.throttle';
1515
import {
16+
APIErrorResponse,
1617
ChannelAPIResponse,
1718
ChannelMemberResponse,
1819
ChannelQueryOptions,
1920
ChannelState,
21+
ErrorFromResponse,
2022
Event,
2123
EventAPIResponse,
2224
Message,
@@ -940,14 +942,34 @@ const ChannelInner = <
940942
} catch (error) {
941943
// error response isn't usable so needs to be stringified then parsed
942944
const stringError = JSON.stringify(error);
943-
const parsedError = stringError ? JSON.parse(stringError) : {};
944-
945-
updateMessage({
946-
...message,
947-
error: parsedError,
948-
errorStatusCode: (parsedError.status as number) || undefined,
949-
status: 'failed',
950-
});
945+
const parsedError = (stringError
946+
? JSON.parse(stringError)
947+
: {}) as ErrorFromResponse<APIErrorResponse>;
948+
949+
// Handle the case where the message already exists
950+
// (typically, when retrying to send a message).
951+
// If the message already exists, we can assume it was sent successfully,
952+
// so we update the message status to "received".
953+
// Right now, the only way to check this error is by checking
954+
// the combination of the error code and the error description,
955+
// since there is no special error code for duplicate messages.
956+
if (
957+
parsedError.code === 4 &&
958+
error instanceof Error &&
959+
error.message.includes('already exists')
960+
) {
961+
updateMessage({
962+
...message,
963+
status: 'received',
964+
});
965+
} else {
966+
updateMessage({
967+
...message,
968+
error: parsedError,
969+
errorStatusCode: parsedError.status || undefined,
970+
status: 'failed',
971+
});
972+
}
951973
}
952974
};
953975

Diff for: src/components/Channel/__tests__/Channel.test.js

+40
Original file line numberDiff line numberDiff line change
@@ -1261,6 +1261,46 @@ describe('Channel', () => {
12611261
expect(await findByText(messageText)).toBeInTheDocument();
12621262
});
12631263

1264+
it('should mark message as received when the backend reports duplicated message id', async () => {
1265+
const { channel, chatClient } = await initClient();
1266+
// flag to prevent infinite loop
1267+
let hasSent = false;
1268+
const messageText = 'hello world';
1269+
const messageId = '123456';
1270+
1271+
let originalMessageStatus = null;
1272+
1273+
const { findByText } = await renderComponent(
1274+
{
1275+
channel,
1276+
chatClient,
1277+
children: <MockMessageList />,
1278+
},
1279+
({ sendMessage }) => {
1280+
jest.spyOn(channel, 'sendMessage').mockImplementation((message) => {
1281+
originalMessageStatus = message.status;
1282+
throw new chatClient.errorFromResponse({
1283+
data: {
1284+
code: 4,
1285+
message: `SendMessage failed with error: "a message with ID ${message.id} already exists"`,
1286+
},
1287+
status: 400,
1288+
});
1289+
});
1290+
if (!hasSent) {
1291+
sendMessage({ text: messageText }, { id: messageId, status: 'sending' });
1292+
}
1293+
hasSent = true;
1294+
},
1295+
);
1296+
expect(await findByText(messageText)).toBeInTheDocument();
1297+
expect(originalMessageStatus).toBe('sending');
1298+
1299+
const msg = channel.state.findMessage(messageId);
1300+
expect(msg).toBeDefined();
1301+
expect(msg.status).toBe('received');
1302+
});
1303+
12641304
it('should use the doSendMessageRequest prop to send messages if that is defined', async () => {
12651305
const { channel, chatClient } = await initClient();
12661306
// flag to prevent infinite loop

0 commit comments

Comments
 (0)