stream-chat-react-native-core 9.0.0-beta.23 → 9.0.0-beta.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -720,6 +720,131 @@ export const OptimisticUpdates = () => {
720
720
  expect(sendMessageSpy).toHaveBeenCalled();
721
721
  });
722
722
  });
723
+
724
+ it('should not re-add a failed local message after reconnect when its pending send task was resolved', async () => {
725
+ const localMessage = generateMessage({
726
+ status: MessageStatusTypes.SENDING,
727
+ text: 'offline resend',
728
+ user: chatClient.user,
729
+ userId: chatClient.userID,
730
+ });
731
+ const serverMessage = generateMessage({
732
+ id: localMessage.id,
733
+ text: localMessage.text,
734
+ user: chatClient.user,
735
+ userId: chatClient.userID,
736
+ });
737
+
738
+ jest.spyOn(channel.messageComposer, 'compose').mockResolvedValue({
739
+ localMessage,
740
+ message: localMessage,
741
+ options: {},
742
+ });
743
+
744
+ render(
745
+ <Chat client={chatClient} enableOfflineSupport>
746
+ <Channel channel={channel} initialValue={localMessage.text}>
747
+ <CallbackEffectWithContext
748
+ callback={async ({ sendMessage }) => {
749
+ useMockedApis(chatClient, [erroredPostApi()]);
750
+ await sendMessage();
751
+ }}
752
+ context={MessageInputContext}
753
+ >
754
+ <View testID='children' />
755
+ </CallbackEffectWithContext>
756
+ </Channel>
757
+ </Chat>,
758
+ );
759
+ await waitFor(() => expect(screen.getByTestId('children')).toBeTruthy());
760
+
761
+ let pendingTask;
762
+ await waitFor(async () => {
763
+ const pendingTasks = await chatClient.offlineDb.getPendingTasks();
764
+ expect(pendingTasks).toHaveLength(1);
765
+ pendingTask = pendingTasks[0];
766
+ });
767
+
768
+ expect(channel.state.messages.some((message) => message.id === localMessage.id)).toBe(true);
769
+
770
+ jest.spyOn(channel, 'watch').mockResolvedValue({});
771
+
772
+ channel.state.removeMessage(localMessage);
773
+ channel.state.addMessageSorted(serverMessage, true);
774
+ await chatClient.offlineDb.deletePendingTask({ id: pendingTask.id });
775
+
776
+ await act(async () => {
777
+ await chatClient.offlineDb.syncManager.invokeSyncStatusListeners(true);
778
+ });
779
+
780
+ await waitFor(() => {
781
+ const matchingMessages = channel.state.messages.filter(
782
+ (message) => message.text === localMessage.text,
783
+ );
784
+
785
+ expect(matchingMessages).toHaveLength(1);
786
+ expect(matchingMessages[0].id).toBe(serverMessage.id);
787
+ expect(matchingMessages[0].status).not.toBe(MessageStatusTypes.FAILED);
788
+ });
789
+ });
790
+
791
+ it('should re-add a failed local message after reconnect when fresh state still does not contain it', async () => {
792
+ const localMessage = generateMessage({
793
+ status: MessageStatusTypes.SENDING,
794
+ text: 'offline resend unresolved',
795
+ user: chatClient.user,
796
+ userId: chatClient.userID,
797
+ });
798
+
799
+ jest.spyOn(channel.messageComposer, 'compose').mockResolvedValue({
800
+ localMessage,
801
+ message: localMessage,
802
+ options: {},
803
+ });
804
+
805
+ render(
806
+ <Chat client={chatClient} enableOfflineSupport>
807
+ <Channel channel={channel} initialValue={localMessage.text}>
808
+ <CallbackEffectWithContext
809
+ callback={async ({ sendMessage }) => {
810
+ useMockedApis(chatClient, [erroredPostApi()]);
811
+ await sendMessage();
812
+ }}
813
+ context={MessageInputContext}
814
+ >
815
+ <View testID='children' />
816
+ </CallbackEffectWithContext>
817
+ </Channel>
818
+ </Chat>,
819
+ );
820
+ await waitFor(() => expect(screen.getByTestId('children')).toBeTruthy());
821
+
822
+ let pendingTask;
823
+ await waitFor(async () => {
824
+ const pendingTasks = await chatClient.offlineDb.getPendingTasks();
825
+ expect(pendingTasks).toHaveLength(1);
826
+ pendingTask = pendingTasks[0];
827
+ });
828
+
829
+ jest.spyOn(channel, 'watch').mockResolvedValue({});
830
+
831
+ channel.state.removeMessage(localMessage);
832
+ await chatClient.offlineDb.deletePendingTask({ id: pendingTask.id });
833
+
834
+ await act(async () => {
835
+ await chatClient.offlineDb.syncManager.invokeSyncStatusListeners(true);
836
+ });
837
+
838
+ await waitFor(() => {
839
+ const matchingMessages = channel.state.messages.filter(
840
+ (message) => message.id === localMessage.id,
841
+ );
842
+
843
+ expect(matchingMessages).toHaveLength(1);
844
+ expect(matchingMessages[0].status).toBe(MessageStatusTypes.FAILED);
845
+ expect(matchingMessages[0].text).toBe(localMessage.text);
846
+ });
847
+ });
723
848
  });
724
849
  });
725
850
  };
@@ -1167,6 +1167,15 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1167
1167
  updated_at: message.updated_at?.toString(),
1168
1168
  }) as unknown as MessageResponse;
1169
1169
 
1170
+ const getRecoverableFailedMessages = (messages: LocalMessage[] = []) =>
1171
+ messages
1172
+ .filter(
1173
+ (message) =>
1174
+ message.status === MessageStatusTypes.FAILED &&
1175
+ !channel.state.findMessage(message.id, message.parent_id),
1176
+ )
1177
+ .map(parseMessage);
1178
+
1170
1179
  try {
1171
1180
  if (channelMessagesState?.messages) {
1172
1181
  await channel?.watch({
@@ -1181,9 +1190,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1181
1190
  if (!thread) {
1182
1191
  copyChannelState();
1183
1192
 
1184
- const failedMessages = channelMessagesState.messages
1185
- ?.filter((message) => message.status === MessageStatusTypes.FAILED)
1186
- .map(parseMessage);
1193
+ const failedMessages = getRecoverableFailedMessages(channelMessagesState.messages);
1187
1194
  if (failedMessages?.length) {
1188
1195
  channel.state.addMessagesSorted(failedMessages);
1189
1196
  }
@@ -1192,11 +1199,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1192
1199
  } else {
1193
1200
  await reloadThread();
1194
1201
 
1195
- const failedThreadMessages = thread
1196
- ? threadMessages
1197
- .filter((message) => message.status === MessageStatusTypes.FAILED)
1198
- .map(parseMessage)
1199
- : [];
1202
+ const failedThreadMessages = thread ? getRecoverableFailedMessages(threadMessages) : [];
1200
1203
  if (failedThreadMessages.length) {
1201
1204
  channel.state.addMessagesSorted(failedThreadMessages);
1202
1205
  setThreadMessages([...channel.state.threads[thread.id]]);
package/src/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "9.0.0-beta.23"
2
+ "version": "9.0.0-beta.24"
3
3
  }