From fa17b3c702f19b380616331636e4d78b49e65589 Mon Sep 17 00:00:00 2001
From: ialokim <ialokim@mailbox.org>
Date: Tue, 30 May 2023 23:34:50 +0200
Subject: [PATCH] add option to mark entire group conversation as read

---
 .../briar/desktop/forum/ForumScreen.kt        |  4 ++--
 .../ForumConversationViewModel.kt             |  4 ++--
 .../forum/conversation/ForumDropdownMenu.kt   | 12 ++++++++++
 .../forum/sharing/ForumSharingViewModel.kt    |  2 +-
 .../privategroup/PrivateGroupScreen.kt        |  3 ++-
 .../PrivateGroupConversationViewModel.kt      |  4 ++--
 .../conversation/PrivateGroupDropdownMenu.kt  | 12 ++++++++++
 .../ThreadedConversationViewModel.kt          | 22 +++++++++----------
 .../ThreadedGroupConversationScreen.kt        |  5 ++++-
 .../conversation/ThreadedGroupDropdownMenu.kt |  1 +
 .../resources/strings/BriarDesktop.properties |  1 +
 11 files changed, 50 insertions(+), 20 deletions(-)

diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/ForumScreen.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/ForumScreen.kt
index ec14f6f623..3eaa778c33 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/ForumScreen.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/ForumScreen.kt
@@ -30,8 +30,8 @@ fun ForumScreen(
 ) = ThreadedGroupScreen(
     strings = ForumStrings,
     viewModel = viewModel,
-    dropdownMenu = { sharingViewModel, expanded, onClose, onLeaveForumClick ->
+    dropdownMenu = { sharingViewModel, expanded, onClose, onMarkReadClick, onLeaveForumClick ->
         val forumSharingViewModel = sharingViewModel as ForumSharingViewModel
-        ForumDropdownMenu(forumSharingViewModel, expanded, onClose, onLeaveForumClick)
+        ForumDropdownMenu(forumSharingViewModel, expanded, onClose, onMarkReadClick, onLeaveForumClick)
     }
 )
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/conversation/ForumConversationViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/conversation/ForumConversationViewModel.kt
index 4d7e26cf4f..429db62a02 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/conversation/ForumConversationViewModel.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/conversation/ForumConversationViewModel.kt
@@ -112,8 +112,8 @@ class ForumConversationViewModel @Inject constructor(
     }
 
     @DatabaseExecutor
-    override fun markThreadItemRead(groupId: GroupId, id: MessageId) =
-        forumManager.setReadFlag(groupId, id, true)
+    override fun markThreadItemRead(txn: Transaction, groupId: GroupId, id: MessageId) =
+        forumManager.setReadFlag(txn, groupId, id, true)
 
     @UiExecutor
     override fun deleteGroup() {
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/conversation/ForumDropdownMenu.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/conversation/ForumDropdownMenu.kt
index 871d3b4b03..19b06b6ddf 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/conversation/ForumDropdownMenu.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/conversation/ForumDropdownMenu.kt
@@ -35,6 +35,7 @@ fun ForumDropdownMenu(
     forumSharingViewModel: ForumSharingViewModel,
     expanded: Boolean,
     onClose: () -> Unit,
+    onMarkReadClick: () -> Unit,
     onLeaveForumClick: () -> Unit,
 ) = DropdownMenu(
     expanded = expanded,
@@ -74,6 +75,17 @@ fun ForumDropdownMenu(
             style = MaterialTheme.typography.body2,
         )
     }
+    DropdownMenuItem(
+        onClick = {
+            onClose()
+            onMarkReadClick()
+        }
+    ) {
+        Text(
+            i18n("group.mark.read"),
+            style = MaterialTheme.typography.body2,
+        )
+    }
     DropdownMenuItem(
         onClick = {
             onClose()
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/sharing/ForumSharingViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/sharing/ForumSharingViewModel.kt
index d47f5e7b7c..773c13f4a5 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/sharing/ForumSharingViewModel.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/sharing/ForumSharingViewModel.kt
@@ -129,7 +129,7 @@ class ForumSharingViewModel @Inject constructor(
         }
     }
 
-    fun loadSharingStatus(txn: Transaction, groupId: GroupId) {
+    private fun loadSharingStatus(txn: Transaction, groupId: GroupId) {
         val map = contactManager.getContacts(txn).associate { contact ->
             contact.id to forumSharingManager.getSharingStatus(txn, groupId, contact)
         }
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupScreen.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupScreen.kt
index 48d13acb0b..9a94982bce 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupScreen.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupScreen.kt
@@ -43,12 +43,13 @@ fun PrivateGroupScreen(
     ThreadedGroupScreen(
         strings = PrivateGroupStrings,
         viewModel = viewModel,
-        dropdownMenu = { sharingViewModel, expanded, onClose, onLeaveOrDissolvePrivateGroupClick ->
+        dropdownMenu = { sharingViewModel, expanded, onClose, onMarkReadClick, onLeaveOrDissolvePrivateGroupClick ->
             val privateGroupSharingViewModel = sharingViewModel as PrivateGroupSharingViewModel
             PrivateGroupDropdownMenu(
                 privateGroupSharingViewModel = privateGroupSharingViewModel,
                 expanded = expanded,
                 onClose = onClose,
+                onMarkReadClick = onMarkReadClick,
                 onLeaveOrDissolvePrivateGroupClick = onLeaveOrDissolvePrivateGroupClick
             )
         },
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/conversation/PrivateGroupConversationViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/conversation/PrivateGroupConversationViewModel.kt
index 81695874c4..ab44963e73 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/conversation/PrivateGroupConversationViewModel.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/conversation/PrivateGroupConversationViewModel.kt
@@ -135,8 +135,8 @@ class PrivateGroupConversationViewModel @Inject constructor(
     }
 
     @DatabaseExecutor
-    override fun markThreadItemRead(groupId: GroupId, id: MessageId) =
-        privateGroupManager.setReadFlag(groupId, id, true)
+    override fun markThreadItemRead(txn: Transaction, groupId: GroupId, id: MessageId) =
+        privateGroupManager.setReadFlag(txn, groupId, id, true)
 
     @UiExecutor
     override fun deleteGroup() {
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/conversation/PrivateGroupDropdownMenu.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/conversation/PrivateGroupDropdownMenu.kt
index a23d807ab2..efb37e4216 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/conversation/PrivateGroupDropdownMenu.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/conversation/PrivateGroupDropdownMenu.kt
@@ -35,6 +35,7 @@ fun PrivateGroupDropdownMenu(
     privateGroupSharingViewModel: PrivateGroupSharingViewModel,
     expanded: Boolean,
     onClose: () -> Unit,
+    onMarkReadClick: () -> Unit,
     onLeaveOrDissolvePrivateGroupClick: () -> Unit,
 ) = DropdownMenu(
     expanded = expanded,
@@ -76,6 +77,17 @@ fun PrivateGroupDropdownMenu(
             )
         }
     }
+    DropdownMenuItem(
+        onClick = {
+            onClose()
+            onMarkReadClick()
+        }
+    ) {
+        Text(
+            i18n("group.mark.read"),
+            style = MaterialTheme.typography.body2,
+        )
+    }
     DropdownMenuItem(
         onClick = {
             onClose()
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/conversation/ThreadedConversationViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/conversation/ThreadedConversationViewModel.kt
index 4d8fe22105..8190c5fd61 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/conversation/ThreadedConversationViewModel.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/conversation/ThreadedConversationViewModel.kt
@@ -112,13 +112,13 @@ abstract class ThreadedConversationViewModel(
     }
 
     @DatabaseExecutor
-    abstract fun markThreadItemRead(groupId: GroupId, id: MessageId)
+    abstract fun markThreadItemRead(txn: Transaction, groupId: GroupId, id: MessageId)
 
     @UiExecutor
-    fun markThreadItemsRead(ids: List<MessageId>) {
+    fun markThreadItemsRead(ids: List<MessageId>? = null) {
         // TODO messageTree.get(id) would be nice, but not in briar-core
         val readIds = (state.value as? Loaded)?.posts?.filter { item ->
-            !item.isRead && ids.contains(item.id)
+            ids?.contains(item.id) ?: true && !item.isRead
         }?.map { item ->
             item.isRead = true
             item.id
@@ -126,17 +126,17 @@ abstract class ThreadedConversationViewModel(
 
         val groupId = _threadedGroupItem.value?.id
         if (readIds.isNotEmpty() && groupId != null) {
-            runOnDbThread {
+            runOnDbThreadWithTransaction(false) { txn ->
                 readIds.forEach { id ->
-                    markThreadItemRead(groupId, id)
+                    markThreadItemRead(txn, groupId, id)
+                }
+                txn.attach(ThreadedGroupMessageReadEvent(clientId, groupId, readIds.size))
+                txn.attach {
+                    // TODO replace immutable ThreadItems instead to avoid recomposing whole list
+                    val messageTree = (state.value as? Loaded)?.messageTree ?: return@attach
+                    _state.value = Loaded(messageTree)
                 }
             }
-            // we don't attach this to the transaction that actually changes the DB,
-            // but that should be fine for this purpose of just decrementing a counter
-            eventBus.broadcast(ThreadedGroupMessageReadEvent(clientId, groupId, readIds.size))
-            // TODO replace immutable ThreadItems instead to avoid recomposing whole list
-            val messageTree = (state.value as? Loaded)?.messageTree ?: return
-            _state.value = Loaded(messageTree)
         }
     }
 
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/conversation/ThreadedGroupConversationScreen.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/conversation/ThreadedGroupConversationScreen.kt
index 2aebe4f82d..86b607c30a 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/conversation/ThreadedGroupConversationScreen.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/conversation/ThreadedGroupConversationScreen.kt
@@ -76,6 +76,7 @@ fun ThreadedGroupConversationScreen(
                     threadedGroupItem = groupItem,
                     sharingViewModel = viewModel.sharingViewModel,
                     onGroupDelete = viewModel::deleteGroup,
+                    onMarkRead = viewModel::markThreadItemsRead,
                     dropdownMenu = dropdownMenu,
                 )
             }
@@ -108,6 +109,7 @@ private fun ThreadedGroupConversationHeader(
     strings: ThreadedGroupStrings,
     threadedGroupItem: ThreadedGroupItem,
     sharingViewModel: ThreadedGroupSharingViewModel,
+    onMarkRead: () -> Unit,
     onGroupDelete: () -> Unit,
     dropdownMenu: ThreadedGroupDropdownMenu,
 ) {
@@ -151,7 +153,8 @@ private fun ThreadedGroupConversationHeader(
                 dropdownMenu(
                     sharingViewModel,
                     menuState.value == MAIN,
-                    close
+                    close,
+                    onMarkRead,
                 ) { deleteGroupDialogVisible.value = true }
             }
         }
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/conversation/ThreadedGroupDropdownMenu.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/conversation/ThreadedGroupDropdownMenu.kt
index dc6909d950..b9cd057015 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/conversation/ThreadedGroupDropdownMenu.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/conversation/ThreadedGroupDropdownMenu.kt
@@ -25,5 +25,6 @@ typealias ThreadedGroupDropdownMenu = @Composable (
     sharingViewModel: ThreadedGroupSharingViewModel,
     expanded: Boolean,
     onClose: () -> Unit,
+    onMarkReadClick: () -> Unit,
     onLeaveGroupClick: () -> Unit,
 ) -> Unit
diff --git a/briar-desktop/src/main/resources/strings/BriarDesktop.properties b/briar-desktop/src/main/resources/strings/BriarDesktop.properties
index 327cf981bb..afa4b54bbd 100644
--- a/briar-desktop/src/main/resources/strings/BriarDesktop.properties
+++ b/briar-desktop/src/main/resources/strings/BriarDesktop.properties
@@ -209,6 +209,7 @@ group.member.created_you=You created the group
 group.member.created_contact={0} created the group
 group.invite.action.title=Invite Contacts
 group.invite.action.no_contacts=No contacts yet. You can only invite contacts to your private group.
+group.mark.read=Mark as read
 
 # Introduction
 introduction.introduce=Make Introduction
-- 
GitLab