From 8216ea37f70c24b5800615055287b3df93b03fbf Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Fri, 12 Jul 2024 09:41:27 -0400 Subject: [PATCH] feat(icon): show a red dot when we have unread notifications and windows icon with white border (#15496) * feat(icon): show a red dot when we have unread notifications * feat(windows-icon): update windows icon to have a white border Fixes #14788 Fixes #14855 Adds a red dot on the tray icon if there is an unread message in an unmuted channel or in the activity center --- .../main/activity_center/io_interface.nim | 3 +++ .../modules/main/activity_center/module.nim | 8 ++++--- src/app/modules/main/activity_center/view.nim | 14 +++++++---- .../modules/main/chat_section/controller.nim | 3 +++ src/app/modules/main/chat_section/model.nim | 7 ------ src/app/modules/main/io_interface.nim | 3 +++ src/app/modules/main/module.nim | 9 +++++++ src/app/modules/main/view.nim | 17 +++++++++++++ .../modules/shared_models/section_model.nim | 11 ++++++++- ui/app/mainui/StatusTrayIcon.qml | 12 +++++++--- ...status-logo-white-windows-with-red-dot.svg | 24 +++++++++++++++++++ .../icons/status-logo-white-windows.svg | 23 ++++++++++++++++++ .../icons/status-logo-white-with-red-dot.svg | 11 +++++++++ ui/main.qml | 1 + 14 files changed, 128 insertions(+), 18 deletions(-) create mode 100644 ui/imports/assets/icons/status-logo-white-windows-with-red-dot.svg create mode 100644 ui/imports/assets/icons/status-logo-white-windows.svg create mode 100644 ui/imports/assets/icons/status-logo-white-with-red-dot.svg diff --git a/src/app/modules/main/activity_center/io_interface.nim b/src/app/modules/main/activity_center/io_interface.nim index 5e68251636..b7b75048e3 100644 --- a/src/app/modules/main/activity_center/io_interface.nim +++ b/src/app/modules/main/activity_center/io_interface.nim @@ -21,6 +21,9 @@ method viewDidLoad*(self: AccessInterface) {.base.} = method hasMoreToShow*(self: AccessInterface): bool {.base.} = raise newException(ValueError, "No implementation available") +method unreadActivityCenterNotificationsCountFromView*(self: AccessInterface): int {.base.} = + raise newException(ValueError, "No implementation available") + method unreadActivityCenterNotificationsCount*(self: AccessInterface): int {.base.} = raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/main/activity_center/module.nim b/src/app/modules/main/activity_center/module.nim index 653ffe1e73..66413e8c4f 100644 --- a/src/app/modules/main/activity_center/module.nim +++ b/src/app/modules/main/activity_center/module.nim @@ -24,6 +24,7 @@ type view: View viewVariant: QVariant moduleLoaded: bool + unreadCount: int proc newModule*( delegate: delegate_interface.AccessInterface, @@ -67,6 +68,9 @@ method viewDidLoad*(self: Module) = method hasMoreToShow*(self: Module): bool = self.controller.hasMoreToShow() +method unreadActivityCenterNotificationsCountFromView*(self: Module): int = + self.view.unreadCount() + method unreadActivityCenterNotificationsCount*(self: Module): int = self.controller.unreadActivityCenterNotificationsCount() @@ -76,9 +80,7 @@ method hasUnseenActivityCenterNotifications*(self: Module): bool = method onNotificationsCountMayHaveChanged*(self: Module) = self.view.unreadActivityCenterNotificationsCountChanged() self.view.hasUnseenActivityCenterNotificationsChanged() - -method hasUnseenActivityCenterNotificationsChanged*(self: Module) = - self.view.hasUnseenActivityCenterNotificationsChanged() + self.delegate.onActivityNotificationsUpdated() proc createMessageItemFromDto(self: Module, message: MessageDto, communityId: string, albumMessages: seq[MessageDto]): MessageItem = let contactDetails = self.controller.getContactDetails(message.`from`) diff --git a/src/app/modules/main/activity_center/view.nim b/src/app/modules/main/activity_center/view.nim index 3b0efa995b..cb1f6259ea 100644 --- a/src/app/modules/main/activity_center/view.nim +++ b/src/app/modules/main/activity_center/view.nim @@ -11,6 +11,7 @@ QtObject: model: Model modelVariant: QVariant groupCounters: Table[ActivityCenterGroup, int] + unreadCount: int proc delete*(self: View) = self.QObject.delete @@ -22,6 +23,7 @@ QtObject: result.model = newModel() result.modelVariant = newQVariant(result.model) result.groupCounters = initTable[ActivityCenterGroup, int]() + result.unreadCount = 0 proc load*(self: View) = self.delegate.viewDidLoad() @@ -37,17 +39,21 @@ QtObject: proc hasMoreToShowChanged*(self: View) {.signal.} - proc hasMoreToShow*(self: View): bool {.slot.} = + proc hasMoreToShow*(self: View): bool {.slot.} = self.delegate.hasMoreToShow() QtProperty[bool] hasMoreToShow: read = hasMoreToShow notify = hasMoreToShowChanged + proc unreadCount*(self: View): int = + return self.unreadCount + proc unreadActivityCenterNotificationsCountChanged*(self: View) {.signal.} - proc unreadActivityCenterNotificationsCount*(self: View): int {.slot.} = - self.delegate.unreadActivityCenterNotificationsCount() + proc unreadActivityCenterNotificationsCount*(self: View): int {.slot.} = + self.unreadCount = self.delegate.unreadActivityCenterNotificationsCount() + return self.unreadCount QtProperty[int] unreadActivityCenterNotificationsCount: read = unreadActivityCenterNotificationsCount @@ -55,7 +61,7 @@ QtObject: proc hasUnseenActivityCenterNotificationsChanged*(self: View) {.signal.} - proc hasUnseenActivityCenterNotifications*(self: View): bool {.slot.} = + proc hasUnseenActivityCenterNotifications*(self: View): bool {.slot.} = self.delegate.hasUnseenActivityCenterNotifications() QtProperty[bool] hasUnseenActivityCenterNotifications: diff --git a/src/app/modules/main/chat_section/controller.nim b/src/app/modules/main/chat_section/controller.nim index 057f435b3d..30b3ad0d70 100644 --- a/src/app/modules/main/chat_section/controller.nim +++ b/src/app/modules/main/chat_section/controller.nim @@ -132,6 +132,9 @@ proc init*(self: Controller) = self.events.on(message_service.SIGNAL_MESSAGE_MARKED_AS_UNREAD) do(e:Args): let args = message_service.MessageMarkMessageAsUnreadArgs(e) let chat = self.chatService.getChatById(args.chatId) + if ((self.isCommunitySection and chat.communityId != self.sectionId) or + (not self.isCommunitySection and chat.communityId != "")): + return self.delegate.onMarkMessageAsUnread(chat) self.events.on(chat_service.SIGNAL_CHAT_LEFT) do(e: Args): diff --git a/src/app/modules/main/chat_section/model.nim b/src/app/modules/main/chat_section/model.nim index 2058461fa6..194603615a 100644 --- a/src/app/modules/main/chat_section/model.nim +++ b/src/app/modules/main/chat_section/model.nim @@ -641,13 +641,6 @@ QtObject: defer: modelIndex.delete self.dataChanged(modelIndex, modelIndex, @[ModelRole.HasUnreadMessages.int, ModelRole.NotificationsCount.int]) - proc incrementNotificationsForItemByIdAndGetNotificationCount*(self: Model, id: string): int = - let index = self.getItemIdxById(id) - if index == -1: - return 0 - self.updateNotificationsForItemById(id, hasUnreadMessages = true, self.items[index].notificationsCount + 1) - return self.items[index].notificationsCount - proc updateLastMessageTimestampOnItemById*(self: Model, id: string, lastMessageTimestamp: int) = let index = self.getItemIdxById(id) if index == -1: diff --git a/src/app/modules/main/io_interface.nim b/src/app/modules/main/io_interface.nim index 37a72d3e85..169fb1aece 100644 --- a/src/app/modules/main/io_interface.nim +++ b/src/app/modules/main/io_interface.nim @@ -126,6 +126,9 @@ method onChatsLoadingFailed*(self: AccessInterface) {.base.} = method onActiveChatChange*(self: AccessInterface, sectionId: string, chatId: string) {.base.} = raise newException(ValueError, "No implementation available") +method onActivityNotificationsUpdated*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + method onNotificationsUpdated*(self: AccessInterface, sectionId: string, sectionHasUnreadMessages: bool, sectionNotificationCount: int) {.base.} = raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/main/module.nim b/src/app/modules/main/module.nim index fdb0be63fa..88b5965459 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -994,9 +994,18 @@ method onActiveChatChange*[T](self: Module[T], sectionId: string, chatId: string method onChatLeft*[T](self: Module[T], chatId: string) = self.appSearchModule.updateSearchLocationIfPointToChatWithId(chatId) +proc checkIfWeHaveNotifications[T](self: Module[T]) = + let sectionWithUnread = self.view.model().isThereASectionWithUnreadMessages() + let activtyCenterNotifications = self.activityCenterModule.unreadActivityCenterNotificationsCountFromView() > 0 + self.view.setNotificationAvailable(sectionWithUnread or activtyCenterNotifications) + +method onActivityNotificationsUpdated[T](self: Module[T]) = + self.checkIfWeHaveNotifications() + method onNotificationsUpdated[T](self: Module[T], sectionId: string, sectionHasUnreadMessages: bool, sectionNotificationCount: int) = self.view.model().updateNotifications(sectionId, sectionHasUnreadMessages, sectionNotificationCount) + self.checkIfWeHaveNotifications() method onNetworkConnected[T](self: Module[T]) = self.view.setConnected(true) diff --git a/src/app/modules/main/view.nim b/src/app/modules/main/view.nim index adee2298b5..959655b9a8 100644 --- a/src/app/modules/main/view.nim +++ b/src/app/modules/main/view.nim @@ -17,6 +17,7 @@ QtObject: modelVariant: QVariant sectionsLoaded: bool chatsLoadingFailed: bool + notificationAvailable: bool activeSection: SectionDetails activeSectionVariant: QVariant chatSearchModel: chat_search_model.Model @@ -45,6 +46,7 @@ QtObject: result.model = section_model.newModel() result.sectionsLoaded = false result.chatsLoadingFailed = false + result.notificationAvailable = false result.modelVariant = newQVariant(result.model) result.activeSection = newActiveSection() result.activeSectionVariant = newQVariant(result.activeSection) @@ -269,6 +271,21 @@ QtObject: read = isConnected notify = onlineStatusChanged + proc notificationAvailableChanged(self: View) {.signal.} + + proc notificationAvailable*(self: View): bool {.slot.} = + result = self.notificationAvailable + + proc setNotificationAvailable*(self: View, value: bool) = + if self.notificationAvailable == value: + return + self.notificationAvailable = value + self.notificationAvailableChanged() + + QtProperty[bool] notificationAvailable: + read = notificationAvailable + notify = notificationAvailableChanged + proc displayUserProfile*(self:View, publicKey: string) {.signal.} proc emitDisplayUserProfileSignal*(self: View, publicKey: string) = self.displayUserProfile(publicKey) diff --git a/src/app/modules/shared_models/section_model.nim b/src/app/modules/shared_models/section_model.nim index 360260afb9..0a6b7e8f40 100644 --- a/src/app/modules/shared_models/section_model.nim +++ b/src/app/modules/shared_models/section_model.nim @@ -369,10 +369,13 @@ QtObject: proc disableSection*(self: SectionModel, sectionType: SectionType) = self.enableDisableSection(sectionType, false) + proc isAMessengerItem*(item: SectionItem): bool = + return item.sectionType == SectionType.Chat or item.sectionType == SectionType.Community + # Count all mentions from all chat&community sections proc allMentionsCount*(self: SectionModel): int = for item in self.items: - if item.sectionType == SectionType.Chat or item.sectionType == SectionType.Community: + if item.isAMessengerItem(): result += item.notificationsCount proc updateIsPendingOwnershipRequest*(self: SectionModel, id: string, isPending: bool) = @@ -395,6 +398,12 @@ QtObject: self.notificationsCountChanged() return + proc isThereASectionWithUnreadMessages*(self: SectionModel): bool = + for item in self.items: + if item.isAMessengerItem() and item.hasNotification == true: + return true + return false + proc appendCommunityToken*(self: SectionModel, id: string, item: TokenItem) = for i in 0 ..< self.items.len: if(self.items[i].id == id): diff --git a/ui/app/mainui/StatusTrayIcon.qml b/ui/app/mainui/StatusTrayIcon.qml index 8ab7838abd..3ed26994f6 100644 --- a/ui/app/mainui/StatusTrayIcon.qml +++ b/ui/app/mainui/StatusTrayIcon.qml @@ -6,13 +6,19 @@ SystemTrayIcon { id: root property bool isProduction: true + property bool showRedDot: false signal activateApp() visible: true - icon.source: Qt.platform.os === Constants.windows // TODO: Add status-logo-white with stroke for windows - ? Style.png("status-logo%1".arg(root.isProduction ? "" : "-dev-circle")) - : Style.svg("status-logo-white") + + + icon.source: { + if (Qt.platform.os === Constants.windows) { + return root.showRedDot ? Style.svg("status-logo-white-windows-with-red-dot") : Style.svg("status-logo-white-windows") + } + return root.showRedDot ? Style.svg("status-logo-white-with-red-dot") : Style.svg("status-logo-white") + } icon.mask: Qt.platform.os !== Constants.windows onMessageClicked: { diff --git a/ui/imports/assets/icons/status-logo-white-windows-with-red-dot.svg b/ui/imports/assets/icons/status-logo-white-windows-with-red-dot.svg new file mode 100644 index 0000000000..66e98ba8fc --- /dev/null +++ b/ui/imports/assets/icons/status-logo-white-windows-with-red-dot.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/imports/assets/icons/status-logo-white-windows.svg b/ui/imports/assets/icons/status-logo-white-windows.svg new file mode 100644 index 0000000000..9949b6980d --- /dev/null +++ b/ui/imports/assets/icons/status-logo-white-windows.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/imports/assets/icons/status-logo-white-with-red-dot.svg b/ui/imports/assets/icons/status-logo-white-with-red-dot.svg new file mode 100644 index 0000000000..2805b54be1 --- /dev/null +++ b/ui/imports/assets/icons/status-logo-white-with-red-dot.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/ui/main.qml b/ui/main.qml index 6269a4340f..9cdb49f804 100644 --- a/ui/main.qml +++ b/ui/main.qml @@ -300,6 +300,7 @@ StatusWindow { id: systemTray objectName: "systemTray" isProduction: production + showRedDot: typeof mainModule !== "undefined" ? mainModule.notificationAvailable : false onActivateApp: { applicationWindow.makeStatusAppActive() }