Skip to content

Commit 280392d

Browse files
committed
Update default message search filters when using predefined filters.
(cherry picked from commit b3595e6)
1 parent ee2d3d8 commit 280392d

2 files changed

Lines changed: 132 additions & 6 deletions

File tree

stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/channels/ChannelListViewModel.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ public class ChannelListViewModel internal constructor(
400400

401401
private suspend fun searchMessagesForQuery(query: String) {
402402
logger.d { "[searchMessagesForQuery] query: '$query'" }
403-
val channelFilter = filterFlow.value ?: Filters.defaultChannelListFilter(user.value) ?: run {
403+
val channelFilter = messageSearchChannelFilter() ?: run {
404404
logger.v { "[searchMessagesForQuery] rejected (no channel filter)" }
405405
return
406406
}
@@ -413,7 +413,7 @@ public class ChannelListViewModel internal constructor(
413413

414414
private suspend fun loadMoreQueryMessages() {
415415
logger.d { "[loadMoreQueryMessages] no args" }
416-
val channelFilter = filterFlow.value ?: Filters.defaultChannelListFilter(user.value) ?: run {
416+
val channelFilter = messageSearchChannelFilter() ?: run {
417417
logger.v { "[loadMoreQueryMessages] rejected (no channel filter)" }
418418
return
419419
}
@@ -632,6 +632,16 @@ public class ChannelListViewModel internal constructor(
632632
Filters.`in`("members", user.value?.id.orEmpty()),
633633
)
634634

635+
private fun messageSearchChannelFilter() = when (mode) {
636+
// Standard mode: Use the initial filters (backwards compatible)
637+
is QueryMode.Standard -> filterFlow.value ?: Filters.defaultChannelListFilter(user.value)
638+
// Predefined mode: Use simple membership filter (aligned with other platforms)
639+
is QueryMode.Predefined -> when (val userId = user.value?.id) {
640+
null -> null
641+
else -> Filters.`in`("members", listOf(userId))
642+
}
643+
}
644+
635645
/**
636646
* Refreshes either channels or search results.
637647
*/

stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/viewmodel/channels/ChannelListViewModelTest.kt

Lines changed: 120 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ import org.mockito.kotlin.verify
7272
import org.mockito.kotlin.whenever
7373
import java.util.Date
7474

75+
@Suppress("LargeClass")
7576
@ExperimentalCoroutinesApi
7677
@ExtendWith(TestCoroutineExtension::class)
7778
internal class ChannelListViewModelTest {
@@ -160,7 +161,7 @@ internal class ChannelListViewModelTest {
160161
fun `Given channel list in content state with a muted channel When unmuting the channel Should unmute the channel`() =
161162
runTest {
162163
val channelMute = ChannelMute(
163-
user = User(id = "Jc"),
164+
user = User(id = currentUserId),
164165
channel = channel1,
165166
createdAt = Date(),
166167
updatedAt = Date(),
@@ -582,7 +583,7 @@ internal class ChannelListViewModelTest {
582583
assertEquals("name", autocompleteByName.fieldName)
583584
assertEquals("Search query", autocompleteByName.value)
584585
assertEquals("members", membersIn.fieldName)
585-
assertTrue("Jc" in membersIn.values)
586+
assertTrue(currentUserId in membersIn.values)
586587
}
587588

588589
@Test
@@ -651,6 +652,119 @@ internal class ChannelListViewModelTest {
651652
verify(chatClient, times(1)).queryChannels(any())
652653
}
653654

655+
@Test
656+
fun `Given predefined filter When searching messages Should scope channelFilter to current user membership only`() =
657+
runTest {
658+
val chatClient: ChatClient = mock()
659+
val viewModel = Fixture(chatClient)
660+
.givenCurrentUser()
661+
.givenChannelsQuery()
662+
.givenChannelsState(
663+
channelsStateData = ChannelsStateData.Result(listOf(channel1)),
664+
loading = false,
665+
)
666+
.givenChannelMutes()
667+
.givenPredefined(name = "vip_filter")
668+
.givenSearchMessagesResult(SearchMessagesResult())
669+
.givenRepositorySelectChannels()
670+
.get(this)
671+
672+
viewModel.setSearchQuery(SearchQuery.Messages("hello"))
673+
advanceUntilIdle()
674+
675+
val channelFilterCaptor = argumentCaptor<FilterObject>()
676+
verify(chatClient).searchMessages(
677+
channelFilter = channelFilterCaptor.capture(),
678+
messageFilter = any(),
679+
offset = anyOrNull(),
680+
limit = anyOrNull(),
681+
next = anyOrNull(),
682+
sort = anyOrNull(),
683+
)
684+
val captured = channelFilterCaptor.firstValue as InFilterObject
685+
assertEquals("members", captured.fieldName)
686+
assertEquals(setOf(currentUserId), captured.values)
687+
}
688+
689+
@Test
690+
fun `Given predefined filter and active message search When loading more Should reuse member-only channelFilter`() =
691+
runTest {
692+
val chatClient: ChatClient = mock()
693+
val firstPage = SearchMessagesResult(
694+
messages = listOf(randomMessage(cid = "messaging:channel1")),
695+
next = "cursor_page2",
696+
)
697+
val secondPage = SearchMessagesResult(
698+
messages = listOf(randomMessage(cid = "messaging:channel1")),
699+
next = null,
700+
)
701+
whenever(
702+
chatClient.searchMessages(any(), any(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull()),
703+
).doReturn(firstPage.asCall(), secondPage.asCall())
704+
705+
val viewModel = Fixture(chatClient)
706+
.givenCurrentUser()
707+
.givenChannelsQuery()
708+
.givenChannelsState(
709+
channelsStateData = ChannelsStateData.Result(listOf(channel1)),
710+
loading = false,
711+
)
712+
.givenChannelMutes()
713+
.givenPredefined(name = "vip_filter")
714+
.givenRepositorySelectChannels(listOf(channel1))
715+
.get(this)
716+
717+
viewModel.setSearchQuery(SearchQuery.Messages("hello"))
718+
advanceUntilIdle()
719+
viewModel.loadMore()
720+
advanceUntilIdle()
721+
722+
val channelFilterCaptor = argumentCaptor<FilterObject>()
723+
verify(chatClient, times(2)).searchMessages(
724+
channelFilter = channelFilterCaptor.capture(),
725+
messageFilter = any(),
726+
offset = anyOrNull(),
727+
limit = anyOrNull(),
728+
next = anyOrNull(),
729+
sort = anyOrNull(),
730+
)
731+
channelFilterCaptor.allValues.forEach { filter ->
732+
val asIn = filter as InFilterObject
733+
assertEquals("members", asIn.fieldName)
734+
assertEquals(setOf(currentUserId), asIn.values)
735+
}
736+
}
737+
738+
@Test
739+
fun `Given standard filter When searching messages Should pass the standard filter as channelFilter`() = runTest {
740+
val chatClient: ChatClient = mock()
741+
val viewModel = Fixture(chatClient)
742+
.givenCurrentUser()
743+
.givenChannelsQuery()
744+
.givenChannelsState(
745+
channelsStateData = ChannelsStateData.Result(listOf(channel1)),
746+
loading = false,
747+
)
748+
.givenChannelMutes()
749+
.givenSearchMessagesResult(SearchMessagesResult())
750+
.givenRepositorySelectChannels()
751+
.get(this)
752+
753+
viewModel.setSearchQuery(SearchQuery.Messages("hello"))
754+
advanceUntilIdle()
755+
756+
val channelFilterCaptor = argumentCaptor<FilterObject>()
757+
verify(chatClient).searchMessages(
758+
channelFilter = channelFilterCaptor.capture(),
759+
messageFilter = any(),
760+
offset = anyOrNull(),
761+
limit = anyOrNull(),
762+
next = anyOrNull(),
763+
sort = anyOrNull(),
764+
)
765+
assertEquals(queryFilter, channelFilterCaptor.firstValue)
766+
}
767+
654768
private class Fixture(
655769
private val chatClient: ChatClient = mock(),
656770
private val channelClient: ChannelClient = mock(),
@@ -679,7 +793,7 @@ internal class ChannelListViewModelTest {
679793
whenever(globalState.channelDraftMessages) doReturn MutableStateFlow(emptyMap())
680794
}
681795

682-
fun givenCurrentUser(currentUser: User = User(id = "Jc")) = apply {
796+
fun givenCurrentUser(currentUser: User = User(id = currentUserId)) = apply {
683797
whenever(clientState.user) doReturn MutableStateFlow(currentUser)
684798
whenever(clientState.initializationState) doReturn MutableStateFlow(InitializationState.COMPLETE)
685799
whenever(chatClient.awaitInitializationState(any())) doReturn InitializationState.COMPLETE
@@ -787,9 +901,11 @@ internal class ChannelListViewModelTest {
787901

788902
companion object {
789903

904+
private const val currentUserId = "user-id"
905+
790906
private val queryFilter = Filters.and(
791907
Filters.eq("type", "messaging"),
792-
Filters.`in`("members", "jc"),
908+
Filters.`in`("members", currentUserId),
793909
)
794910
private val querySort = QuerySortByField.descByName<Channel>("lastUpdated")
795911

0 commit comments

Comments
 (0)