diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/DesktopModule.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/DesktopModule.kt index bb0b796eb3f5c54b64770ee5db7b5af86e2bb9b4..738ffc8041d3c38cc2b321743271f0fb95a726c8 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/DesktopModule.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/DesktopModule.kt @@ -68,6 +68,8 @@ import org.briarproject.briar.desktop.threading.BriarExecutorsImpl import org.briarproject.briar.desktop.threading.UiExecutor import org.briarproject.briar.desktop.ui.BriarUi import org.briarproject.briar.desktop.ui.BriarUiImpl +import org.briarproject.briar.desktop.ui.MessageCounter +import org.briarproject.briar.desktop.ui.MessageCounterImpl import org.briarproject.briar.desktop.viewmodel.ViewModelModule import org.briarproject.briar.identity.IdentityModule import java.io.File @@ -205,4 +207,8 @@ internal class DesktopModule( @Singleton internal fun provideNotificationProvider(): NotificationProvider = if (isLinux()) LibnotifyNotificationProvider else StubNotificationProvider + + @Provides + @Singleton + internal fun provideMessageCounter(messageCounter: MessageCounterImpl): MessageCounter = messageCounter } diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt index d0f935fc286aba751006bf9b5d1f11b125c89eb6..2a8e0a1eede1978d673530da6ee6f0f38f373019 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt @@ -52,7 +52,7 @@ import org.briarproject.bramble.api.identity.AuthorId import org.briarproject.briar.desktop.theme.selectedCard import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE import org.briarproject.briar.desktop.ui.HorizontalDivider -import org.briarproject.briar.desktop.ui.MessageCounter +import org.briarproject.briar.desktop.ui.NumberBadge import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18nF import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18nP @@ -168,8 +168,8 @@ private fun RealContactRow(contactItem: ContactItem) { ) { Box { ProfileCircle(36.dp, contactItem) - MessageCounter( - unread = contactItem.unread, + NumberBadge( + num = contactItem.unread, modifier = Modifier.align(Alignment.TopEnd).offset(6.dp, (-6).dp) ) } diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationList.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationList.kt index bfc7c0406edddf628e9fadaf2a2caa790d05d341..5a39243773ea8d6719fb3ed2850c4020eda53892 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationList.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationList.kt @@ -65,7 +65,7 @@ import org.briarproject.briar.desktop.theme.ChevronUp import org.briarproject.briar.desktop.theme.divider import org.briarproject.briar.desktop.ui.HorizontalDivider import org.briarproject.briar.desktop.ui.Loader -import org.briarproject.briar.desktop.ui.MessageCounter +import org.briarproject.briar.desktop.ui.NumberBadge import org.briarproject.briar.desktop.ui.isWindowFocused import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n import org.briarproject.briar.desktop.utils.PreviewUtils.preview @@ -295,7 +295,7 @@ fun UnreadMessagesFAB( Icons.Filled.ChevronUp else Icons.Filled.ChevronDown Icon(arrow, i18n("access.message.jump_to_unread")) } - MessageCounter( + NumberBadge( counter, Modifier.align(Alignment.TopEnd).offset(3.dp, (-3).dp) ) 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 8de9dce6c61f6c3aeb6f9e330fd7ba2cd77af705..4d4a3e78468dda4e1d8870f6ef8a1b080adeaf7f 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 @@ -35,7 +35,7 @@ interface NotificationProvider { fun init() fun uninit() - fun notifyPrivateMessages(num: Int) + fun notifyPrivateMessages(num: Int, contacts: Int) } object StubNotificationProvider : NotificationProvider { @@ -47,5 +47,5 @@ object StubNotificationProvider : NotificationProvider { override fun init() {} override fun uninit() {} - override fun notifyPrivateMessages(num: Int) {} + override fun notifyPrivateMessages(num: Int, contacts: Int) {} } 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 65623e4bfebcd0d708158c41d4d9a250610d1be4..06728605d735f2aaa7b896fde2e2b92bb71d1677 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 @@ -25,6 +25,7 @@ import org.briarproject.briar.desktop.notification.NotificationProvider import org.briarproject.briar.desktop.utils.AudioUtils.loadAudioFromResource import org.briarproject.briar.desktop.utils.AudioUtils.play import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18nF import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18nP import org.briarproject.briar.desktop.utils.KLoggerUtils.e import org.briarproject.briar.desktop.utils.KLoggerUtils.i @@ -99,7 +100,7 @@ object LibnotifyNotificationProvider : NotificationProvider { } } - override fun notifyPrivateMessages(num: Int) { + override fun notifyPrivateMessages(num: Int, contacts: Int) { if (!libNotifyAvailable) { // play sound even if libnotify unavailable if (soundAvailable) sound.play() @@ -116,7 +117,10 @@ object LibnotifyNotificationProvider : NotificationProvider { * The summary must be encoded using UTF-8. */ // todo: we could use body instead with markup (where supported) - val text = i18nP("notifications.message.private", num) + 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) /** diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threading/BriarExecutors.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threading/BriarExecutors.kt index 5c0cae2b54cd8966eeb6c229fbbb6da812d1edd6..545c12cd7969033e0df5fdb5f7ad82613e1501d2 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threading/BriarExecutors.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threading/BriarExecutors.kt @@ -19,11 +19,17 @@ package org.briarproject.briar.desktop.threading import org.briarproject.bramble.api.db.DatabaseExecutor +import org.briarproject.bramble.api.db.Transaction import org.briarproject.bramble.api.lifecycle.IoExecutor interface BriarExecutors { fun onDbThread(@DatabaseExecutor task: () -> Unit) + fun onDbThreadWithTransaction( + readOnly: Boolean, + @DatabaseExecutor task: (Transaction) -> Unit, + ) + fun onUiThread(@UiExecutor task: () -> Unit) fun onIoThread(@IoExecutor task: () -> Unit) diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threading/BriarExecutorsImpl.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threading/BriarExecutorsImpl.kt index 531f4273506fe57710e90c2fca63827bc8fceedd..f002b48e0a265e5b889dc3e9f606db58d5ef8238 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threading/BriarExecutorsImpl.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threading/BriarExecutorsImpl.kt @@ -18,8 +18,13 @@ package org.briarproject.briar.desktop.threading +import mu.KotlinLogging import org.briarproject.bramble.api.db.DatabaseExecutor +import org.briarproject.bramble.api.db.Transaction +import org.briarproject.bramble.api.db.TransactionManager import org.briarproject.bramble.api.lifecycle.IoExecutor +import org.briarproject.bramble.api.lifecycle.LifecycleManager +import org.briarproject.briar.desktop.utils.KLoggerUtils.w import java.util.concurrent.Executor import javax.inject.Inject @@ -29,8 +34,46 @@ constructor( @UiExecutor private val uiExecutor: Executor, @DatabaseExecutor private val dbExecutor: Executor, @IoExecutor private val ioExecutor: Executor, + private val lifecycleManager: LifecycleManager, + private val db: TransactionManager, ) : BriarExecutors { - override fun onDbThread(@DatabaseExecutor task: () -> Unit) = dbExecutor.execute(task) + + companion object { + private val LOG = KotlinLogging.logger {} + } + + override fun onDbThread(@DatabaseExecutor task: () -> Unit) = dbExecutor.execute { + try { + lifecycleManager.waitForDatabase() + task() + } catch (e: InterruptedException) { + LOG.w { "Interrupted while waiting for database" } + Thread.currentThread().interrupt() + } catch (e: Exception) { + LOG.w(e) { "Unhandled exception in database executor" } + } + } + + override fun onDbThreadWithTransaction( + readOnly: Boolean, + @DatabaseExecutor task: (Transaction) -> Unit, + ) = dbExecutor.execute { + try { + lifecycleManager.waitForDatabase() + val txn = db.startTransaction(readOnly) + try { + task(txn) + db.commitTransaction(txn) + } finally { + db.endTransaction(txn) + } + } catch (e: InterruptedException) { + LOG.w { "Interrupted while waiting for database" } + Thread.currentThread().interrupt() + } catch (e: Exception) { + LOG.w(e) { "Unhandled exception in database executor" } + } + } override fun onUiThread(@UiExecutor task: () -> Unit) = uiExecutor.execute(task) 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 7b8ca936ec15d1efa67d668f99c0aa39a4a7776d..b7acda7f56929cd9c3fcbe71dcdc799ffbb51183 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 @@ -45,8 +45,6 @@ import org.briarproject.bramble.api.event.EventListener import org.briarproject.bramble.api.lifecycle.LifecycleManager import org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent -import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent -import org.briarproject.briar.desktop.conversation.ConversationMessagesReadEvent import org.briarproject.briar.desktop.expiration.ExpirationBanner import org.briarproject.briar.desktop.login.ErrorScreen import org.briarproject.briar.desktop.login.StartupScreen @@ -95,6 +93,7 @@ constructor( private val viewModelProvider: ViewModelProvider, private val configuration: Configuration, private val notificationProvider: NotificationProvider, + private val messageCounter: MessageCounterImpl, ) : BriarUi { private var screenState by mutableStateOf( @@ -134,29 +133,11 @@ constructor( .toAwtImage(LocalDensity.current, LocalLayoutDirection.current, Size(32f, 32f)) DisposableEffect(Unit) { - // todo: hard-coded messageCount doesn't account for unread messages on application start - // also see https://code.briarproject.org/briar/briar-desktop/-/issues/133 - var messageCount = 0 val eventListener = EventListener { e -> when (e) { is LifecycleEvent -> if (e.lifecycleState == RUNNING) screenState = MAIN - - is ConversationMessageReceivedEvent<*> -> { - messageCount++ - if (!focusState.focused) { - window.iconImage = iconBadge - if (configuration.showNotifications) { - notificationProvider.notifyPrivateMessages(messageCount) - } - } - } - - is ConversationMessagesReadEvent -> { - messageCount -= e.count - if (messageCount < 0) messageCount = 0 - } } } val focusListener = object : WindowFocusListener { @@ -169,12 +150,22 @@ constructor( focusState.focused = false } } + val messageCounterListener: MessageCounterListener = { (total, contacts) -> + if (total > 0 && !focusState.focused) { + window.iconImage = iconBadge + if (configuration.showNotifications) { + notificationProvider.notifyPrivateMessages(total, contacts) + } + } + } notificationProvider.init() eventBus.addListener(eventListener) window.addWindowFocusListener(focusListener) + messageCounter.addListener(messageCounterListener) onDispose { + messageCounter.removeListener(messageCounterListener) eventBus.removeListener(eventListener) window.removeWindowFocusListener(focusListener) notificationProvider.uninit() 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 07f2c8d71f9b64e85901401ff4f8b77bd39f40b0..f01ff09f0a93345774d534a97a9f2ea3b2555750 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 @@ -18,43 +18,16 @@ package org.briarproject.briar.desktop.ui -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import org.briarproject.briar.desktop.theme.outline +interface MessageCounter { -@Composable -fun MessageCounter(unread: Int, modifier: Modifier = Modifier) { - val outlineColor = MaterialTheme.colors.outline - val briarSecondary = MaterialTheme.colors.secondary - if (unread > 0) { - Box( - modifier = modifier - .height(20.dp) - .widthIn(min = 20.dp, max = Dp.Infinity) - .border(1.dp, outlineColor, CircleShape) - .background(briarSecondary, CircleShape) - .padding(horizontal = 6.dp) - ) { - Text( - modifier = Modifier.align(Alignment.Center), - style = MaterialTheme.typography.overline, - textAlign = TextAlign.Center, - text = unread.toString(), - maxLines = 1 - ) - } - } + fun addListener(listener: MessageCounterListener): Boolean + + fun removeListener(listener: MessageCounterListener): Boolean } + +data class MessageCounterData( + val total: Int, + val contacts: 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 new file mode 100644 index 0000000000000000000000000000000000000000..2234e6812451a7f2992562292e4649ba25e030cc --- /dev/null +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/MessageCounterImpl.kt @@ -0,0 +1,86 @@ +/* + * Briar Desktop + * Copyright (C) 2021-2022 The Briar Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package org.briarproject.briar.desktop.ui + +import org.briarproject.bramble.api.Multiset +import org.briarproject.bramble.api.contact.ContactId +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.briar.api.conversation.ConversationManager +import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent +import org.briarproject.briar.desktop.conversation.ConversationMessagesReadEvent +import org.briarproject.briar.desktop.threading.BriarExecutors +import javax.inject.Inject + +class MessageCounterImpl +@Inject +constructor( + private val contactManager: ContactManager, + private val conversationManager: ConversationManager, + private val briarExecutors: BriarExecutors, + eventBus: EventBus, +) : MessageCounter { + + private val messageCount = Multiset<ContactId>() + + private val listeners = mutableListOf<MessageCounterListener>() + + init { + eventBus.addListener { e -> + when (e) { + is LifecycleEvent -> + if (e.lifecycleState == RUNNING) { + briarExecutors.onDbThreadWithTransaction(true) { txn -> + val contacts = contactManager.getContacts(txn) + for (c in contacts) { + val unreadMessages = conversationManager.getGroupCount(txn, c.id).unreadCount + messageCount.addCount(c.id, unreadMessages) + } + txn.attach { informListeners() } + } + } + + is ConversationMessageReceivedEvent<*> -> { + messageCount.add(e.contactId) + informListeners() + } + + is ConversationMessagesReadEvent -> { + messageCount.removeCount(e.contactId, e.count) + } + } + } + } + + override fun addListener(listener: MessageCounterListener) = listeners.add(listener) + + override fun removeListener(listener: MessageCounterListener) = listeners.remove(listener) + + private fun informListeners() = listeners.forEach { l -> + l.invoke(MessageCounterData(messageCount.total, messageCount.unique)) + } + + private fun <T> Multiset<T>.removeCount(t: T, count: Int) = + repeat(count) { remove(t) } + + private fun <T> Multiset<T>.addCount(t: T, count: Int) = + repeat(count) { add(t) } +} diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/NumberBadge.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/NumberBadge.kt new file mode 100644 index 0000000000000000000000000000000000000000..564b66488107754347b316e0fe5e4033e8c495aa --- /dev/null +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/NumberBadge.kt @@ -0,0 +1,60 @@ +/* + * Briar Desktop + * Copyright (C) 2021-2022 The Briar Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package org.briarproject.briar.desktop.ui + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import org.briarproject.briar.desktop.theme.outline + +@Composable +fun NumberBadge(num: Int, modifier: Modifier = Modifier) { + val outlineColor = MaterialTheme.colors.outline + val briarSecondary = MaterialTheme.colors.secondary + if (num > 0) { + Box( + modifier = modifier + .height(20.dp) + .widthIn(min = 20.dp, max = Dp.Infinity) + .border(1.dp, outlineColor, CircleShape) + .background(briarSecondary, CircleShape) + .padding(horizontal = 6.dp) + ) { + Text( + modifier = Modifier.align(Alignment.Center), + style = MaterialTheme.typography.overline, + textAlign = TextAlign.Center, + text = num.toString(), + maxLines = 1 + ) + } + } +} diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/viewmodel/DbViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/viewmodel/DbViewModel.kt index 1a09ad893ce4fb31ecc4c57bc98c027a9a0a34d3..73d439337dd4bc004c13cc29a5eb88f1c8f2a3ee 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/viewmodel/DbViewModel.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/viewmodel/DbViewModel.kt @@ -24,7 +24,6 @@ import org.briarproject.bramble.api.db.Transaction import org.briarproject.bramble.api.db.TransactionManager import org.briarproject.bramble.api.lifecycle.LifecycleManager import org.briarproject.briar.desktop.threading.BriarExecutors -import org.briarproject.briar.desktop.utils.KLoggerUtils.w abstract class DbViewModel( private val briarExecutors: BriarExecutors, @@ -42,17 +41,7 @@ abstract class DbViewModel( * whenever the UI should react to a successful transaction, * strongly consider using [runOnDbThreadWithTransaction] instead. */ - protected fun runOnDbThread(task: () -> Unit) = briarExecutors.onDbThread { - try { - lifecycleManager.waitForDatabase() - task() - } catch (e: InterruptedException) { - LOG.w { "Interrupted while waiting for database" } - Thread.currentThread().interrupt() - } catch (e: Exception) { - LOG.w(e) { "Unhandled exception in database executor" } - } - } + protected fun runOnDbThread(task: () -> Unit) = briarExecutors.onDbThread(task) /** * Waits for the DB to open and runs the given [task] on the [DatabaseExecutor], @@ -63,21 +52,5 @@ abstract class DbViewModel( protected fun runOnDbThreadWithTransaction( readOnly: Boolean, task: (Transaction) -> Unit - ) = briarExecutors.onDbThread { - try { - lifecycleManager.waitForDatabase() - val txn = db.startTransaction(readOnly) - try { - task(txn) - db.commitTransaction(txn) - } finally { - db.endTransaction(txn) - } - } catch (e: InterruptedException) { - LOG.w { "Interrupted while waiting for database" } - Thread.currentThread().interrupt() - } catch (e: Exception) { - LOG.w(e) { "Unhandled exception in database executor" } - } - } + ) = briarExecutors.onDbThreadWithTransaction(readOnly, task) } diff --git a/briar-desktop/src/main/resources/strings/BriarDesktop.properties b/briar-desktop/src/main/resources/strings/BriarDesktop.properties index c68aeec54288e94e9eb68e21eeaee0a77ff5449a..b7f24e82a2be50673a6c6e04bd51dec265811f36 100644 --- a/briar-desktop/src/main/resources/strings/BriarDesktop.properties +++ b/briar-desktop/src/main/resources/strings/BriarDesktop.properties @@ -255,7 +255,8 @@ expiration.banner.part1.nozero={0, plural, one {This is a test version of Briar expiration.banner.part2=Please update to a newer version in time. # Notification -notifications.message.private={0, plural, one {New private message.} other {{0} new private messages.}} +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. # Settings settings.title=Settings diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/DesktopTestModule.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/DesktopTestModule.kt index a043f8baa051d1b17dc41c42bd8eb76fe13a0ee6..7ac394f9433338a27bc40418a40b9b0edfb4ecdc 100644 --- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/DesktopTestModule.kt +++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/DesktopTestModule.kt @@ -72,6 +72,8 @@ import org.briarproject.briar.desktop.threading.BriarExecutorsImpl import org.briarproject.briar.desktop.threading.UiExecutor import org.briarproject.briar.desktop.ui.BriarUi import org.briarproject.briar.desktop.ui.BriarUiImpl +import org.briarproject.briar.desktop.ui.MessageCounter +import org.briarproject.briar.desktop.ui.MessageCounterImpl import org.briarproject.briar.desktop.viewmodel.ViewModelModule import org.briarproject.briar.identity.IdentityModule import org.briarproject.briar.test.TestModule @@ -212,6 +214,10 @@ internal class DesktopTestModule( internal fun provideNotificationProvider(): NotificationProvider = if (isLinux()) LibnotifyNotificationProvider else StubNotificationProvider + @Provides + @Singleton + internal fun provideMessageCounter(messageCounter: MessageCounterImpl): MessageCounter = messageCounter + @Provides @Singleton internal fun provideTestAvatarCreator(testAvatarCreator: TestAvatarCreatorImpl): TestAvatarCreator { diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/notification/linux/TestNativeNotifications.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/notification/linux/TestNativeNotifications.kt index 83e833d1361f16e0294805a80b6ba4e6aeb02ee2..dffcbebfaa3f3bced3fbd7906259a5d60cfe7d26 100644 --- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/notification/linux/TestNativeNotifications.kt +++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/notification/linux/TestNativeNotifications.kt @@ -26,10 +26,10 @@ fun main() { LibnotifyNotificationProvider.apply { init() - notifyPrivateMessages(9) + notifyPrivateMessages(4, 1) Thread.sleep(1000) - notifyPrivateMessages(10) + notifyPrivateMessages(10, 3) Thread.sleep(1000) uninit()