From 44b9522edb55d88542816d0176c06f4310c944aa Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Tue, 28 Mar 2023 15:25:33 -0400 Subject: [PATCH] fix(unread): fix badge disappearing because of wrong calculation Fixes #10058 We were calling `updateParentBadgeNotifications` with wrong values, so we basically removed the badge all the time by accident. Now, we calculate the unread messages and messages from the service's cache. Also, we still called `updateParentBadgeNotifications` when a contact is updated, as if the contact requests still went in the old contact request popup. --- .../modules/main/chat_section/controller.nim | 19 ++++-- .../main/chat_section/io_interface.nim | 2 +- src/app/modules/main/chat_section/model.nim | 14 ++--- src/app/modules/main/chat_section/module.nim | 62 ++++++++++++------- src/app/modules/main/io_interface.nim | 3 + src/app/modules/main/module.nim | 3 + .../modules/shared_models/section_model.nim | 23 ++++++- src/app_service/service/chat/service.nim | 15 ++++- src/app_service/service/community/service.nim | 2 +- .../service/message/async_tasks.nim | 5 +- 10 files changed, 104 insertions(+), 44 deletions(-) diff --git a/src/app/modules/main/chat_section/controller.nim b/src/app/modules/main/chat_section/controller.nim index 464a49bd97..9583787afc 100644 --- a/src/app/modules/main/chat_section/controller.nim +++ b/src/app/modules/main/chat_section/controller.nim @@ -152,6 +152,9 @@ proc init*(self: Controller) = let args = message_service.MessagesMarkedAsReadArgs(e) # update chat entity in chat service var chat = self.chatService.getChatById(args.chatId) + if ((self.isCommunitySection and chat.communityId != self.sectionId) or + (not self.isCommunitySection and chat.communityId != "")): + return chat.unviewedMessagesCount = 0 chat.unviewedMentionsCount = 0 self.chatService.updateOrAddChat(chat) @@ -181,14 +184,14 @@ proc init*(self: Controller) = var args = ChatUpdateArgs(e) for chat in args.chats: let belongsToCommunity = chat.communityId.len > 0 - self.delegate.addChatIfDontExist(chat, belongsToCommunity, self.events, self.settingsService, self.nodeConfigurationService, + self.delegate.addOrUpdateChat(chat, belongsToCommunity, self.events, self.settingsService, self.nodeConfigurationService, self.contactService, self.chatService, self.communityService, self.messageService, self.gifService, self.mailserversService, setChatAsActive = false) self.events.on(SIGNAL_CHAT_CREATED) do(e: Args): var args = CreatedChatArgs(e) let belongsToCommunity = args.chat.communityId.len > 0 - self.delegate.addChatIfDontExist(args.chat, belongsToCommunity, self.events, self.settingsService, self.nodeConfigurationService, + self.delegate.addOrUpdateChat(args.chat, belongsToCommunity, self.events, self.settingsService, self.nodeConfigurationService, self.contactService, self.chatService, self.communityService, self.messageService, self.gifService, self.mailserversService, setChatAsActive = true) @@ -203,7 +206,7 @@ proc init*(self: Controller) = self.events.on(SIGNAL_COMMUNITY_CHANNEL_CREATED) do(e:Args): let args = CommunityChatArgs(e) let belongsToCommunity = args.chat.communityId.len > 0 - self.delegate.addChatIfDontExist(args.chat, belongsToCommunity, self.events, self.settingsService, self.nodeConfigurationService, + self.delegate.addOrUpdateChat(args.chat, belongsToCommunity, self.events, self.settingsService, self.nodeConfigurationService, self.contactService, self.chatService, self.communityService, self.messageService, self.gifService, self.mailserversService, setChatAsActive = true) @@ -392,6 +395,10 @@ proc getChats*(self: Controller, communityId: string, categoryId: string): seq[C proc getAllChats*(self: Controller, communityId: string): seq[ChatDto] = return self.communityService.getAllChats(communityId) +proc sectionUnreadMessagesAndMentionsCount*(self: Controller, communityId: string): + tuple[unviewedMessagesCount: int, unviewedMentionsCount: int] = + return self.chatService.sectionUnreadMessagesAndMentionsCount(communityId) + proc asyncGetChats*(self: Controller) = self.chatService.asyncGetChatsByChannelGroupId(self.sectionId) @@ -431,7 +438,7 @@ proc getOneToOneChatNameAndImage*(self: Controller, chatId: string): proc createOneToOneChat*(self: Controller, communityID: string, chatId: string, ensName: string) = let response = self.chatService.createOneToOneChat(communityID, chatId, ensName) if(response.success): - self.delegate.addChatIfDontExist(response.chatDto, false, self.events, self.settingsService, self.nodeConfigurationService, + self.delegate.addOrUpdateChat(response.chatDto, false, self.events, self.settingsService, self.nodeConfigurationService, self.contactService, self.chatService, self.communityService, self.messageService, self.gifService, self.mailserversService) @@ -501,14 +508,14 @@ proc makeAdmin*(self: Controller, communityID: string, chatId: string, pubKey: s proc createGroupChat*(self: Controller, communityID: string, groupName: string, pubKeys: seq[string]) = let response = self.chatService.createGroupChat(communityID, groupName, pubKeys) if(response.success): - self.delegate.addChatIfDontExist(response.chatDto, false, self.events, self.settingsService, self.nodeConfigurationService, + self.delegate.addOrUpdateChat(response.chatDto, false, self.events, self.settingsService, self.nodeConfigurationService, self.contactService, self.chatService, self.communityService, self.messageService, self.gifService, self.mailserversService) proc joinGroupChatFromInvitation*(self: Controller, groupName: string, chatId: string, adminPK: string) = let response = self.chatService.createGroupChatFromInvitation(groupName, chatId, adminPK) if(response.success): - self.delegate.addChatIfDontExist(response.chatDto, false, self.events, self.settingsService, self.nodeConfigurationService, + self.delegate.addOrUpdateChat(response.chatDto, false, self.events, self.settingsService, self.nodeConfigurationService, self.contactService, self.chatService, self.communityService, self.messageService, self.gifService, self.mailserversService) diff --git a/src/app/modules/main/chat_section/io_interface.nim b/src/app/modules/main/chat_section/io_interface.nim index 95500e3fe5..c82635e1c1 100644 --- a/src/app/modules/main/chat_section/io_interface.nim +++ b/src/app/modules/main/chat_section/io_interface.nim @@ -86,7 +86,7 @@ method doesCatOrChatExist*(self: AccessInterface, chatId: string): bool {.base.} method doesTopLevelChatExist*(self: AccessInterface, chatId: string): bool {.base.} = raise newException(ValueError, "No implementation available") -method addChatIfDontExist*(self: AccessInterface, +method addOrUpdateChat*(self: AccessInterface, chat: ChatDto, belongsToCommunity: bool, events: UniqueUUIDEventEmitter, diff --git a/src/app/modules/main/chat_section/model.nim b/src/app/modules/main/chat_section/model.nim index bdec58c615..4e281b9595 100644 --- a/src/app/modules/main/chat_section/model.nim +++ b/src/app/modules/main/chat_section/model.nim @@ -444,6 +444,13 @@ QtObject: let modelIndex = self.createIndex(index, 0, nil) 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: @@ -454,13 +461,6 @@ QtObject: let modelIndex = self.createIndex(index, 0, nil) self.dataChanged(modelIndex, modelIndex, @[ModelRole.LastMessageTimestamp.int]) - proc getAllNotifications*(self: Model): tuple[hasNotifications: bool, notificationsCount: int] = - result.hasNotifications = false - result.notificationsCount = 0 - for i in 0 ..< self.items.len: - result.hasNotifications = result.hasNotifications or self.items[i].hasUnreadMessages - result.notificationsCount = result.notificationsCount + self.items[i].notificationsCount - proc reorderChatById*( self: Model, chatId: string, diff --git a/src/app/modules/main/chat_section/module.nim b/src/app/modules/main/chat_section/module.nim index 5a61789029..b5dd11e79f 100644 --- a/src/app/modules/main/chat_section/module.nim +++ b/src/app/modules/main/chat_section/module.nim @@ -68,7 +68,7 @@ proc buildChatSectionUI(self: Module, gifService: gif_service.Service, mailserversService: mailservers_service.Service) -proc addChatIfDontExist(self: Module, +proc addOrUpdateChat(self: Module, chat: ChatDto, channelGroup: ChannelGroupDto, belongsToCommunity: bool, @@ -221,7 +221,7 @@ proc buildChatSectionUI( categoryPosition = category.position break - self.addChatIfDontExist( + self.addOrUpdateChat( chatDto, channelGroup, belongsToCommunity = chatDto.communityId.len > 0, @@ -472,13 +472,19 @@ method getChatContentModule*(self: Module, chatId: string): QVariant = return self.chatContentModules[chatId].getModuleAsVariant() -proc updateParentBadgeNotifications(self: Module, sectionHasUnreadMessagesArg: bool = false, unviewedMentionsCountArg: int = 0) = +proc updateParentBadgeNotifications(self: Module) = + let (unviewedMessagesCount, unviewedMentionsCount) = self.controller.sectionUnreadMessagesAndMentionsCount( + self.controller.getMySectionId() + ) self.delegate.onNotificationsUpdated( self.controller.getMySectionId(), - sectionHasUnreadMessagesArg, - unviewedMentionsCountArg + unviewedMessagesCount > 0, + unviewedMentionsCount ) +proc incrementParentBadgeNotifications(self: Module) = + self.delegate.onNotificationsIncremented(self.controller.getMySectionId()) + proc updateBadgeNotifications(self: Module, chatId: string, hasUnreadMessages: bool, unviewedMentionsCount: int) = # update model of this module (appropriate chat from the chats list (chats model)) self.view.chatsModel().updateNotificationsForItemById(chatId, hasUnreadMessages, unviewedMentionsCount) @@ -486,7 +492,16 @@ proc updateBadgeNotifications(self: Module, chatId: string, hasUnreadMessages: b if (self.chatContentModules.contains(chatId)): self.chatContentModules[chatId].onNotificationsUpdated(hasUnreadMessages, unviewedMentionsCount) # update parent module - self.updateParentBadgeNotifications(hasUnreadMessages, unviewedMentionsCount) + self.updateParentBadgeNotifications() + +proc incrementBadgeNotifications(self: Module, chatId: string) = + if self.chatsLoaded: + let notificationCount = self.view.chatsModel().incrementNotificationsForItemByIdAndGetNotificationCount(chatId) + # update child module + if (self.chatContentModules.contains(chatId)): + self.chatContentModules[chatId].onNotificationsUpdated(hasUnreadMessages = true, notificationCount) + # update parent module + self.incrementParentBadgeNotifications() method updateLastMessageTimestamp*(self: Module, chatId: string, lastMessageTimestamp: int) = self.view.chatsModel().updateLastMessageTimestampOnItemById(chatId, lastMessageTimestamp) @@ -850,8 +865,6 @@ method onContactAdded*(self: Module, publicKey: string) = if (contact.isContact): self.switchToOrCreateOneToOneChat(publicKey) - self.updateParentBadgeNotifications() - method acceptAllContactRequests*(self: Module) = let pubKeys = self.view.contactRequestsModel().getItemIds() for pk in pubKeys: @@ -862,7 +875,6 @@ method dismissContactRequest*(self: Module, publicKey: string) = method onContactRejected*(self: Module, publicKey: string) = self.view.contactRequestsModel().removeItemById(publicKey) - self.updateParentBadgeNotifications() method dismissAllContactRequests*(self: Module) = let pubKeys = self.view.contactRequestsModel().getItemIds() @@ -875,7 +887,6 @@ method blockContact*(self: Module, publicKey: string) = method onContactBlocked*(self: Module, publicKey: string) = self.view.contactRequestsModel().removeItemById(publicKey) self.view.chatsModel().changeBlockedOnItemById(publicKey, blocked=true) - self.updateParentBadgeNotifications() method onContactUnblocked*(self: Module, publicKey: string) = self.view.chatsModel().changeBlockedOnItemById(publicKey, blocked=false) @@ -891,7 +902,6 @@ method onContactDetailsUpdated*(self: Module, publicKey: string) = not self.view.contactRequestsModel().isContactWithIdAdded(publicKey)): let item = self.createItemFromPublicKey(publicKey) self.view.contactRequestsModel().addItem(item) - self.updateParentBadgeNotifications() singletonInstance.globalEvents.showNewContactRequestNotification("New Contact Request", fmt "{contactDetails.defaultDisplayName} added you as contact", singletonInstance.userProfile.getPubKey()) @@ -914,10 +924,6 @@ method onNewMessagesReceived*(self: Module, sectionIdMsgBelongsTo: string, chatI let chatDetails = self.controller.getChatDetails(chatIdMsgBelongsTo) - # Badge notification - let showBadge = (not chatDetails.muted and unviewedMessagesCount > 0) or unviewedMentionsCount > 0 - self.updateBadgeNotifications(chatIdMsgBelongsTo, showBadge, unviewedMentionsCount) - if (chatDetails.muted): # No need to send a notification return @@ -965,8 +971,8 @@ method onMeMentionedInEditedMessage*(self: Module, chatId: string, editedMessage (editedMessage.communityId.len > 0 and self.controller.getMySectionId() != editedMessage.communityId)): return - var (sectionHasUnreadMessages, sectionNotificationCount) = self.view.chatsModel().getAllNotifications() - self.updateBadgeNotifications(chatId, sectionHasUnreadMessages, sectionNotificationCount + 1) + + self.incrementBadgeNotifications(chatId) method addGroupMembers*(self: Module, chatId: string, pubKeys: string) = self.controller.addGroupMembers(chatId, self.convertPubKeysToJson(pubKeys)) @@ -1117,7 +1123,7 @@ method reorderCommunityChat*(self: Module, categoryId: string, chatId: string, p method setLoadingHistoryMessagesInProgress*(self: Module, isLoading: bool) = self.view.setLoadingHistoryMessagesInProgress(isLoading) -proc addChatIfDontExist(self: Module, +proc addOrUpdateChat(self: Module, chat: ChatDto, channelGroup: ChannelGroupDto, belongsToCommunity: bool, @@ -1131,15 +1137,25 @@ proc addChatIfDontExist(self: Module, gifService: gif_service.Service, mailserversService: mailservers_service.Service, setChatAsActive: bool = true) = - if not self.chatsLoaded: - return let sectionId = self.controller.getMySectionId() if(belongsToCommunity and sectionId != chat.communityId or not belongsToCommunity and sectionId != singletonInstance.userProfile.getPubKey()): return - if self.doesCatOrChatExist(chat.id): + let chatExists = self.doesCatOrChatExist(chat.id) + + if not self.chatsLoaded or chatExists: + # Update badges + var hasUnreadMessages = false + if not chat.muted: + hasUnreadMessages = chat.unviewedMessagesCount > 0 + self.updateBadgeNotifications(chat.id, hasUnreadMessages, chat.unviewedMentionsCount) + + if not self.chatsLoaded: + return + + if chatExists: if (chat.chatType == ChatType.PrivateGroupChat): self.onGroupChatDetailsUpdated(chat.id, chat.name, chat.color, chat.icon) elif (chat.chatType != ChatType.OneToOne): @@ -1162,7 +1178,7 @@ proc addChatIfDontExist(self: Module, setChatAsActive, ) -method addChatIfDontExist*(self: Module, +method addOrUpdateChat*(self: Module, chat: ChatDto, belongsToCommunity: bool, events: UniqueUUIDEventEmitter, @@ -1175,7 +1191,7 @@ method addChatIfDontExist*(self: Module, gifService: gif_service.Service, mailserversService: mailservers_service.Service, setChatAsActive: bool = true) = - self.addChatIfDontExist( + self.addOrUpdateChat( chat, ChannelGroupDto(), belongsToCommunity, diff --git a/src/app/modules/main/io_interface.nim b/src/app/modules/main/io_interface.nim index 9d2d16ad56..23234ee518 100644 --- a/src/app/modules/main/io_interface.nim +++ b/src/app/modules/main/io_interface.nim @@ -123,6 +123,9 @@ method onNotificationsUpdated*(self: AccessInterface, sectionId: string, section sectionNotificationCount: int) {.base.} = raise newException(ValueError, "No implementation available") +method onNotificationsIncremented*(self: AccessInterface, sectionId: string) {.base.} = + raise newException(ValueError, "No implementation available") + method onNotificationsIncreased*(self: AccessInterface, sectionId: string, addedSectionNotificationCount: 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 2f5f52769e..3a3d6fa859 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -827,6 +827,9 @@ method onNotificationsUpdated[T](self: Module[T], sectionId: string, sectionHasU sectionNotificationCount: int) = self.view.model().updateNotifications(sectionId, sectionHasUnreadMessages, sectionNotificationCount) +method onNotificationsIncremented[T](self: Module[T], sectionId: string) = + self.view.model().incrementNotifications(sectionId) + method onNetworkConnected[T](self: Module[T]) = self.view.setConnected(true) diff --git a/src/app/modules/shared_models/section_model.nim b/src/app/modules/shared_models/section_model.nim index a9cff66ad5..7f2ec1bd6d 100644 --- a/src/app/modules/shared_models/section_model.nim +++ b/src/app/modules/shared_models/section_model.nim @@ -1,11 +1,10 @@ import NimQml, Tables, strutils, strformat -import json, json_serialization +import json import section_item, member_model import ../main/communities/tokens/models/token_item - type ModelRole {.pure.} = enum Id = UserRole + 1 @@ -261,6 +260,7 @@ QtObject: self.items[index].muted = muted let dataIndex = self.createIndex(index, 0, nil) + defer: dataIndex.delete self.dataChanged(dataIndex, dataIndex, @[ModelRole.Muted.int]) @@ -271,6 +271,7 @@ QtObject: self.items[index] = item let dataIndex = self.createIndex(index, 0, nil) + defer: dataIndex.delete self.dataChanged(dataIndex, dataIndex, @[ ModelRole.Name.int, ModelRole.Description.int, @@ -330,11 +331,13 @@ QtObject: for i in 0 ..< self.items.len: if(self.items[i].active): let index = self.createIndex(i, 0, nil) + defer: index.delete self.items[i].active = false self.dataChanged(index, index, @[ModelRole.Active.int]) if(self.items[i].id == id): let index = self.createIndex(i, 0, nil) + defer: index.delete self.items[i].active = true self.items[i].loaderActive = true @@ -349,6 +352,7 @@ QtObject: for i in 0 ..< self.items.len: if(self.items[i].sectionType == sectionType): let index = self.createIndex(i, 0, nil) + defer: index.delete self.items[i].enabled = value self.dataChanged(index, index, @[ModelRole.Enabled.int]) else: @@ -386,16 +390,30 @@ QtObject: for i in 0 ..< self.items.len: if(self.items[i].id == id): let index = self.createIndex(i, 0, nil) + defer: index.delete self.items[i].hasNotification = hasNotification self.items[i].notificationsCount = notificationsCount self.dataChanged(index, index, @[ModelRole.HasNotification.int, ModelRole.NotificationsCount.int]) self.notificationsCountChanged() return + proc incrementNotifications*(self: SectionModel, id: string) = + let index = self.getItemIndex(id) + if (index == -1): + return + + self.items[index].hasNotification = true + self.items[index].notificationsCount = self.items[index].notificationsCount + 1 + let modelIndex = self.createIndex(index, 0, nil) + defer: modelIndex.delete + self.dataChanged(modelIndex, modelIndex, @[ModelRole.HasNotification.int, ModelRole.NotificationsCount.int]) + self.notificationsCountChanged() + proc appendCommunityToken*(self: SectionModel, id: string, item: TokenItem) = for i in 0 ..< self.items.len: if(self.items[i].id == id): let index = self.createIndex(i, 0, nil) + defer: index.delete self.items[i].appendCommunityToken(item) self.dataChanged(index, index, @[ModelRole.CommunityTokensModel.int]) return @@ -443,5 +461,6 @@ QtObject: for i in 0 ..< self.items.len: if(self.items[i].id == sectionId): let index = self.createIndex(i, 0, nil) + defer: index.delete self.items[i].loaderActive = false self.dataChanged(index, index, @[ModelRole.LoaderActive.int]) \ No newline at end of file diff --git a/src/app_service/service/chat/service.nim b/src/app_service/service/chat/service.nim index e078e87043..0fc2ff4857 100644 --- a/src/app_service/service/chat/service.nim +++ b/src/app_service/service/chat/service.nim @@ -277,7 +277,6 @@ QtObject: i.inc() return -1 - proc chatsWithCategoryHaveUnreadMessages*(self: Service, communityId: string, categoryId: string): bool = if communityId == "" or categoryId == "": return false @@ -287,6 +286,20 @@ QtObject: return true return false + proc sectionUnreadMessagesAndMentionsCount*(self: Service, communityId: string): + tuple[unviewedMessagesCount: int, unviewedMentionsCount: int] = + if communityId == "": + return + + result.unviewedMentionsCount = 0 + result.unviewedMessagesCount = 0 + for chat in self.channelGroups[communityId].chats: + result.unviewedMentionsCount += chat.unviewedMentionsCount + if chat.muted: + continue + if chat.unviewedMessagesCount > 0: + result.unviewedMessagesCount = result.unviewedMessagesCount + chat.unviewedMessagesCount + proc updateOrAddChat*(self: Service, chat: ChatDto) = # status-go doesn't seem to preserve categoryIDs from chat # objects received via new messages. So we rely on what we diff --git a/src/app_service/service/community/service.nim b/src/app_service/service/community/service.nim index fa728cd065..50ea0fba61 100644 --- a/src/app_service/service/community/service.nim +++ b/src/app_service/service/community/service.nim @@ -727,7 +727,7 @@ QtObject: ## Returns all chats belonging to the community with passed `communityId`, sorted by position. ## Returned chats are sorted by position following set `order` parameter. if(not self.communities.contains(communityId)): - error "trying to get all community chats for an unexisting community id" + error "trying to get all community chats for an unexisting community id", communityId return result = self.communities[communityId].chats diff --git a/src/app_service/service/message/async_tasks.nim b/src/app_service/service/message/async_tasks.nim index b0d008c2af..e00c7daa35 100644 --- a/src/app_service/service/message/async_tasks.nim +++ b/src/app_service/service/message/async_tasks.nim @@ -110,11 +110,10 @@ type const asyncMarkAllMessagesReadTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = let arg = decode[AsyncMarkAllMessagesReadTaskArg](argEncoded) - discard status_go.markAllMessagesFromChatWithIdAsRead(arg.chatId) - + let response = status_go.markAllMessagesFromChatWithIdAsRead(arg.chatId) let responseJson = %*{ "chatId": arg.chatId, - "error": "" + "error": response.error } arg.finish(responseJson) #################################################