From f3eca9971265e4484a5deceb39a2c6261928ad83 Mon Sep 17 00:00:00 2001 From: rapterjet2004 Date: Wed, 13 May 2026 10:41:20 -0500 Subject: [PATCH 1/3] Intuitive search ordering of filtered data Signed-off-by: rapterjet2004 --- .../viewmodels/ConversationsListViewModel.kt | 93 ++++++++++++++++--- 1 file changed, 78 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/conversationlist/viewmodels/ConversationsListViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationlist/viewmodels/ConversationsListViewModel.kt index b90f90a195..1b010a904e 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationlist/viewmodels/ConversationsListViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationlist/viewmodels/ConversationsListViewModel.kt @@ -8,6 +8,8 @@ package com.nextcloud.talk.conversationlist.viewmodels import android.content.Context import android.util.Log +import androidx.compose.ui.text.intl.Locale +import androidx.compose.ui.text.toLowerCase import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -330,31 +332,66 @@ class ConversationsListViewModel @Inject constructor( if (localConvs.isNotEmpty()) { entries.add(ConversationListEntry.Header(conversationsTitle)) - localConvs.forEach { entries.add(ConversationListEntry.ConversationEntry(it)) } + + val pattern = """\b${Regex.escape(filter)}\b""".toRegex(RegexOption.IGNORE_CASE) + + processOrderAndAdd( + localConvs, + firstPredicate = { it.name.trim().equals(filter, ignoreCase = true) }, + secondPredicate = { it.name.contains(pattern) }, + addAction = { + entries.add(ConversationListEntry.ConversationEntry(it)) + } + ) } if (openConvs.isNotEmpty()) { entries.add(ConversationListEntry.Header(openConversationsTitle)) - openConvs.forEach { conv -> - entries.add( - ConversationListEntry.ConversationEntry( - ConversationModel.mapToConversationModel(conv, currentUser) + + val pattern = """\b${Regex.escape(filter)}\b""".toRegex(RegexOption.IGNORE_CASE) + + processOrderAndAdd( + openConvs, + firstPredicate = { it.name.trim().equals(filter, ignoreCase = true) }, + secondPredicate = { it.name.contains(pattern) }, + addAction = { conv -> + entries.add( + ConversationListEntry.ConversationEntry( + ConversationModel.mapToConversationModel(conv, currentUser) + ) ) - ) - } + } + ) } if (contacts.isNotEmpty()) { entries.add(ConversationListEntry.Header(usersTitle)) - contacts.forEach { autocompleteUser -> - val participant = Participant() - participant.actorId = autocompleteUser.id - participant.actorType = actorTypeConverter.getFromString(autocompleteUser.source) - participant.displayName = autocompleteUser.label - entries.add(ConversationListEntry.ContactEntry(participant)) - } + + val pattern = """\b${Regex.escape(filter)}\b""".toRegex(RegexOption.IGNORE_CASE) + + processOrderAndAdd( + contacts, + firstPredicate = { it.label?.trim().equals(filter, ignoreCase = true) }, + secondPredicate = { it.label?.contains(pattern) ?: false }, + addAction = { autocompleteUser -> + val participant = Participant() + participant.actorId = autocompleteUser.id + participant.actorType = actorTypeConverter.getFromString(autocompleteUser.source) + participant.displayName = autocompleteUser.label + entries.add(ConversationListEntry.ContactEntry(participant)) + } + ) } if (messages.isNotEmpty()) { entries.add(ConversationListEntry.Header(messagesTitle)) - messages.forEach { msg -> entries.add(ConversationListEntry.MessageResultEntry(msg)) } + + val pattern = """\b${Regex.escape(filter)}\b""".toRegex(RegexOption.IGNORE_CASE) + + processOrderAndAdd( + messages, + firstPredicate = { + it.messageExcerpt.toLowerCase(Locale.current).contains(pattern) + }, + addAction = { msg -> entries.add(ConversationListEntry.MessageResultEntry(msg)) } + ) } if (hasMore) entries.add(ConversationListEntry.LoadMore) @@ -366,6 +403,32 @@ class ConversationsListViewModel @Inject constructor( } } + private fun processOrderAndAdd( + list: List, + firstPredicate: (T) -> Boolean, + secondPredicate: ((T) -> Boolean)? = null, + addAction: (T) -> Unit + ) { + val predicateOneAndInverse = list.split(firstPredicate) + predicateOneAndInverse.first.forEach(addAction) + + secondPredicate?.let { + val predicateTwoAndInverse = predicateOneAndInverse.second.split(secondPredicate) + predicateTwoAndInverse.first.forEach(addAction) + + predicateTwoAndInverse.second.forEach(addAction) + } ?: { + predicateOneAndInverse.second.forEach(addAction) + } + } + + private inline fun Iterable.split(predicate: (T) -> Boolean): Pair, List> { + return Pair( + this.filter(predicate), + this.filterNot(predicate) + ) + } + private fun getMessagesFlow(search: String): Flow = flow { emit(searchHelper.startMessageSearch(search)) From 56a97f8ce0ca4c610ec640fdf2b47baaed1d4095 Mon Sep 17 00:00:00 2001 From: rapterjet2004 Date: Thu, 14 May 2026 13:40:35 -0500 Subject: [PATCH 2/3] linter + refactoring Signed-off-by: rapterjet2004 --- .../viewmodels/ConversationsListViewModel.kt | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/conversationlist/viewmodels/ConversationsListViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationlist/viewmodels/ConversationsListViewModel.kt index 1b010a904e..71014dda6a 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationlist/viewmodels/ConversationsListViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationlist/viewmodels/ConversationsListViewModel.kt @@ -365,7 +365,7 @@ class ConversationsListViewModel @Inject constructor( if (contacts.isNotEmpty()) { entries.add(ConversationListEntry.Header(usersTitle)) - val pattern = """\b${Regex.escape(filter)}\b""".toRegex(RegexOption.IGNORE_CASE) + val pattern = """\s*${Regex.escape(filter)}\s*""".toRegex(RegexOption.IGNORE_CASE) processOrderAndAdd( contacts, @@ -403,31 +403,35 @@ class ConversationsListViewModel @Inject constructor( } } + // I made this function to order any arbitrary list according to + // [meets predicate 1][meets predicate 2]?[the rest] + // and applying a unit function upon the result of these reorderings private fun processOrderAndAdd( list: List, firstPredicate: (T) -> Boolean, secondPredicate: ((T) -> Boolean)? = null, addAction: (T) -> Unit ) { - val predicateOneAndInverse = list.split(firstPredicate) - predicateOneAndInverse.first.forEach(addAction) + val result = mutableListOf() - secondPredicate?.let { - val predicateTwoAndInverse = predicateOneAndInverse.second.split(secondPredicate) - predicateTwoAndInverse.first.forEach(addAction) + val (firstPredicate, remainingFirst) = list.split(firstPredicate) + result.addAll(firstPredicate) - predicateTwoAndInverse.second.forEach(addAction) + secondPredicate?.let { + val (secondPredicate, remainingSecond) = remainingFirst.split(secondPredicate) + result.addAll(secondPredicate + remainingSecond) } ?: { - predicateOneAndInverse.second.forEach(addAction) + result.addAll(remainingFirst) } + + result.forEach(addAction) } - private inline fun Iterable.split(predicate: (T) -> Boolean): Pair, List> { - return Pair( + private inline fun Iterable.split(predicate: (T) -> Boolean): Pair, List> = + Pair( this.filter(predicate), this.filterNot(predicate) ) - } private fun getMessagesFlow(search: String): Flow = flow { From f7976a00b4672f6207337944f90ec7bbc46d9b2d Mon Sep 17 00:00:00 2001 From: rapterjet2004 Date: Fri, 15 May 2026 11:26:41 -0500 Subject: [PATCH 3/3] linter + refactoring Signed-off-by: rapterjet2004 --- .../viewmodels/ConversationsListViewModel.kt | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/conversationlist/viewmodels/ConversationsListViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationlist/viewmodels/ConversationsListViewModel.kt index 71014dda6a..a54766b7e8 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationlist/viewmodels/ConversationsListViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationlist/viewmodels/ConversationsListViewModel.kt @@ -6,10 +6,9 @@ */ package com.nextcloud.talk.conversationlist.viewmodels +import android.annotation.SuppressLint import android.content.Context import android.util.Log -import androidx.compose.ui.text.intl.Locale -import androidx.compose.ui.text.toLowerCase import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -302,6 +301,7 @@ class ConversationsListViewModel @Inject constructor( } } + @SuppressLint("DefaultLocale") @Suppress("LongMethod") fun getSearchQuery(context: Context, filter: String) { // Rotation / display-off guard: if the composition restarts (config change) the @@ -388,7 +388,7 @@ class ConversationsListViewModel @Inject constructor( processOrderAndAdd( messages, firstPredicate = { - it.messageExcerpt.toLowerCase(Locale.current).contains(pattern) + it.messageExcerpt.contains(pattern) }, addAction = { msg -> entries.add(ConversationListEntry.MessageResultEntry(msg)) } ) @@ -403,7 +403,7 @@ class ConversationsListViewModel @Inject constructor( } } - // I made this function to order any arbitrary list according to + // This function orders any arbitrary list according to // [meets predicate 1][meets predicate 2]?[the rest] // and applying a unit function upon the result of these reorderings private fun processOrderAndAdd( @@ -414,25 +414,19 @@ class ConversationsListViewModel @Inject constructor( ) { val result = mutableListOf() - val (firstPredicate, remainingFirst) = list.split(firstPredicate) + val (firstPredicate, remainingFirst) = list.partition(firstPredicate) result.addAll(firstPredicate) - secondPredicate?.let { - val (secondPredicate, remainingSecond) = remainingFirst.split(secondPredicate) + if (secondPredicate != null) { + val (secondPredicate, remainingSecond) = remainingFirst.partition(secondPredicate) result.addAll(secondPredicate + remainingSecond) - } ?: { + } else { result.addAll(remainingFirst) } result.forEach(addAction) } - private inline fun Iterable.split(predicate: (T) -> Boolean): Pair, List> = - Pair( - this.filter(predicate), - this.filterNot(predicate) - ) - private fun getMessagesFlow(search: String): Flow = flow { emit(searchHelper.startMessageSearch(search))