@@ -154,9 +154,15 @@ import io.reactivex.android.schedulers.AndroidSchedulers
154154import io.reactivex.disposables.Disposable
155155import io.reactivex.schedulers.Schedulers
156156import io.reactivex.subjects.BehaviorSubject
157+ import kotlinx.coroutines.Dispatchers
158+ import kotlinx.coroutines.async
159+ import kotlinx.coroutines.awaitAll
157160import kotlinx.coroutines.flow.collect
158161import kotlinx.coroutines.flow.onEach
159162import kotlinx.coroutines.launch
163+ import kotlinx.coroutines.sync.Mutex
164+ import kotlinx.coroutines.sync.withLock
165+ import kotlinx.coroutines.withContext
160166import org.apache.commons.lang3.builder.CompareToBuilder
161167import org.greenrobot.eventbus.Subscribe
162168import org.greenrobot.eventbus.ThreadMode
@@ -215,6 +221,7 @@ class ConversationsListActivity :
215221 private var conversationItemsWithHeader: MutableList <AbstractFlexibleItem <* >> = ArrayList ()
216222 private var searchableConversationItems: MutableList <AbstractFlexibleItem <* >> = ArrayList ()
217223 private var filterableConversationItems: MutableList <AbstractFlexibleItem <* >> = ArrayList ()
224+ private val mutex = Mutex ()
218225 private var nearFutureEventConversationItems: MutableList <AbstractFlexibleItem <* >> = ArrayList ()
219226 private var searchItem: MenuItem ? = null
220227 private var chooseAccountItem: MenuItem ? = null
@@ -451,8 +458,8 @@ class ConversationsListActivity :
451458 when (state) {
452459 is ConversationsListViewModel .OpenConversationsUiState .Success -> {
453460 val openConversationItems: MutableList <AbstractFlexibleItem <* >> = ArrayList ()
461+ val headerTitle = resources!! .getString(R .string.openConversations)
454462 for (conversation in state.conversations) {
455- val headerTitle = resources!! .getString(R .string.openConversations)
456463 var genericTextHeaderItem: GenericTextHeaderItem
457464 if (! callHeaderItems.containsKey(headerTitle)) {
458465 genericTextHeaderItem = GenericTextHeaderItem (headerTitle, viewThemeUtils)
@@ -467,7 +474,15 @@ class ConversationsListActivity :
467474 )
468475 openConversationItems.add(conversationItem)
469476 }
470- searchableConversationItems.addAll(openConversationItems)
477+
478+ mutex.withLock {
479+ // Filters out all old open conversation items from the previous query
480+ searchableConversationItems = searchableConversationItems.filter {
481+ ! (it is ConversationItem && it.header == callHeaderItems[headerTitle])
482+ }.toMutableList()
483+
484+ searchableConversationItems.addAll(openConversationItems)
485+ }
471486 }
472487 is ConversationsListViewModel .OpenConversationsUiState .Error -> {
473488 handleHttpExceptions(state.exception)
@@ -541,13 +556,14 @@ class ConversationsListActivity :
541556 userItems.add(contactItem)
542557 }
543558
544- val list = searchableConversationItems.filter {
545- it !is ContactItem
546- }.toMutableList()
559+ mutex.withLock {
560+ // Filters out all old user items from the previous query
561+ searchableConversationItems = searchableConversationItems.filter {
562+ it !is ContactItem
563+ }.toMutableList()
547564
548- list.addAll(userItems)
549-
550- searchableConversationItems = list
565+ searchableConversationItems.addAll(userItems)
566+ }
551567 }
552568
553569 else -> {}
@@ -659,8 +675,6 @@ class ConversationsListActivity :
659675 }
660676
661677 Handler ().postDelayed({ checkToShowUnreadBubble() }, UNREAD_BUBBLE_DELAY .toLong())
662-
663- fetchOpenConversations()
664678 }
665679
666680 fun applyFilter () {
@@ -989,9 +1003,9 @@ class ConversationsListActivity :
9891003 override fun onMenuItemActionExpand (item : MenuItem ): Boolean {
9901004 initSearchDisposable()
9911005 adapter?.setHeadersShown(true )
992- if (! hasFilterEnabled()) filterableConversationItems = searchableConversationItems
993- adapter!! .updateDataSet(filterableConversationItems, false )
9941006 adapter!! .showAllHeaders()
1007+ if (! hasFilterEnabled()) filterableConversationItems = searchableConversationItems
1008+ // adapter!!.updateDataSet(filterableConversationItems, false)
9951009 binding.swipeRefreshLayoutView.isEnabled = false
9961010 searchBehaviorSubject.onNext(true )
9971011 return true
@@ -1232,14 +1246,14 @@ class ConversationsListActivity :
12321246 }
12331247 }
12341248
1235- private fun fetchOpenConversations () {
1249+ private suspend fun fetchOpenConversations (searchTerm : String ) {
12361250 searchableConversationItems.clear()
12371251 searchableConversationItems.addAll(conversationItemsWithHeader)
1238- conversationsListViewModel.fetchOpenConversations()
1252+ conversationsListViewModel.fetchOpenConversations(searchTerm )
12391253 }
12401254
1241- private fun fetchUsers (query : String = "") {
1242- contactsViewModel.getContactsFromSearchParams (query)
1255+ private suspend fun fetchUsers (query : String = "") {
1256+ contactsViewModel.getBlockingContactsFromSearchParams (query)
12431257 }
12441258
12451259 private fun handleHttpExceptions (throwable : Throwable ) {
@@ -1444,26 +1458,64 @@ class ConversationsListActivity :
14441458 if (filter!! .length >= SEARCH_MIN_CHARS ) {
14451459 clearMessageSearchResults()
14461460 binding.noArchivedConversationLayout.visibility = View .GONE
1461+ binding.swipeRefreshLayoutView.isRefreshing = true
14471462
1448- fetchUsers(filter)
1463+ lifecycleScope.launch {
1464+ // gets users, updates collector async, which adds them to searchableConversationItems
1465+ val deferred1 = async {
1466+ fetchUsers(filter)
1467+ }
14491468
1450- if (hasFilterEnabled()) {
1451- adapter?.updateDataSet(conversationItems)
1452- adapter?.setFilter(filter)
1453- adapter?.filterItems()
1454- adapter?.updateDataSet(filterableConversationItems)
1455- } else {
1456- adapter?.updateDataSet(searchableConversationItems)
1457- adapter?.setFilter(filter)
1458- adapter?.filterItems()
1459- }
1469+ // gets open conversations, updates collector async, which adds them to searchableConversationItems
1470+ val deferred2 = async {
1471+ fetchOpenConversations(filter)
1472+ }
14601473
1461- if (hasSpreedFeatureCapability(
1462- currentUser?.capabilities?.spreedCapability,
1463- SpreedFeatures .UNIFIED_SEARCH
1464- )
1465- ) {
1466- startMessageSearch(filter)
1474+ awaitAll(deferred1, deferred2)
1475+
1476+ // Waits until both work in collectors is over, to avoid data races
1477+ mutex.withLock {
1478+ if (hasFilterEnabled()) {
1479+ val headerTitle = resources!! .getString(R .string.openConversations)
1480+
1481+ fun AbstractFlexibleItem <* >.isRegularConversationItem () =
1482+ this is ConversationItem && this .header != callHeaderItems[headerTitle]
1483+
1484+ // Only keeps the Open Conversations, Users
1485+ val list = searchableConversationItems.filter {
1486+ ! it.isRegularConversationItem()
1487+ }.toMutableList()
1488+
1489+ // Only keeps the conversation items with the applied Nextcloud filter [mention/archive/unread]
1490+ filterableConversationItems = filterableConversationItems.filter {
1491+ it.isRegularConversationItem()
1492+ }.toMutableList()
1493+
1494+ filterableConversationItems.addAll(list)
1495+ adapter?.updateDataSet(filterableConversationItems)
1496+
1497+ adapter?.setFilter(filter)
1498+ adapter?.filterItems()
1499+ } else {
1500+ // Conversation Items without Nextcloud filter + Open conversations/users
1501+ adapter?.updateDataSet(searchableConversationItems)
1502+ adapter?.setFilter(filter)
1503+ adapter?.filterItems()
1504+ }
1505+ }
1506+
1507+ if (hasSpreedFeatureCapability(
1508+ currentUser?.capabilities?.spreedCapability,
1509+ SpreedFeatures .UNIFIED_SEARCH
1510+ )
1511+ ) {
1512+ // gets messages async, adds them to the adapter, but NOT the searchableConversationItems
1513+ startMessageSearch(filter)
1514+ }
1515+
1516+ withContext(Dispatchers .Main ) {
1517+ binding.swipeRefreshLayoutView.isRefreshing = false
1518+ }
14671519 }
14681520 } else {
14691521 resetSearchResults()
@@ -1512,7 +1564,10 @@ class ConversationsListActivity :
15121564 binding.swipeRefreshLayoutView.isRefreshing = true
15131565 val observable = searchHelper!! .loadMore()
15141566 observable?.observeOn(AndroidSchedulers .mainThread())
1515- ?.subscribe({ results: MessageSearchResults -> onMessageSearchResult(results) }) { throwable: Throwable ->
1567+ ?.subscribe({ results: MessageSearchResults ->
1568+ onMessageSearchResult(results)
1569+ binding.swipeRefreshLayoutView.isRefreshing = false
1570+ }) { throwable: Throwable ->
15161571 onMessageSearchError(
15171572 throwable
15181573 )
@@ -2202,10 +2257,8 @@ class ConversationsListActivity :
22022257 }
22032258
22042259 adapter?.addItems(Int .MAX_VALUE , adapterItems)
2205- binding.recyclerView.scrollToPosition(0 )
22062260 }
22072261 }
2208- binding.swipeRefreshLayoutView.isRefreshing = false
22092262 }
22102263
22112264 private fun onMessageSearchError (throwable : Throwable ) {
0 commit comments