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
This commit is contained in:
parent
fae3d14d50
commit
8216ea37f7
|
@ -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")
|
||||
|
||||
|
|
|
@ -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`)
|
||||
|
|
|
@ -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()
|
||||
|
@ -44,10 +46,14 @@ QtObject:
|
|||
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()
|
||||
self.unreadCount = self.delegate.unreadActivityCenterNotificationsCount()
|
||||
return self.unreadCount
|
||||
|
||||
QtProperty[int] unreadActivityCenterNotificationsCount:
|
||||
read = unreadActivityCenterNotificationsCount
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_520_2)">
|
||||
<path d="M0.5 7.99981C0.5 5.02751 1.00183 3.20508 2.10355 2.10336C3.20527 1.00164 5.0277 0.499809 8 0.499809C10.9723 0.499809 12.7947 1.00164 13.8964 2.10336C14.9982 3.20508 15.5 5.02751 15.5 7.99981C15.5 10.9721 14.9982 12.7945 13.8964 13.8963C12.7947 14.998 10.9723 15.4998 8 15.4998C5.0277 15.4998 3.20527 14.998 2.10355 13.8963C1.00183 12.7945 0.5 10.9721 0.5 7.99981Z" fill="white" stroke="#82878A"/>
|
||||
<g filter="url(#filter0_d_520_2)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.82589 8.32108C9.69564 9.95266 8.52412 11.3949 6.93746 11.4856C5.96323 11.5412 4.99014 10.9463 4.93758 9.98025C4.89683 9.20282 5.37785 8.63553 6.21726 8.41821C6.41558 8.36622 6.61875 8.33456 6.82359 8.32372C7.71517 8.27346 8.27351 8.47793 9.1651 8.42728C9.3708 8.41686 9.57493 8.38609 9.77447 8.33544C9.79047 8.33091 9.80989 8.32599 9.82589 8.32108Z" fill="#82878A"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.17682 7.6789C6.30707 6.04732 7.47859 4.60509 9.06525 4.51438C10.0395 4.45883 11.0126 5.05371 11.0651 6.01973C11.1078 6.79716 10.6249 7.36446 9.78545 7.58215C9.58713 7.63414 9.38396 7.6658 9.17912 7.67664C8.28753 7.7269 7.7292 7.52244 6.83761 7.57232C6.63191 7.58275 6.42777 7.61351 6.22823 7.66417C6.21224 7.66908 6.19281 7.67399 6.17682 7.6789Z" fill="#82878A"/>
|
||||
</g>
|
||||
<circle cx="13" cy="13" r="2.5" fill="#EB2222" stroke="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_520_2" x="-952.598" y="-697.68" width="1921.2" height="1922.04" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="255.342"/>
|
||||
<feGaussianBlur stdDeviation="478.766"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.0352941 0 0 0 0 0.0627451 0 0 0 0 0.109804 0 0 0 0.12 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_520_2"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_520_2" result="shape"/>
|
||||
</filter>
|
||||
<clipPath id="clip0_520_2">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,23 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_520_8)">
|
||||
<path d="M0.5 7.99981C0.5 5.02751 1.00183 3.20508 2.10355 2.10336C3.20527 1.00164 5.0277 0.499809 8 0.499809C10.9723 0.499809 12.7947 1.00164 13.8964 2.10336C14.9982 3.20508 15.5 5.02751 15.5 7.99981C15.5 10.9721 14.9982 12.7945 13.8964 13.8963C12.7947 14.998 10.9723 15.4998 8 15.4998C5.0277 15.4998 3.20527 14.998 2.10355 13.8963C1.00183 12.7945 0.5 10.9721 0.5 7.99981Z" fill="white" stroke="#82878A"/>
|
||||
<g filter="url(#filter0_d_520_8)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.82589 8.32108C9.69564 9.95266 8.52412 11.3949 6.93746 11.4856C5.96323 11.5412 4.99014 10.9463 4.93758 9.98025C4.89683 9.20282 5.37785 8.63553 6.21726 8.41821C6.41558 8.36622 6.61875 8.33456 6.82359 8.32372C7.71517 8.27346 8.27351 8.47793 9.1651 8.42728C9.3708 8.41686 9.57493 8.38609 9.77447 8.33544C9.79047 8.33091 9.80989 8.32599 9.82589 8.32108Z" fill="#82878A"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.17682 7.6789C6.30707 6.04732 7.47859 4.60509 9.06525 4.51438C10.0395 4.45883 11.0126 5.05371 11.0651 6.01973C11.1078 6.79716 10.6249 7.36446 9.78545 7.58215C9.58713 7.63414 9.38396 7.6658 9.17912 7.67664C8.28753 7.7269 7.7292 7.52244 6.83761 7.57232C6.63191 7.58275 6.42777 7.61351 6.22823 7.66417C6.21224 7.66908 6.19281 7.67399 6.17682 7.6789Z" fill="#82878A"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_520_8" x="-952.598" y="-697.68" width="1921.2" height="1922.04" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="255.342"/>
|
||||
<feGaussianBlur stdDeviation="478.766"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.0352941 0 0 0 0 0.0627451 0 0 0 0 0.109804 0 0 0 0.12 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_520_8"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_520_8" result="shape"/>
|
||||
</filter>
|
||||
<clipPath id="clip0_520_8">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,11 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_7558_1933)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 16C2 5.5 5.5 2 16 2C26.5 2 30 5.5 30 16C30 26.5 26.5 30 16 30C5.5 30 2 26.5 2 16ZM12.8076 15.2453C13.0356 12.3901 15.0857 9.86618 17.8624 9.70744C19.5673 9.61021 21.2702 10.6513 21.3622 12.3418C21.4368 13.7023 20.5917 14.6951 19.1227 15.076C18.7757 15.167 18.4201 15.2224 18.0616 15.2414C17.281 15.2854 16.6463 15.2178 16.0116 15.1502H16.0116L16.0116 15.1502C15.3776 15.0827 14.7436 15.0152 13.964 15.0588C13.604 15.0771 13.2468 15.1309 12.8976 15.2196C12.8836 15.2239 12.8681 15.2282 12.8526 15.2324L12.8525 15.2325C12.837 15.2368 12.8216 15.2411 12.8076 15.2453ZM19.1994 16.7545C18.9714 19.6098 16.9213 22.1337 14.1446 22.2924C12.4397 22.3897 10.7368 21.3486 10.6448 19.6581C10.5735 18.2976 11.4153 17.3048 12.8843 16.9245C13.2313 16.8335 13.5869 16.7781 13.9453 16.7592C14.725 16.7152 15.359 16.7825 15.9929 16.8499C16.6277 16.9173 17.2623 16.9847 18.043 16.9404C18.403 16.9221 18.7602 16.8683 19.1094 16.7797L19.1425 16.7706L19.1426 16.7705C19.1619 16.7653 19.1818 16.7599 19.1994 16.7545Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_7558_1933">
|
||||
<rect width="28" height="28" fill="white" transform="translate(2 2)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<circle r="4.776" fill="#EB2222" cx="25" cy="25"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -300,6 +300,7 @@ StatusWindow {
|
|||
id: systemTray
|
||||
objectName: "systemTray"
|
||||
isProduction: production
|
||||
showRedDot: typeof mainModule !== "undefined" ? mainModule.notificationAvailable : false
|
||||
onActivateApp: {
|
||||
applicationWindow.makeStatusAppActive()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue