From 565bcfec9c018fa91b64ed295a1ebcaae070fbf8 Mon Sep 17 00:00:00 2001
From: ialokim <ialokim@mailbox.org>
Date: Sat, 30 Oct 2021 00:17:15 +0200
Subject: [PATCH] unify filter approach in view models, fix selection of
 contacts after filtering the list

---
 .../briar/desktop/contact/ContactList.kt      | 10 ++--
 .../desktop/contact/ContactListViewModel.kt   | 51 +++++--------------
 .../desktop/contact/ContactsViewModel.kt      | 25 ++++++---
 .../conversation/PrivateMessageView.kt        |  2 +-
 .../introduction/IntroductionViewModel.kt     |  5 +-
 .../briarproject/briar/desktop/ui/BriarUi.kt  |  1 -
 6 files changed, 43 insertions(+), 51 deletions(-)

diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt
index 4e769de56a..7fa969a5b3 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt
@@ -6,7 +6,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.lazy.items
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Scaffold
 import androidx.compose.runtime.Composable
@@ -45,8 +45,12 @@ fun ContactList(
         },
         content = {
             LazyColumn {
-                itemsIndexed(viewModel.contactList) { index, contactItem ->
-                    ContactCard(contactItem, { viewModel.selectContact(index) }, viewModel.isSelected(index))
+                items(viewModel.contactList) { contactItem ->
+                    ContactCard(
+                        contactItem,
+                        { viewModel.selectContact(contactItem.contact) },
+                        viewModel.isSelected(contactItem.contact)
+                    )
                 }
             }
         },
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactListViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactListViewModel.kt
index ef763a604c..ee4a7f8c82 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactListViewModel.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactListViewModel.kt
@@ -1,10 +1,9 @@
 package org.briarproject.briar.desktop.contact
 
 import androidx.compose.runtime.State
-import androidx.compose.runtime.mutableStateListOf
 import androidx.compose.runtime.mutableStateOf
 import org.briarproject.bramble.api.connection.ConnectionRegistry
-import org.briarproject.bramble.api.contact.ContactId
+import org.briarproject.bramble.api.contact.Contact
 import org.briarproject.bramble.api.contact.ContactManager
 import org.briarproject.bramble.api.contact.event.ContactAliasChangedEvent
 import org.briarproject.bramble.api.event.Event
@@ -32,44 +31,32 @@ constructor(
         eventBus.addListener(this)
     }
 
-    private val _filteredContactList = mutableStateListOf<ContactItem>()
     private val _filterBy = mutableStateOf("")
-    private var _selectedContactIndex = -1
-    private val _selectedContact = mutableStateOf<ContactItem?>(null)
+    private val _selectedContact = mutableStateOf<Contact?>(null)
 
-    override val contactList: List<ContactItem> = _filteredContactList
     val filterBy: State<String> = _filterBy
-    val selectedContact: State<ContactItem?> = _selectedContact
+    val selectedContact: State<Contact?> = _selectedContact
 
-    override fun loadContacts() {
-        super.loadContacts()
-        updateFilteredList()
+    fun selectContact(contact: Contact) {
+        _selectedContact.value = contact
     }
 
-    fun selectContact(index: Int) {
-        _selectedContactIndex = index
-        _selectedContact.value = _filteredContactList[index]
-    }
+    fun isSelected(contact: Contact) = _selectedContact.value?.id == contact.id
 
-    fun isSelected(index: Int) = _selectedContactIndex == index
-
-    private fun updateFilteredList() {
-        _filteredContactList.apply {
-            clear()
-            addAll(
-                _contactList.filter {
-                    // todo: also filter on alias?
-                    it.contact.author.name.lowercase().contains(_filterBy.value)
-                }
-            )
-        }
-    }
+    override fun filterContact(contact: Contact) =
+        // todo: also filter on alias
+        contact.author.name.contains(_filterBy.value, ignoreCase = true)
 
     fun setFilterBy(filter: String) {
         _filterBy.value = filter
         updateFilteredList()
     }
 
+    override fun updateFilteredList() {
+        super.updateFilteredList()
+        _selectedContact.value?.let { if (!filterContact(it)) _selectedContact.value = null }
+    }
+
     override fun eventOccurred(e: Event?) {
         super.eventOccurred(e)
         when (e) {
@@ -83,14 +70,4 @@ constructor(
             }
         }
     }
-
-    override fun updateItem(contactId: ContactId, update: (ContactItem) -> ContactItem) {
-        super.updateItem(contactId, update)
-        updateFilteredList()
-    }
-
-    override fun removeItem(contactId: ContactId) {
-        super.removeItem(contactId)
-        updateFilteredList()
-    }
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt
index ffd994358e..ccb10ccd33 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt
@@ -1,5 +1,6 @@
 package org.briarproject.briar.desktop.contact
 
+import androidx.compose.runtime.mutableStateListOf
 import org.briarproject.bramble.api.connection.ConnectionRegistry
 import org.briarproject.bramble.api.contact.Contact
 import org.briarproject.bramble.api.contact.ContactId
@@ -25,17 +26,18 @@ abstract class ContactsViewModel(
         private val LOG = Logger.getLogger(ContactsViewModel::class.java.name)
     }
 
-    protected val _contactList = mutableListOf<ContactItem>()
+    private val _fullContactList = mutableListOf<ContactItem>()
+    private val _filteredContactList = mutableStateListOf<ContactItem>()
 
-    abstract val contactList: List<ContactItem>
+    val contactList: List<ContactItem> = _filteredContactList
 
     protected open fun filterContact(contact: Contact) = true
 
     open fun loadContacts() {
-        _contactList.apply {
+        _fullContactList.apply {
             clear()
             addAll(
-                contactManager.contacts.filter(::filterContact).map { contact ->
+                contactManager.contacts.map { contact ->
                     ContactItem(
                         contact,
                         connectionRegistry.isConnected(contact.id),
@@ -44,6 +46,15 @@ abstract class ContactsViewModel(
                 }
             )
         }
+        updateFilteredList()
+    }
+
+    // todo: when migrated to StateFlow, this could be done implicitly instead
+    protected open fun updateFilteredList() {
+        _filteredContactList.apply {
+            clear()
+            addAll(_fullContactList.filter { filterContact(it.contact) })
+        }
     }
 
     override fun eventOccurred(e: Event?) {
@@ -68,10 +79,12 @@ abstract class ContactsViewModel(
     }
 
     protected open fun updateItem(contactId: ContactId, update: (ContactItem) -> ContactItem) {
-        _contactList.replaceFirst({ it.contact.id == contactId }, update)
+        _fullContactList.replaceFirst({ it.contact.id == contactId }, update)
+        updateFilteredList()
     }
 
     protected open fun removeItem(contactId: ContactId) {
-        _contactList.removeFirst { it.contact.id == contactId }
+        _fullContactList.removeFirst { it.contact.id == contactId }
+        updateFilteredList()
     }
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageView.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageView.kt
index ae06e0135a..b9dc764418 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageView.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageView.kt
@@ -25,7 +25,7 @@ fun PrivateMessageView(
         Column(modifier = Modifier.weight(1f).fillMaxHeight()) {
             contactListViewModel.selectedContact.value?.also { selectedContact ->
                 Conversation(
-                    selectedContact.contact,
+                    selectedContact,
                     introductionViewModel
                 )
             } ?: UiPlaceholder()
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/introduction/IntroductionViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/introduction/IntroductionViewModel.kt
index 3b2e3f393c..fda0aa64dc 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/introduction/IntroductionViewModel.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/introduction/IntroductionViewModel.kt
@@ -7,7 +7,6 @@ import org.briarproject.bramble.api.contact.Contact
 import org.briarproject.bramble.api.contact.ContactManager
 import org.briarproject.bramble.api.event.EventBus
 import org.briarproject.briar.api.conversation.ConversationManager
-import org.briarproject.briar.desktop.contact.ContactItem
 import org.briarproject.briar.desktop.contact.ContactsViewModel
 import java.util.logging.Logger
 import javax.inject.Inject
@@ -35,7 +34,6 @@ constructor(
     private val _secondScreen = mutableStateOf(false)
     private val _introductionMessage = mutableStateOf("")
 
-    override val contactList: List<ContactItem> = _contactList
     val firstContact: State<Contact?> = _firstContact
     val secondContact: State<Contact?> = _secondContact
     val secondScreen: State<Boolean> = _secondScreen
@@ -54,6 +52,7 @@ constructor(
 
     fun backToFirstScreen() {
         _secondScreen.value = false
+        _introductionMessage.value = ""
     }
 
     fun setIntroductionMessage(msg: String) {
@@ -61,6 +60,6 @@ constructor(
     }
 
     override fun filterContact(contact: Contact): Boolean {
-        return _firstContact.value!!.id != contact.id
+        return _firstContact.value?.id != contact.id
     }
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt b/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt
index afbda2623f..f621dab9eb 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt
@@ -104,7 +104,6 @@ constructor(
                     when (screenState) {
                         Screen.REGISTRATION ->
                             Registration(registrationViewModel) {
-                                contactListViewModel.loadContacts()
                                 screenState = Screen.MAIN
                             }
                         Screen.LOGIN ->
-- 
GitLab