From 85cc7cc32bbb112b7e09a36ad40bbd234ffbcdfc Mon Sep 17 00:00:00 2001
From: ialokim <ialokim@mailbox.org>
Date: Sat, 3 Dec 2022 00:30:20 +0100
Subject: [PATCH] send notifications on new forum posts

---
 .../notification/NotificationProvider.kt      |  2 +
 .../notification/SoundNotificationProvider.kt |  5 ++-
 .../linux/LibnotifyNotificationProvider.kt    | 20 ++++++---
 .../briarproject/briar/desktop/ui/BriarUi.kt  | 26 ++++++++---
 .../briar/desktop/ui/MessageCounter.kt        |  5 ++-
 .../briar/desktop/ui/MessageCounterImpl.kt    | 45 +++++++++++++++----
 .../resources/strings/BriarDesktop.properties |  2 +
 7 files changed, 84 insertions(+), 21 deletions(-)

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 60449a37d8..ac9888246a 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
@@ -36,6 +36,7 @@ interface NotificationProvider {
     fun init()
     fun uninit()
     fun notifyPrivateMessages(num: Int, contacts: Int)
+    fun notifyForumPosts(num: Int, forums: Int)
 }
 
 interface VisualNotificationProvider : NotificationProvider
@@ -50,4 +51,5 @@ object StubNotificationProvider : VisualNotificationProvider {
     override fun init() {}
     override fun uninit() {}
     override fun notifyPrivateMessages(num: Int, contacts: Int) {}
+    override fun notifyForumPosts(num: Int, forums: 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 8611f17a78..ef5edba4e7 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
@@ -51,7 +51,10 @@ object SoundNotificationProvider : NotificationProvider {
         }
     }
 
-    override fun notifyPrivateMessages(num: Int, contacts: Int) {
+    private fun playSound() {
         if (available) sound.play()
     }
+
+    override fun notifyPrivateMessages(num: Int, contacts: Int) = playSound()
+    override fun notifyForumPosts(num: Int, forums: Int) = playSound()
 }
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/linux/LibnotifyNotificationProvider.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/linux/LibnotifyNotificationProvider.kt
index 5527fa230c..0e91e5d8f0 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/linux/LibnotifyNotificationProvider.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/linux/LibnotifyNotificationProvider.kt
@@ -82,7 +82,7 @@ object LibnotifyNotificationProvider : VisualNotificationProvider {
         }
     }
 
-    override fun notifyPrivateMessages(num: Int, contacts: Int) {
+    private fun sendNotification(text: String) {
         if (!available) return
 
         /**
@@ -95,10 +95,6 @@ object LibnotifyNotificationProvider : VisualNotificationProvider {
          * The summary must be encoded using UTF-8.
          */
         // todo: we could use body instead with markup (where supported)
-        val text = if (contacts == 1)
-            i18nP("notifications.message.private.one_chat", num)
-        else
-            i18nF("notifications.message.private.several_chats", num, contacts)
         val notification = libNotify.notify_notification_new(text, null, null)
 
         /**
@@ -135,6 +131,20 @@ object LibnotifyNotificationProvider : VisualNotificationProvider {
         }
     }
 
+    override fun notifyPrivateMessages(num: Int, contacts: Int) = sendNotification(
+        if (contacts == 1)
+            i18nP("notifications.message.private.one_chat", num)
+        else
+            i18nF("notifications.message.private.several_chats", num, contacts)
+    )
+
+    override fun notifyForumPosts(num: Int, forums: Int) = sendNotification(
+        if (forums == 1)
+            i18nP("notifications.message.forum.one_forum", num)
+        else
+            i18nF("notifications.message.forum.several_forums", num, forums)
+    )
+
     /**
      * Functions as defined in the source code at
      * https://www.freedesktop.org/software/gstreamer-sdk/data/docs/latest/glib/glib-GVariant.html
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 a2085c5f0a..8badde07f6 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
@@ -47,12 +47,15 @@ import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent
 import org.briarproject.briar.desktop.expiration.ExpirationBanner
 import org.briarproject.briar.desktop.login.ErrorScreen
 import org.briarproject.briar.desktop.login.StartupScreen
+import org.briarproject.briar.desktop.notification.NotificationProvider
 import org.briarproject.briar.desktop.notification.SoundNotificationProvider
 import org.briarproject.briar.desktop.notification.VisualNotificationProvider
 import org.briarproject.briar.desktop.settings.Configuration
 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.PrivateMessage
 import org.briarproject.briar.desktop.ui.Screen.EXPIRED
 import org.briarproject.briar.desktop.ui.Screen.MAIN
 import org.briarproject.briar.desktop.ui.Screen.STARTUP
@@ -136,7 +139,8 @@ constructor(
             DisposableEffect(Unit) {
 
                 val notificationCoolDown = 5.seconds.inWholeMilliseconds
-                var lastNotification = 0L
+                var lastNotificationPrivateMessage = 0L
+                var lastNotificationForum = 0L
 
                 val eventListener = EventListener { e ->
                     when (e) {
@@ -153,19 +157,29 @@ constructor(
                     override fun windowLostFocus(e: WindowEvent?) {
                         focusState.focused = false
                         // reset notification cool-down
-                        lastNotification = 0
+                        lastNotificationPrivateMessage = 0
+                        lastNotificationForum = 0
                     }
                 }
-                val messageCounterListener: MessageCounterListener = { (total, contacts) ->
+                val messageCounterListener: MessageCounterListener = { (type, total, groups) ->
                     if (total > 0 && !focusState.focused) {
+                        val callback: NotificationProvider.() -> Unit = when (type) {
+                            PrivateMessage -> { { notifyPrivateMessages(total, groups) } }
+                            Forum -> { { notifyForumPosts(total, groups) } }
+                        }
+                        val (lastNotification, setLastNotification) = when (type) {
+                            PrivateMessage -> lastNotificationPrivateMessage to { v: Long -> lastNotificationPrivateMessage = v }
+                            Forum -> lastNotificationForum to { v: Long -> lastNotificationForum = v }
+                        }
+
                         window.iconImage = iconBadge
                         val currentTime = System.currentTimeMillis()
                         if (currentTime - lastNotification > notificationCoolDown) {
                             if (configuration.visualNotifications)
-                                visualNotificationProvider.notifyPrivateMessages(total, contacts)
+                                visualNotificationProvider.apply(callback)
                             if (configuration.soundNotifications)
-                                soundNotificationProvider.notifyPrivateMessages(total, contacts)
-                            lastNotification = currentTime
+                                soundNotificationProvider.apply(callback)
+                            setLastNotification(currentTime)
                         }
                     }
                 }
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 f01ff09f0a..b5a07dfac7 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,9 +25,12 @@ interface MessageCounter {
     fun removeListener(listener: MessageCounterListener): Boolean
 }
 
+enum class MessageCounterDataType { PrivateMessage, Forum }
+
 data class MessageCounterData(
+    val type: MessageCounterDataType,
     val total: Int,
-    val contacts: Int,
+    val groups: Int,
 )
 
 typealias MessageCounterListener = (MessageCounterData) -> Unit
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 2234e68124..906a40ebfb 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
@@ -24,10 +24,16 @@ import org.briarproject.bramble.api.contact.ContactManager
 import org.briarproject.bramble.api.event.EventBus
 import org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING
 import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent
+import org.briarproject.bramble.api.sync.GroupId
 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.desktop.conversation.ConversationMessagesReadEvent
+import org.briarproject.briar.desktop.forums.ForumPostReadEvent
 import org.briarproject.briar.desktop.threading.BriarExecutors
+import org.briarproject.briar.desktop.ui.MessageCounterDataType.Forum
+import org.briarproject.briar.desktop.ui.MessageCounterDataType.PrivateMessage
 import javax.inject.Inject
 
 class MessageCounterImpl
@@ -35,11 +41,13 @@ class MessageCounterImpl
 constructor(
     private val contactManager: ContactManager,
     private val conversationManager: ConversationManager,
+    private val forumManager: ForumManager,
     private val briarExecutors: BriarExecutors,
     eventBus: EventBus,
 ) : MessageCounter {
 
-    private val messageCount = Multiset<ContactId>()
+    private val countPrivateMessages = Multiset<ContactId>()
+    private val countForumPosts = Multiset<GroupId>()
 
     private val listeners = mutableListOf<MessageCounterListener>()
 
@@ -52,19 +60,36 @@ constructor(
                             val contacts = contactManager.getContacts(txn)
                             for (c in contacts) {
                                 val unreadMessages = conversationManager.getGroupCount(txn, c.id).unreadCount
-                                messageCount.addCount(c.id, unreadMessages)
+                                countPrivateMessages.addCount(c.id, unreadMessages)
+                            }
+                            val forums = forumManager.getForums(txn)
+                            for (f in forums) {
+                                val unreadMessages = forumManager.getGroupCount(txn, f.id).unreadCount
+                                countForumPosts.addCount(f.id, unreadMessages)
+                            }
+                            txn.attach {
+                                informListeners(PrivateMessage)
+                                informListeners(Forum)
                             }
-                            txn.attach { informListeners() }
                         }
                     }
 
                 is ConversationMessageReceivedEvent<*> -> {
-                    messageCount.add(e.contactId)
-                    informListeners()
+                    countPrivateMessages.add(e.contactId)
+                    informListeners(PrivateMessage)
                 }
 
                 is ConversationMessagesReadEvent -> {
-                    messageCount.removeCount(e.contactId, e.count)
+                    countPrivateMessages.removeCount(e.contactId, e.count)
+                }
+
+                is ForumPostReceivedEvent -> {
+                    countForumPosts.add(e.groupId)
+                    informListeners(Forum)
+                }
+
+                is ForumPostReadEvent -> {
+                    countForumPosts.removeCount(e.groupId, e.numMarkedRead)
                 }
             }
         }
@@ -74,8 +99,12 @@ constructor(
 
     override fun removeListener(listener: MessageCounterListener) = listeners.remove(listener)
 
-    private fun informListeners() = listeners.forEach { l ->
-        l.invoke(MessageCounterData(messageCount.total, messageCount.unique))
+    private fun informListeners(type: MessageCounterDataType) = listeners.forEach { l ->
+        val groupCount = when (type) {
+            PrivateMessage -> countPrivateMessages
+            Forum -> countForumPosts
+        }
+        l.invoke(MessageCounterData(type, groupCount.total, groupCount.unique))
     }
 
     private fun <T> Multiset<T>.removeCount(t: T, count: Int) =
diff --git a/briar-desktop/src/main/resources/strings/BriarDesktop.properties b/briar-desktop/src/main/resources/strings/BriarDesktop.properties
index 27063d7799..3b77ccf202 100644
--- a/briar-desktop/src/main/resources/strings/BriarDesktop.properties
+++ b/briar-desktop/src/main/resources/strings/BriarDesktop.properties
@@ -296,6 +296,8 @@ expiration.banner.part2=Please update to a newer version in time.
 # Notification
 notifications.message.private.one_chat={0, plural, one {New private message.} other {{0} new private messages.}}
 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_forum={0} new posts in {1} forums.
 
 # Settings
 settings.title=Settings
-- 
GitLab