From 5348c0bc5aec0d8ade009453b1350b94cbfc568f Mon Sep 17 00:00:00 2001
From: ialokim <ialokim@mailbox.org>
Date: Sun, 9 Apr 2023 12:02:04 +0200
Subject: [PATCH] private groups: show notifications and update unread message
 badges

---
 .../briar/desktop/forum/ForumListViewModel.kt |  4 +-
 .../ForumConversationViewModel.kt             |  3 ++
 .../briar/desktop/navigation/BriarSidebar.kt  |  3 +-
 .../desktop/navigation/SidebarViewModel.kt    |  3 ++
 .../AbstractNotificationProvider.kt           |  7 +++
 .../notification/NotificationProvider.kt      |  2 +
 .../notification/SoundNotificationProvider.kt |  1 +
 .../desktop/privategroup/PrivateGroupItem.kt  |  6 +--
 .../privategroup/PrivateGroupListViewModel.kt | 11 ++--
 .../PrivateGroupConversationViewModel.kt      |  3 ++
 .../ThreadedGroupMessageReadEvent.kt}         |  9 ++--
 .../ThreadedConversationViewModel.kt          |  7 ++-
 .../briarproject/briar/desktop/ui/BriarUi.kt  | 11 ++++
 .../briar/desktop/ui/MessageCounter.kt        |  2 +-
 .../briar/desktop/ui/MessageCounterImpl.kt    | 54 ++++++++++++++++---
 .../resources/strings/BriarDesktop.properties |  2 +
 16 files changed, 104 insertions(+), 24 deletions(-)
 rename briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/{forum/ForumPostReadEvent.kt => threadedgroup/ThreadedGroupMessageReadEvent.kt} (80%)

diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/ForumListViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/ForumListViewModel.kt
index 6dbdad4dbb..be9909579d 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/ForumListViewModel.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/ForumListViewModel.kt
@@ -32,6 +32,7 @@ import org.briarproject.briar.api.forum.ForumManager
 import org.briarproject.briar.api.forum.event.ForumPostReceivedEvent
 import org.briarproject.briar.desktop.forum.conversation.ForumConversationViewModel
 import org.briarproject.briar.desktop.threadedgroup.ThreadedGroupListViewModel
+import org.briarproject.briar.desktop.threadedgroup.ThreadedGroupMessageReadEvent
 import org.briarproject.briar.desktop.threading.BriarExecutors
 import org.briarproject.briar.desktop.utils.removeFirst
 import org.briarproject.briar.desktop.utils.replaceFirst
@@ -62,7 +63,8 @@ class ForumListViewModel
                 updateItem(e.groupId) { it.updateOnPostReceived(e.header) }
             }
 
-            is ForumPostReadEvent -> {
+            is ThreadedGroupMessageReadEvent -> {
+                if (e.clientId != clientId) return
                 updateItem(e.groupId) { it.updateOnPostsRead(e.numMarkedRead) }
             }
         }
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 75570a87a2..cd7afa13d7 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
@@ -32,6 +32,7 @@ import org.briarproject.bramble.api.event.EventBus
 import org.briarproject.bramble.api.identity.IdentityManager
 import org.briarproject.bramble.api.identity.LocalAuthor
 import org.briarproject.bramble.api.lifecycle.LifecycleManager
+import org.briarproject.bramble.api.sync.ClientId
 import org.briarproject.bramble.api.sync.GroupId
 import org.briarproject.bramble.api.sync.MessageId
 import org.briarproject.bramble.api.system.Clock
@@ -64,6 +65,8 @@ class ForumConversationViewModel @Inject constructor(
     eventBus
 ) {
 
+    override val clientId: ClientId = ForumManager.CLIENT_ID
+
     @UiExecutor
     override fun eventOccurred(e: Event) {
         if (e is ForumPostReceivedEvent) {
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/navigation/BriarSidebar.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/navigation/BriarSidebar.kt
index debaba99ef..d8b408af28 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/navigation/BriarSidebar.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/navigation/BriarSidebar.kt
@@ -88,7 +88,8 @@ fun BriarSidebar(
         Spacer(Modifier.height(4.dp))
 
         BriarSidebarButton(UiMode.CONTACTS, messageCount.privateMessages)
-        if (configuration.shouldEnablePrivateGroups()) BriarSidebarButton(UiMode.GROUPS)
+        if (configuration.shouldEnablePrivateGroups())
+            BriarSidebarButton(UiMode.GROUPS, messageCount.privateGroupMessages)
         if (configuration.shouldEnableForums()) BriarSidebarButton(UiMode.FORUMS, messageCount.forumPosts)
         if (configuration.shouldEnableBlogs()) BriarSidebarButton(UiMode.BLOGS)
 
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/navigation/SidebarViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/navigation/SidebarViewModel.kt
index b6b7591a66..8d1e6b74c1 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/navigation/SidebarViewModel.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/navigation/SidebarViewModel.kt
@@ -29,6 +29,7 @@ import org.briarproject.briar.desktop.threading.UiExecutor
 import org.briarproject.briar.desktop.ui.MessageCounter
 import org.briarproject.briar.desktop.ui.MessageCounterData
 import org.briarproject.briar.desktop.ui.MessageCounterDataType.Forum
+import org.briarproject.briar.desktop.ui.MessageCounterDataType.PrivateGroup
 import org.briarproject.briar.desktop.ui.MessageCounterDataType.PrivateMessage
 import org.briarproject.briar.desktop.ui.UiMode
 import org.briarproject.briar.desktop.viewmodel.ViewModel
@@ -69,6 +70,7 @@ constructor(
         when (type) {
             PrivateMessage -> _messageCount.update { copy(privateMessages = count) }
             Forum -> _messageCount.update { copy(forumPosts = count) }
+            PrivateGroup -> _messageCount.update { copy(privateGroupMessages = count) }
         }
     }
 
@@ -97,5 +99,6 @@ constructor(
     data class MessageCount(
         val privateMessages: Int = 0,
         val forumPosts: Int = 0,
+        val privateGroupMessages: Int = 0,
     )
 }
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/AbstractNotificationProvider.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/AbstractNotificationProvider.kt
index c71c81bbf5..29511a0a8d 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/AbstractNotificationProvider.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/AbstractNotificationProvider.kt
@@ -37,4 +37,11 @@ abstract class AbstractNotificationProvider : VisualNotificationProvider {
         else
             i18nF("notifications.message.forum.several_forums", num, forums)
     )
+
+    override fun notifyPrivateGroupMessages(num: Int, groups: Int) = sendNotification(
+        if (groups == 1)
+            i18nP("notifications.message.group.one_group", num)
+        else
+            i18nF("notifications.message.group.several_groups", num, groups)
+    )
 }
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/NotificationProvider.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/NotificationProvider.kt
index ac9888246a..adf09ce574 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/NotificationProvider.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/NotificationProvider.kt
@@ -37,6 +37,7 @@ interface NotificationProvider {
     fun uninit()
     fun notifyPrivateMessages(num: Int, contacts: Int)
     fun notifyForumPosts(num: Int, forums: Int)
+    fun notifyPrivateGroupMessages(num: Int, groups: Int)
 }
 
 interface VisualNotificationProvider : NotificationProvider
@@ -52,4 +53,5 @@ object StubNotificationProvider : VisualNotificationProvider {
     override fun uninit() {}
     override fun notifyPrivateMessages(num: Int, contacts: Int) {}
     override fun notifyForumPosts(num: Int, forums: Int) {}
+    override fun notifyPrivateGroupMessages(num: Int, groups: Int) {}
 }
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/SoundNotificationProvider.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/SoundNotificationProvider.kt
index ef5edba4e7..f005f97e1f 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/SoundNotificationProvider.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/SoundNotificationProvider.kt
@@ -57,4 +57,5 @@ object SoundNotificationProvider : NotificationProvider {
 
     override fun notifyPrivateMessages(num: Int, contacts: Int) = playSound()
     override fun notifyForumPosts(num: Int, forums: Int) = playSound()
+    override fun notifyPrivateGroupMessages(num: Int, groups: Int) = playSound()
 }
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupItem.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupItem.kt
index fce9fc24ab..1f32e27acd 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupItem.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupItem.kt
@@ -20,7 +20,7 @@ package org.briarproject.briar.desktop.privategroup
 
 import org.briarproject.bramble.api.sync.GroupId
 import org.briarproject.briar.api.client.MessageTracker
-import org.briarproject.briar.api.client.PostHeader
+import org.briarproject.briar.api.privategroup.GroupMessageHeader
 import org.briarproject.briar.api.privategroup.PrivateGroup
 import org.briarproject.briar.desktop.threadedgroup.ThreadedGroupItem
 import kotlin.math.max
@@ -43,13 +43,13 @@ data class PrivateGroupItem(
     override val id: GroupId = privateGroup.id
     override val name: String = privateGroup.name
 
-    fun updateOnPostReceived(header: PostHeader) =
+    fun updateOnMessageReceived(header: GroupMessageHeader) =
         copy(
             msgCount = msgCount + 1,
             unread = if (header.isRead) unread else unread + 1,
             timestamp = max(header.timestamp, this.timestamp)
         )
 
-    fun updateOnPostsRead(num: Int) =
+    fun updateOnMessagesRead(num: Int) =
         copy(unread = unread - num)
 }
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupListViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupListViewModel.kt
index 5997082470..c85d0807fc 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupListViewModel.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupListViewModel.kt
@@ -42,6 +42,7 @@ import org.briarproject.briar.api.privategroup.PrivateGroupManager
 import org.briarproject.briar.api.privategroup.event.GroupMessageAddedEvent
 import org.briarproject.briar.desktop.privategroup.conversation.PrivateGroupConversationViewModel
 import org.briarproject.briar.desktop.threadedgroup.ThreadedGroupListViewModel
+import org.briarproject.briar.desktop.threadedgroup.ThreadedGroupMessageReadEvent
 import org.briarproject.briar.desktop.threading.BriarExecutors
 import org.briarproject.briar.desktop.utils.removeFirst
 import org.briarproject.briar.desktop.utils.replaceFirst
@@ -70,13 +71,13 @@ class PrivateGroupListViewModel
         super.eventOccurred(e)
         when (e) {
             is GroupMessageAddedEvent -> {
-                updateItem(e.groupId) { it.updateOnPostReceived(e.header) }
+                updateItem(e.groupId) { it.updateOnMessageReceived(e.header) }
             }
 
-            // TODO
-            /*is ForumPostReadEvent -> {
-                updateItem(e.groupId) { it.updateOnPostsRead(e.numMarkedRead) }
-            }*/
+            is ThreadedGroupMessageReadEvent -> {
+                if (e.clientId != clientId) return
+                updateItem(e.groupId) { it.updateOnMessagesRead(e.numMarkedRead) }
+            }
         }
     }
 
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 d803d29109..5e9677446a 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
@@ -32,6 +32,7 @@ import org.briarproject.bramble.api.event.EventBus
 import org.briarproject.bramble.api.identity.IdentityManager
 import org.briarproject.bramble.api.identity.LocalAuthor
 import org.briarproject.bramble.api.lifecycle.LifecycleManager
+import org.briarproject.bramble.api.sync.ClientId
 import org.briarproject.bramble.api.sync.GroupId
 import org.briarproject.bramble.api.sync.MessageId
 import org.briarproject.bramble.api.system.Clock
@@ -65,6 +66,8 @@ class PrivateGroupConversationViewModel @Inject constructor(
     eventBus
 ) {
 
+    override val clientId: ClientId = PrivateGroupManager.CLIENT_ID
+
     @UiExecutor
     override fun eventOccurred(e: Event) {
         // todo: handle incoming messages
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/ForumPostReadEvent.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/ThreadedGroupMessageReadEvent.kt
similarity index 80%
rename from briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/ForumPostReadEvent.kt
rename to briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/ThreadedGroupMessageReadEvent.kt
index f0da6e2e9c..a1867d403a 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/ForumPostReadEvent.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/ThreadedGroupMessageReadEvent.kt
@@ -16,15 +16,18 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-package org.briarproject.briar.desktop.forum
+package org.briarproject.briar.desktop.threadedgroup
 
 import org.briarproject.bramble.api.event.Event
+import org.briarproject.bramble.api.sync.ClientId
 import org.briarproject.bramble.api.sync.GroupId
 
 /**
- * An event that is broadcast when [numMarkedRead] post(s) in a forum was marked as read.
+ * An event that is broadcast when [numMarkedRead] post(s) in a forum
+ * or message(s) in a private group are marked as read.
  */
-data class ForumPostReadEvent(
+data class ThreadedGroupMessageReadEvent(
+    val clientId: ClientId,
     val groupId: GroupId,
     val numMarkedRead: Int,
 ) : Event()
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 3e5122191d..36f62241ee 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
@@ -25,12 +25,13 @@ import org.briarproject.bramble.api.db.Transaction
 import org.briarproject.bramble.api.db.TransactionManager
 import org.briarproject.bramble.api.event.EventBus
 import org.briarproject.bramble.api.lifecycle.LifecycleManager
+import org.briarproject.bramble.api.sync.ClientId
 import org.briarproject.bramble.api.sync.GroupId
 import org.briarproject.bramble.api.sync.MessageId
 import org.briarproject.briar.api.client.PostHeader
 import org.briarproject.briar.client.MessageTreeImpl
-import org.briarproject.briar.desktop.forum.ForumPostReadEvent
 import org.briarproject.briar.desktop.threadedgroup.ThreadedGroupItem
+import org.briarproject.briar.desktop.threadedgroup.ThreadedGroupMessageReadEvent
 import org.briarproject.briar.desktop.threadedgroup.sharing.ThreadedGroupSharingViewModel
 import org.briarproject.briar.desktop.threading.BriarExecutors
 import org.briarproject.briar.desktop.threading.UiExecutor
@@ -45,6 +46,8 @@ abstract class ThreadedConversationViewModel(
     private val eventBus: EventBus,
 ) : EventListenerDbViewModel(briarExecutors, lifecycleManager, db, eventBus) {
 
+    protected abstract val clientId: ClientId
+
     private val _threadedGroupItem = mutableStateOf<ThreadedGroupItem?>(null)
     val groupItem = _threadedGroupItem.asState()
 
@@ -128,7 +131,7 @@ abstract class ThreadedConversationViewModel(
             }
             // 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(ForumPostReadEvent(groupId, readIds.size))
+            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/ui/BriarUi.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt
index e528ff6c69..20b1a68c05 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt
@@ -57,6 +57,7 @@ import org.briarproject.briar.desktop.settings.UnencryptedSettings.Theme.AUTO
 import org.briarproject.briar.desktop.settings.UnencryptedSettings.Theme.DARK
 import org.briarproject.briar.desktop.theme.BriarTheme
 import org.briarproject.briar.desktop.ui.MessageCounterDataType.Forum
+import org.briarproject.briar.desktop.ui.MessageCounterDataType.PrivateGroup
 import org.briarproject.briar.desktop.ui.MessageCounterDataType.PrivateMessage
 import org.briarproject.briar.desktop.ui.Screen.EXPIRED
 import org.briarproject.briar.desktop.ui.Screen.MAIN
@@ -139,6 +140,7 @@ constructor(
                 val notificationCoolDown = 5.seconds.inWholeMilliseconds
                 var lastNotificationPrivateMessage = 0L
                 var lastNotificationForum = 0L
+                var lastNotificationPrivateGroup = 0L
 
                 val eventListener = EventListener { e ->
                     when (e) {
@@ -157,6 +159,7 @@ constructor(
                         // reset notification cool-down
                         lastNotificationPrivateMessage = 0
                         lastNotificationForum = 0
+                        lastNotificationPrivateGroup = 0
                     }
                 }
                 val messageCounterListener: MessageCounterListener = { (type, total, groups, inc) ->
@@ -169,6 +172,10 @@ constructor(
                             Forum -> {
                                 { notifyForumPosts(total, groups) }
                             }
+
+                            PrivateGroup -> {
+                                { notifyPrivateGroupMessages(total, groups) }
+                            }
                         }
                         val (lastNotification, setLastNotification) = when (type) {
                             PrivateMessage -> lastNotificationPrivateMessage to { v: Long ->
@@ -176,6 +183,10 @@ constructor(
                             }
 
                             Forum -> lastNotificationForum to { v: Long -> lastNotificationForum = v }
+
+                            PrivateGroup -> lastNotificationPrivateGroup to { v: Long ->
+                                lastNotificationPrivateGroup = v
+                            }
                         }
 
                         window.iconImage = iconBadge
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/MessageCounter.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/MessageCounter.kt
index 31cc4f6360..bc79db45e7 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/MessageCounter.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/MessageCounter.kt
@@ -25,7 +25,7 @@ interface MessageCounter {
     fun removeListener(listener: MessageCounterListener): Boolean
 }
 
-enum class MessageCounterDataType { PrivateMessage, Forum }
+enum class MessageCounterDataType { PrivateMessage, Forum, PrivateGroup }
 
 /**
  * Data holder for MessageCounter updates.
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/MessageCounterImpl.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/MessageCounterImpl.kt
index 876ec358b2..e8d5a5cc0b 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/MessageCounterImpl.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/MessageCounterImpl.kt
@@ -32,11 +32,14 @@ import org.briarproject.briar.api.conversation.ConversationManager
 import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent
 import org.briarproject.briar.api.forum.ForumManager
 import org.briarproject.briar.api.forum.event.ForumPostReceivedEvent
+import org.briarproject.briar.api.privategroup.PrivateGroupManager
+import org.briarproject.briar.api.privategroup.event.GroupMessageAddedEvent
 import org.briarproject.briar.desktop.conversation.ConversationMessagesReadEvent
-import org.briarproject.briar.desktop.forum.ForumPostReadEvent
+import org.briarproject.briar.desktop.threadedgroup.ThreadedGroupMessageReadEvent
 import org.briarproject.briar.desktop.threading.BriarExecutors
 import org.briarproject.briar.desktop.threading.UiExecutor
 import org.briarproject.briar.desktop.ui.MessageCounterDataType.Forum
+import org.briarproject.briar.desktop.ui.MessageCounterDataType.PrivateGroup
 import org.briarproject.briar.desktop.ui.MessageCounterDataType.PrivateMessage
 import org.briarproject.briar.desktop.utils.KLoggerUtils.e
 import javax.inject.Inject
@@ -47,6 +50,7 @@ constructor(
     private val contactManager: ContactManager,
     private val conversationManager: ConversationManager,
     private val forumManager: ForumManager,
+    private val privateGroupManager: PrivateGroupManager,
     private val briarExecutors: BriarExecutors,
     eventBus: EventBus,
 ) : MessageCounter {
@@ -61,6 +65,9 @@ constructor(
     @UiExecutor
     private val countForumPosts = Multiset<GroupId>()
 
+    @UiExecutor
+    private val countPrivateGroupMessages = Multiset<GroupId>()
+
     private val listeners = mutableListOf<MessageCounterListener>()
 
     init {
@@ -75,6 +82,9 @@ constructor(
                             val countForumPostsMap = forumManager.getForums(txn).associate { f ->
                                 f.id to forumManager.getGroupCount(txn, f.id).unreadCount
                             }
+                            val countPrivateGroupMessageMap = privateGroupManager.getPrivateGroups(txn).associate { g ->
+                                g.id to privateGroupManager.getGroupCount(txn, g.id).unreadCount
+                            }
                             txn.attach {
                                 countPrivateMessagesMap.forEach { (id, count) ->
                                     countPrivateMessages.addCount(id, count)
@@ -82,9 +92,13 @@ constructor(
                                 countForumPostsMap.forEach { (id, count) ->
                                     countForumPosts.addCount(id, count)
                                 }
+                                countPrivateGroupMessageMap.forEach { (id, count) ->
+                                    countPrivateGroupMessages.addCount(id, count)
+                                }
 
                                 informListeners(PrivateMessage, true)
                                 informListeners(Forum, true)
+                                informListeners(PrivateGroup, true)
                             }
                         }
                     }
@@ -116,22 +130,45 @@ constructor(
                     informListeners(Forum, true)
                 }
 
-                is ForumPostReadEvent -> {
+                is GroupMessageAddedEvent -> {
+                    if (e.isLocal) return@addListener
+
+                    countPrivateGroupMessages.add(e.groupId)
+                    informListeners(PrivateGroup, true)
+                }
+
+                is ThreadedGroupMessageReadEvent -> {
                     try {
-                        countForumPosts.removeCount(e.groupId, e.numMarkedRead)
+                        when (e.clientId) {
+                            ForumManager.CLIENT_ID -> {
+                                countForumPosts.removeCount(e.groupId, e.numMarkedRead)
+                                informListeners(Forum, false)
+                            }
+
+                            PrivateGroupManager.CLIENT_ID -> {
+                                countPrivateGroupMessages.removeCount(e.groupId, e.numMarkedRead)
+                                informListeners(PrivateGroup, false)
+                            }
+                        }
                     } catch (e: NoSuchElementException) {
                         LOG.e(e) {
-                            "inconsistent state in MessageCounterImpl.countForumPosts: " +
+                            "inconsistent state in MessageCounterImpl: " +
                                 "trying to remove non-existing element"
                         }
                     }
-                    informListeners(Forum, false)
                 }
 
                 is GroupRemovedEvent -> {
-                    if (e.group.clientId == ForumManager.CLIENT_ID) {
-                        countForumPosts.removeAll(e.group.id)
-                        informListeners(Forum, false)
+                    when (e.group.clientId) {
+                        ForumManager.CLIENT_ID -> {
+                            countForumPosts.removeAll(e.group.id)
+                            informListeners(Forum, false)
+                        }
+
+                        PrivateGroupManager.CLIENT_ID -> {
+                            countPrivateGroupMessages.removeAll(e.group.id)
+                            informListeners(PrivateGroup, false)
+                        }
                     }
                 }
             }
@@ -146,6 +183,7 @@ constructor(
         val groupCount = when (type) {
             PrivateMessage -> countPrivateMessages
             Forum -> countForumPosts
+            PrivateGroup -> countPrivateGroupMessages
         }
         l.invoke(MessageCounterData(type, groupCount.total, groupCount.unique, increment))
     }
diff --git a/briar-desktop/src/main/resources/strings/BriarDesktop.properties b/briar-desktop/src/main/resources/strings/BriarDesktop.properties
index a16d623948..28dcf45281 100644
--- a/briar-desktop/src/main/resources/strings/BriarDesktop.properties
+++ b/briar-desktop/src/main/resources/strings/BriarDesktop.properties
@@ -398,6 +398,8 @@ notifications.message.private.one_chat={0, plural, one {New private message.} ot
 notifications.message.private.several_chats={0} new messages in {1} private chats.
 notifications.message.forum.one_forum={0, plural, one {New forum post.} other {{0} new forum posts.}}
 notifications.message.forum.several_forums={0} new posts in {1} forums.
+notifications.message.group.one_group={0, plural, one {New private group message.} other {{0} new private group messages.}}
+notifications.message.group.several_groups={0} new messages in {1} private groups.
 
 # Settings
 settings.title=Settings
-- 
GitLab