From 165afdda46ea64bdb0448d21727f211003468da8 Mon Sep 17 00:00:00 2001 From: VelikovPetar Date: Mon, 24 Nov 2025 10:05:43 +0100 Subject: [PATCH 1/4] Prevent calling fillTheGap when paginating messages. --- .../logic/channel/internal/ChannelLogic.kt | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogic.kt b/stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogic.kt index 67994d6928b..e392d8c2f66 100644 --- a/stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogic.kt +++ b/stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogic.kt @@ -237,7 +237,12 @@ internal class ChannelLogic( val offlineChannel = runChannelQueryOffline(request) val onlineResult = runChannelQueryOnline(request) - .onSuccess { fillTheGap(request.messagesLimit(), loadedMessages, it.messages) } + .onSuccess { + // Fill the missing messages gap only if loading around ID + if (request.isFilteringAroundIdMessages()) { + fillTheGap(request.messagesLimit(), loadedMessages, it.messages) + } + } return when { onlineResult is Result.Success -> onlineResult @@ -480,15 +485,6 @@ internal class ChannelLogic( channelStateLogic.removeMessagesBefore(date, systemMessage) } - /** - * Hides the messages created before the given date. - * - * @param date The date used for generating result. - */ - internal fun hideMessagesBefore(date: Date) { - channelStateLogic.hideMessagesBefore(date) - } - private fun upsertReminder(messageId: String, reminder: MessageReminder) { val message = reminder.message ?: mutableState.getMessageById(messageId) ?: return upsertEventMessage(message.copy(reminder = reminder.toMessageReminderInfo())) From 334f25de54985abce0153c52076f5059156df3d2 Mon Sep 17 00:00:00 2001 From: VelikovPetar Date: Mon, 24 Nov 2025 10:06:02 +0100 Subject: [PATCH 2/4] Prevent calling loadOlderMessages if already loading. --- .../messages/list/MessageListController.kt | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/list/MessageListController.kt b/stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/list/MessageListController.kt index 07e9edc4322..eb0a60b9ac8 100644 --- a/stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/list/MessageListController.kt +++ b/stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/list/MessageListController.kt @@ -1210,15 +1210,14 @@ public class MessageListController( logger.d { "[loadOlderMessages] messageLimit: $messageLimit" } if (clientState.isOffline) return - _mode.value.run { - when (this) { - is MessageMode.Normal -> { - if (channelState.value?.endOfOlderMessages?.value == true) return - chatClient.loadOlderMessages(cid, messageLimit).enqueue() - } - - is MessageMode.MessageThread -> threadLoadMore(this) + when (val mode = _mode.value) { + is MessageMode.Normal -> { + val endOfOlderMessages = channelState.value?.endOfOlderMessages?.value == true + val loadingOlderMessages = channelState.value?.loadingOlderMessages?.value == true + if (endOfOlderMessages || loadingOlderMessages) return + chatClient.loadOlderMessages(cid, messageLimit).enqueue() } + is MessageMode.MessageThread -> threadLoadMore(mode) } } From 38899f46915f6efe6a216ef57530e25560f90058 Mon Sep 17 00:00:00 2001 From: VelikovPetar Date: Mon, 24 Nov 2025 10:21:09 +0100 Subject: [PATCH 3/4] Update CHANGELOG.md. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73aefcbd6a7..f93cf72183c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ ## stream-chat-android-state ### 🐞 Fixed +- Prevent duplicate `GetOrCreateChannel` calls when paginating messages. [#6016](https://github.com/GetStream/stream-chat-android/pull/6016) ### ⬆️ Improved From 21a1403dbbc05f8c5115ec2fb963e18bfad975c8 Mon Sep 17 00:00:00 2001 From: VelikovPetar Date: Fri, 28 Nov 2025 11:50:22 +0100 Subject: [PATCH 4/4] Update channel loading states if GetOrCreateChannel fails. --- .../channel/internal/ChannelStateLogic.kt | 2 ++ .../channel/internal/ChannelStateLogicTest.kt | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.kt b/stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.kt index 9607fa7ef7e..588993202c6 100644 --- a/stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.kt +++ b/stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.kt @@ -784,6 +784,8 @@ internal class ChannelStateLogic( } mutableState.recoveryNeeded = true } + mutableState.setLoadingOlderMessages(false) + mutableState.setLoadingNewerMessages(false) } /** diff --git a/stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogicTest.kt b/stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogicTest.kt index 0d8a40271a1..f19948e857b 100644 --- a/stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogicTest.kt +++ b/stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogicTest.kt @@ -51,6 +51,7 @@ import io.getstream.chat.android.state.model.querychannels.pagination.internal.Q import io.getstream.chat.android.state.plugin.state.channel.internal.ChannelMutableState import io.getstream.chat.android.state.plugin.state.global.internal.MutableGlobalState import io.getstream.chat.android.test.TestCoroutineExtension +import io.getstream.result.Error import kotlinx.coroutines.flow.MutableStateFlow import org.amshove.kluent.`should be equal to` import org.amshove.kluent.`should not be equal to` @@ -65,6 +66,7 @@ import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock +import org.mockito.kotlin.never import org.mockito.kotlin.spy import org.mockito.kotlin.times import org.mockito.kotlin.verify @@ -789,6 +791,28 @@ internal class ChannelStateLogicTest { ) } + @Test + fun `When propagateQueryError is called for recoverable error, Then channel is marked for recovery and loading is reset`() { + // when + channelStateLogic.propagateQueryError(Error.GenericError("Test error")) + + // then + verify(mutableState).recoveryNeeded = true + verify(mutableState).setLoadingOlderMessages(false) + verify(mutableState).setLoadingNewerMessages(false) + } + + @Test + fun `When propagateQueryError is called for unrecoverable error, Then channel is not marked for recovery and loading is reset`() { + // when + channelStateLogic.propagateQueryError(Error.NetworkError("Test network error", 500)) + + // then + verify(mutableState, never()).recoveryNeeded = any() + verify(mutableState).setLoadingOlderMessages(false) + verify(mutableState).setLoadingNewerMessages(false) + } + companion object { @JvmField