Skip to content

Commit 6807a8e

Browse files
VelikovPetarclaude
andauthored
Add messageSearchSort option to ChannelListViewModel and ChannelViewModelFactory (#6187)
* Use cursor-based pagination for search messages Co-Authored-By: Claude <noreply@anthropic.com> * Fix detekt. * Add messageSearchSort option to ChannelListViewModel and ChannelViewModelFactory Co-Authored-By: Claude <noreply@anthropic.com> * Add tests for messageSearchSort in ChannelListViewModel Co-Authored-By: Claude <noreply@anthropic.com> * Make next internal * Add messageSearchSort to logs * detekt * detekt --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent db0d4c8 commit 6807a8e

4 files changed

Lines changed: 86 additions & 5 deletions

File tree

stream-chat-android-compose/api/stream-chat-android-compose.api

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4887,8 +4887,8 @@ public final class io/getstream/chat/android/compose/viewmodel/channel/ChannelIn
48874887

48884888
public final class io/getstream/chat/android/compose/viewmodel/channels/ChannelListViewModel : androidx/lifecycle/ViewModel {
48894889
public static final field $stable I
4890-
public fun <init> (Lio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/models/querysort/QuerySorter;Lio/getstream/chat/android/models/FilterObject;ILjava/lang/Integer;Ljava/lang/Integer;Lio/getstream/chat/android/state/event/handler/chat/factory/ChatEventHandlerFactory;JZLkotlinx/coroutines/flow/Flow;)V
4891-
public synthetic fun <init> (Lio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/models/querysort/QuerySorter;Lio/getstream/chat/android/models/FilterObject;ILjava/lang/Integer;Ljava/lang/Integer;Lio/getstream/chat/android/state/event/handler/chat/factory/ChatEventHandlerFactory;JZLkotlinx/coroutines/flow/Flow;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
4890+
public fun <init> (Lio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/models/querysort/QuerySorter;Lio/getstream/chat/android/models/FilterObject;ILjava/lang/Integer;Ljava/lang/Integer;Lio/getstream/chat/android/state/event/handler/chat/factory/ChatEventHandlerFactory;JZLio/getstream/chat/android/models/querysort/QuerySorter;Lkotlinx/coroutines/flow/Flow;)V
4891+
public synthetic fun <init> (Lio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/models/querysort/QuerySorter;Lio/getstream/chat/android/models/FilterObject;ILjava/lang/Integer;Ljava/lang/Integer;Lio/getstream/chat/android/state/event/handler/chat/factory/ChatEventHandlerFactory;JZLio/getstream/chat/android/models/querysort/QuerySorter;Lkotlinx/coroutines/flow/Flow;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
48924892
public final fun archiveChannel (Lio/getstream/chat/android/models/Channel;)V
48934893
public final fun deleteConversation (Lio/getstream/chat/android/models/Channel;)V
48944894
public final fun dismissChannelAction ()V
@@ -4920,8 +4920,8 @@ public final class io/getstream/chat/android/compose/viewmodel/channels/ChannelL
49204920
public final class io/getstream/chat/android/compose/viewmodel/channels/ChannelViewModelFactory : androidx/lifecycle/ViewModelProvider$Factory {
49214921
public static final field $stable I
49224922
public fun <init> ()V
4923-
public fun <init> (Lio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/models/querysort/QuerySorter;Lio/getstream/chat/android/models/FilterObject;ILjava/lang/Integer;Ljava/lang/Integer;Lio/getstream/chat/android/state/event/handler/chat/factory/ChatEventHandlerFactory;Z)V
4924-
public synthetic fun <init> (Lio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/models/querysort/QuerySorter;Lio/getstream/chat/android/models/FilterObject;ILjava/lang/Integer;Ljava/lang/Integer;Lio/getstream/chat/android/state/event/handler/chat/factory/ChatEventHandlerFactory;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
4923+
public fun <init> (Lio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/models/querysort/QuerySorter;Lio/getstream/chat/android/models/FilterObject;ILjava/lang/Integer;Ljava/lang/Integer;Lio/getstream/chat/android/state/event/handler/chat/factory/ChatEventHandlerFactory;ZLio/getstream/chat/android/models/querysort/QuerySorter;)V
4924+
public synthetic fun <init> (Lio/getstream/chat/android/client/ChatClient;Lio/getstream/chat/android/models/querysort/QuerySorter;Lio/getstream/chat/android/models/FilterObject;ILjava/lang/Integer;Ljava/lang/Integer;Lio/getstream/chat/android/state/event/handler/chat/factory/ChatEventHandlerFactory;ZLio/getstream/chat/android/models/querysort/QuerySorter;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
49254925
public fun create (Ljava/lang/Class;)Landroidx/lifecycle/ViewModel;
49264926
}
49274927

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ import kotlin.coroutines.cancellation.CancellationException
8989
* @param chatEventHandlerFactory The instance of [ChatEventHandlerFactory] used to create [ChatEventHandler].
9090
* @param searchDebounceMs The debounce time for search queries.
9191
* @param isDraftMessageEnabled If the draft message feature is enabled.
92+
* @param messageSearchSort Sorting for message search results. When `null`, the server-side default is used.
9293
* @param globalState A flow emitting the current [GlobalState].
9394
*/
9495
@OptIn(ExperimentalCoroutinesApi::class)
@@ -103,6 +104,7 @@ public class ChannelListViewModel(
103104
private val chatEventHandlerFactory: ChatEventHandlerFactory = ChatEventHandlerFactory(chatClient.clientState),
104105
searchDebounceMs: Long = SEARCH_DEBOUNCE_MS,
105106
private val isDraftMessageEnabled: Boolean = false,
107+
private val messageSearchSort: QuerySorter<Message>? = null,
106108
private val globalState: Flow<GlobalState> = chatClient.globalStateFlow,
107109
) : ViewModel() {
108110

@@ -369,10 +371,14 @@ public class ChannelListViewModel(
369371
): SearchMessageState {
370372
val limit = channelLimit
371373
val next = currentState.next
372-
logger.v { "[searchMessages] #$src; query: '${currentState.query}', next: $next, limit: $limit" }
374+
logger.v {
375+
"[searchMessages] #$src; query: '${currentState.query}', sort: $messageSearchSort, next: $next, " +
376+
"limit: $limit"
377+
}
373378
val result = chatClient.searchMessages(
374379
channelFilter = channelFilter,
375380
messageFilter = Filters.autocomplete("text", currentState.query),
381+
sort = messageSearchSort,
376382
limit = limit,
377383
next = next,
378384
).await()

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import androidx.lifecycle.ViewModelProvider
2121
import io.getstream.chat.android.client.ChatClient
2222
import io.getstream.chat.android.models.Channel
2323
import io.getstream.chat.android.models.FilterObject
24+
import io.getstream.chat.android.models.Message
2425
import io.getstream.chat.android.models.querysort.QuerySortByField
2526
import io.getstream.chat.android.models.querysort.QuerySorter
2627
import io.getstream.chat.android.state.event.handler.chat.ChatEventHandler
@@ -39,6 +40,7 @@ import io.getstream.chat.android.state.event.handler.chat.factory.ChatEventHandl
3940
* @param messageLimit How many messages are fetched for each channel item when loading channels.
4041
* When `null`, the server-side default is used.
4142
* @param chatEventHandlerFactory The instance of [ChatEventHandlerFactory] used to create [ChatEventHandler].
43+
* @param messageSearchSort Optional sorting for message search results. When `null`, the server-side default is used.
4244
*/
4345
public class ChannelViewModelFactory(
4446
private val chatClient: ChatClient = ChatClient.instance(),
@@ -49,6 +51,7 @@ public class ChannelViewModelFactory(
4951
private val messageLimit: Int? = null,
5052
private val chatEventHandlerFactory: ChatEventHandlerFactory = ChatEventHandlerFactory(chatClient.clientState),
5153
private val isDraftMessageEnabled: Boolean = false,
54+
private val messageSearchSort: QuerySorter<Message>? = null,
5255
) : ViewModelProvider.Factory {
5356

5457
/**
@@ -68,6 +71,7 @@ public class ChannelViewModelFactory(
6871
memberLimit = memberLimit,
6972
chatEventHandlerFactory = chatEventHandlerFactory,
7073
isDraftMessageEnabled = isDraftMessageEnabled,
74+
messageSearchSort = messageSearchSort,
7175
) as T
7276
}
7377
}

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

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import io.getstream.chat.android.models.ChannelMute
3030
import io.getstream.chat.android.models.FilterObject
3131
import io.getstream.chat.android.models.Filters
3232
import io.getstream.chat.android.models.InitializationState
33+
import io.getstream.chat.android.models.Message
3334
import io.getstream.chat.android.models.OrFilterObject
3435
import io.getstream.chat.android.models.SearchMessagesResult
3536
import io.getstream.chat.android.models.TypingEvent
@@ -462,6 +463,70 @@ internal class ChannelListViewModelTest {
462463
)
463464
}
464465

466+
@Test
467+
fun `Given no messageSearchSort When searching messages Should pass null sort to searchMessages`() =
468+
runTest {
469+
val chatClient: ChatClient = mock()
470+
val viewModel = Fixture(chatClient)
471+
.givenCurrentUser()
472+
.givenChannelsQuery()
473+
.givenChannelsState(
474+
channelsStateData = ChannelsStateData.Result(listOf(channel1, channel2)),
475+
loading = false,
476+
)
477+
.givenChannelMutes()
478+
.givenSearchMessagesResult(SearchMessagesResult())
479+
.givenRepositorySelectChannels()
480+
.get(this)
481+
482+
viewModel.setSearchQuery(SearchQuery.Messages("test"))
483+
advanceUntilIdle()
484+
485+
val sortCaptor = argumentCaptor<QuerySorter<Message>>()
486+
verify(chatClient).searchMessages(
487+
channelFilter = any(),
488+
messageFilter = any(),
489+
offset = anyOrNull(),
490+
limit = anyOrNull(),
491+
next = anyOrNull(),
492+
sort = sortCaptor.capture(),
493+
)
494+
assertNull(sortCaptor.firstValue)
495+
}
496+
497+
@Test
498+
fun `Given messageSearchSort is set When searching messages Should pass the sort to searchMessages`() =
499+
runTest {
500+
val chatClient: ChatClient = mock()
501+
val messageSearchSort = QuerySortByField.descByName<Message>("created_at")
502+
val viewModel = Fixture(chatClient)
503+
.givenCurrentUser()
504+
.givenChannelsQuery()
505+
.givenChannelsState(
506+
channelsStateData = ChannelsStateData.Result(listOf(channel1, channel2)),
507+
loading = false,
508+
)
509+
.givenChannelMutes()
510+
.givenMessageSearchSort(messageSearchSort)
511+
.givenSearchMessagesResult(SearchMessagesResult())
512+
.givenRepositorySelectChannels()
513+
.get(this)
514+
515+
viewModel.setSearchQuery(SearchQuery.Messages("test"))
516+
advanceUntilIdle()
517+
518+
val sortCaptor = argumentCaptor<QuerySorter<Message>>()
519+
verify(chatClient).searchMessages(
520+
channelFilter = any(),
521+
messageFilter = any(),
522+
offset = anyOrNull(),
523+
limit = anyOrNull(),
524+
next = anyOrNull(),
525+
sort = sortCaptor.capture(),
526+
)
527+
assertEquals(messageSearchSort, sortCaptor.firstValue)
528+
}
529+
465530
private class Fixture(
466531
private val chatClient: ChatClient = mock(),
467532
private val channelClient: ChannelClient = mock(),
@@ -472,6 +537,7 @@ internal class ChannelListViewModelTest {
472537
private val stateRegistry: StateRegistry = mock()
473538
private val globalState: GlobalState = mock()
474539
private val repositoryFacade: RepositoryFacade = mock()
540+
private var messageSearchSort: QuerySorter<Message>? = null
475541

476542
init {
477543
val statePlugin: StatePlugin = mock()
@@ -520,6 +586,10 @@ internal class ChannelListViewModelTest {
520586
whenever(chatClient.unmuteChannel(any(), any())) doReturn Unit.asCall()
521587
}
522588

589+
fun givenMessageSearchSort(sort: QuerySorter<Message>?) = apply {
590+
messageSearchSort = sort
591+
}
592+
523593
fun givenSearchMessagesResult(result: SearchMessagesResult) = apply {
524594
whenever(
525595
chatClient.searchMessages(any(), any(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull()),
@@ -556,6 +626,7 @@ internal class ChannelListViewModelTest {
556626
initialFilters = initialFilters,
557627
isDraftMessageEnabled = false,
558628
chatEventHandlerFactory = ChatEventHandlerFactory(clientState),
629+
messageSearchSort = messageSearchSort,
559630
globalState = MutableStateFlow(globalState),
560631
)
561632
testScope.advanceUntilIdle()

0 commit comments

Comments
 (0)