refactor(@desktop/chat): reading a channel message does not mark it as read

- non active chats/channels are made bold if there are new messages inside them
a dot badge is added to the chat/community section if it's needed in that case
- for non active chats/channels count badge is added if there are new mentions
inside them, a count badge is added to the chat/community section if it's needed in
that case
- selecting chat/channel marks messages from it as read and update its displaying
and dot/count badge of chat/community section accordingly

Fixes #4403
This commit is contained in:
Sale Djenic 2022-01-12 20:52:35 +01:00
parent f0ca1a4cc3
commit 46211c38ee
13 changed files with 130 additions and 32 deletions

View File

@ -253,4 +253,7 @@ method onContactDetailsUpdated*(self: Module, contactId: string) =
self.view.pinnedModel().updateSenderDetails(contactId, updatedContact.displayName, updatedContact.details.localNickname,
updatedContact.icon, updatedContact.isIdenticon)
if(self.controller.getMyChatId() == contactId):
self.view.updateChatDetails(updatedContact.displayName, updatedContact.icon, updatedContact.isIdenticon)
self.view.updateChatDetailsNameAndIcon(updatedContact.displayName, updatedContact.icon, updatedContact.isIdenticon)
method onNotificationsUpdated*(self: Module, hasUnreadMessages: bool, notificationCount: int) =
self.view.updateChatDetailsNotifications(hasUnreadMessages, notificationCount)

View File

@ -10,4 +10,7 @@ method isLoaded*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method getModuleAsVariant*(self: AccessInterface): QVariant {.base.} =
raise newException(ValueError, "No implementation available")
method onNotificationsUpdated*(self: AccessInterface, hasUnreadMessages: bool, notificationCount: int) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -94,10 +94,14 @@ QtObject:
proc setMuted*(self: View, muted: bool) =
self.chatDetails.setMuted(muted)
proc updateChatDetails*(self: View, name, icon: string, isIdenticon: bool) =
proc updateChatDetailsNameAndIcon*(self: View, name, icon: string, isIdenticon: bool) =
self.chatDetails.setName(name)
self.chatDetails.setIcon(icon, isIdenticon)
proc updateChatDetailsNotifications*(self: View, hasUnreadMessages: bool, notificationCount: int) =
self.chatDetails.setHasUnreadMessages(hasUnreadMessages)
self.chatDetails.setNotificationCount(notificationCount)
proc getChatDetails(self: View): QVariant {.slot.} =
return self.chatDetailsVariant
QtProperty[QVariant] chatDetails:

View File

@ -46,6 +46,14 @@ method delete*(self: Controller) =
discard
method init*(self: Controller) =
self.events.on(SIGNAL_NEW_MESSAGE_RECEIVED) do(e: Args):
let args = MessagesArgs(e)
if(self.isCommunitySection and args.chatType != ChatType.CommunityChat or
not self.isCommunitySection and args.chatType == ChatType.CommunityChat):
return
self.delegate.onNewMessagesReceived(args.chatId, args.unviewedMessagesCount, args.unviewedMentionsCount,
args.messages)
self.events.on(chat_service.SIGNAL_CHAT_MUTED) do(e:Args):
let args = chat_service.ChatArgs(e)
self.delegate.onChatMuted(args.chatId)

View File

@ -1,6 +1,6 @@
import NimQml, Tables, strutils, strformat, json
import item, base_item, sub_model
from ../../../../app_service/service/chat/dto/chat import ChatType
import item, sub_item, base_item, sub_model
type
ModelRole {.pure.} = enum
@ -161,6 +161,12 @@ QtObject:
if(it.id == id):
return it
proc getSubItemById*(self: Model, id: string): SubItem =
for it in self.items:
let item = it.subItems.getItemById(id)
if(not item.isNil):
return item
proc setActiveItemSubItem*(self: Model, id: string, subItemId: string) =
for i in 0 ..< self.items.len:
self.items[i].setActiveSubItem(subItemId)
@ -193,19 +199,11 @@ QtObject:
self.dataChanged(index, index, @[ModelRole.Muted.int])
return
var found = false
if self.items[i].subItems.muteUnmuteItemById(id, mute):
return
proc setHasUnreadMessage*(self: Model, id: string, value: bool) =
for i in 0 ..< self.items.len:
if(self.items[i].id == id):
let index = self.createIndex(i, 0, nil)
self.items[i].BaseItem.hasUnreadMessages = value
self.dataChanged(index, index, @[ModelRole.HasUnreadMessages.int])
return
proc updateItemDetails*(self: Model, id, name, icon: string, isIdenticon: bool) =
## This updates only first level items, it doesn't update subitems, since subitems cannot have custom icon.
for i in 0 ..< self.items.len:
if(self.items[i].id == id):
self.items[i].BaseItem.name = name
@ -213,4 +211,29 @@ QtObject:
self.items[i].BaseItem.isIdenticon = isIdenticon
let index = self.createIndex(i, 0, nil)
self.dataChanged(index, index, @[ModelRole.Name.int, ModelRole.Icon.int, ModelRole.IsIdenticon.int])
return
return
proc updateNotificationsForItemOrSubItemById*(self: Model, id: string, hasUnreadMessages: bool,
notificationsCount: int) =
for i in 0 ..< self.items.len:
if(self.items[i].id == id):
let index = self.createIndex(i, 0, nil)
self.items[i].BaseItem.hasUnreadMessages = hasUnreadMessages
self.items[i].BaseItem.notificationsCount = notificationsCount
self.dataChanged(index, index, @[ModelRole.HasUnreadMessages.int, ModelRole.NotificationsCount.int])
return
if self.items[i].subItems.updateNotificationsForItemById(id, hasUnreadMessages, notificationsCount):
return
proc getAllNotifications*(self: Model): tuple[hasNotifications: bool, notificationsCount: int] =
result.hasNotifications = false
result.notificationsCount = 0
for i in 0 ..< self.items.len:
# if it's category item type is set to `ChatType.Unknown`
# (in one point of time we may start maintaining notifications per category as well)
if(self.items[i].BaseItem.`type` == ChatType.Unknown.int):
continue
result.hasNotifications = result.hasNotifications or self.items[i].BaseItem.hasUnreadMessages
result.notificationsCount = result.notificationsCount + self.items[i].BaseItem.notificationsCount

View File

@ -108,7 +108,7 @@ proc buildChatUI(self: Module, events: EventEmitter,
let item = initItem(c.id, chatName, chatImage, isIdenticon, c.color, c.description, c.chatType.int, amIChatAdmin,
hasNotification, notificationsCount, c.muted, false, 0)
self.view.appendItem(item)
self.view.chatsModel().appendItem(item)
self.addSubmodule(c.id, false, isUsersListAvailable, events, settingsService, contactService, chatService,
communityService, messageService)
@ -141,7 +141,7 @@ proc buildCommunityUI(self: Module, events: EventEmitter,
let amIChatAdmin = comm.admin
let channelItem = initItem(chatDto.id, chatDto.name, chatDto.identicon, false, chatDto.color, chatDto.description,
chatDto.chatType.int, amIChatAdmin, hasNotification, notificationsCount, chatDto.muted, false, c.position)
self.view.appendItem(channelItem)
self.view.chatsModel().appendItem(channelItem)
self.addSubmodule(chatDto.id, true, true, events, settingsService, contactService, chatService, communityService,
messageService)
@ -184,7 +184,7 @@ proc buildCommunityUI(self: Module, events: EventEmitter,
var categoryItem = initItem(cat.id, cat.name, "", false, "", "", ChatType.Unknown.int, false,
hasNotificationPerCategory, notificationsCountPerCategory, false, false, cat.position)
categoryItem.prependSubItems(categoryChannels)
self.view.appendItem(categoryItem)
self.view.chatsModel().appendItem(categoryItem)
self.setActiveItemSubItem(selectedItemId, selectedSubItemId)
@ -260,10 +260,13 @@ method activeItemSubItemSet*(self: Module, itemId: string, subItemId: string) =
# to any category have empty `subItemId`
let subItem = item.subItems.getItemById(subItemId)
# update view maintained by this module
self.view.chatsModel().setActiveItemSubItem(itemId, subItemId)
self.view.activeItemSubItemSet(item, subItem)
# notify parent module about active chat/channel
self.delegate.onActiveChatChange(self.controller.getMySectionId(), self.controller.getActiveChatId())
# update notifications caused by setting active chat/channel
self.controller.markAllMessagesRead(self.controller.getActiveChatId())
method getModuleAsVariant*(self: Module): QVariant =
return self.viewVariant
@ -316,7 +319,7 @@ method addNewChat*(
self.addSubmodule(chatDto.id, false, isUsersListAvailable, events, settingsService, contactService, chatService,
communityService, messageService)
self.chatContentModules[chatDto.id].load()
self.view.appendItem(item)
self.view.chatsModel().appendItem(item)
# make new added chat active one
self.setActiveItemSubItem(item.id, "")
@ -325,7 +328,7 @@ method removeChat*(self: Module, chatId: string) =
if(not self.chatContentModules.contains(chatId)):
return
self.view.removeItem(chatId)
self.view.chatsModel().removeItemById(chatId)
self.removeSubmodule(chatId)
# remove active state form the removed chat (if applicable)
@ -342,6 +345,16 @@ method createOneToOneChat*(self: Module, chatId: string, ensName: string) =
self.controller.createOneToOneChat(chatId, ensName)
proc updateNotifications(self: Module, chatId: string, unviewedMessagesCount: int, unviewedMentionsCount: int) =
let hasUnreadMessages = unviewedMessagesCount > 0
# update model of this module (appropriate chat from the chats list (chats model))
self.view.chatsModel().updateNotificationsForItemOrSubItemById(chatId, hasUnreadMessages, unviewedMentionsCount)
# update child module
self.chatContentModules[chatId].onNotificationsUpdated(hasUnreadMessages, unviewedMentionsCount)
# update parent module
let (sectionHasUnreadMessages, sectionNotificationCount) = self.view.chatsModel().getAllNotifications()
self.delegate.onNotificationsUpdated(self.controller.getMySectionId(), sectionHasUnreadMessages, sectionNotificationCount)
method leaveChat*(self: Module, chatId: string) =
self.controller.leaveChat(chatId)
@ -358,7 +371,7 @@ method onChatUnmuted*(self: Module, chatId: string) =
self.view.chatsModel().muteUnmuteItemOrSubItemById(chatId, false)
method onMarkAllMessagesRead*(self: Module, chatId: string) =
self.view.chatsModel().setHasUnreadMessage(chatId, value=false)
self.updateNotifications(chatId, unviewedMessagesCount=0, unviewedMentionsCount=0)
method markAllMessagesRead*(self: Module, chatId: string) =
self.controller.markAllMessagesRead(chatId)
@ -399,4 +412,12 @@ method onContactBlocked*(self: Module, publicKey: string) =
method onContactDetailsUpdated*(self: Module, publicKey: string) =
let (chatName, chatImage, isIdenticon) = self.controller.getOneToOneChatNameAndImage(publicKey)
self.view.chatsModel().updateItemDetails(publicKey, chatName, chatImage, isIdenticon)
self.view.chatsModel().updateItemDetails(publicKey, chatName, chatImage, isIdenticon)
method onNewMessagesReceived*(self: Module, chatId: string, unviewedMessagesCount: int, unviewedMentionsCount: int,
messages: seq[MessageDto]) =
let activeChatId = self.controller.getActiveChatId()
if(activeChatId == chatId):
self.controller.markAllMessagesRead(chatId)
else:
self.updateNotifications(chatId, unviewedMessagesCount, unviewedMentionsCount)

View File

@ -7,6 +7,10 @@ method addNewChat*(self: AccessInterface, chatDto: ChatDto, events: EventEmitter
messageService: message_service.Service) {.base.} =
raise newException(ValueError, "No implementation available")
method onNewMessagesReceived*(self: AccessInterface, chatId: string, unviewedMessagesCount: int,
unviewedMentionsCount: int, messages: seq[MessageDto]) {.base.} =
raise newException(ValueError, "No implementation available")
method onChatMuted*(self: AccessInterface, chatId: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -189,5 +189,15 @@ QtObject:
self.items[i].BaseItem.muted = mute
self.dataChanged(index, index, @[ModelRole.Muted.int])
return true
return false
proc updateNotificationsForItemById*(self: SubModel, id: string, hasUnreadMessages: bool,
notificationsCount: int): bool =
for i in 0 ..< self.items.len:
if(self.items[i].id == id):
let index = self.createIndex(i, 0, nil)
self.items[i].BaseItem.hasUnreadMessages = hasUnreadMessages
self.items[i].BaseItem.notificationsCount = notificationsCount
self.dataChanged(index, index, @[ModelRole.HasUnreadMessages.int, ModelRole.NotificationsCount.int])
return true
return false

View File

@ -58,15 +58,6 @@ QtObject:
QtProperty[QVariant] contactRequestsModel:
read = getContactRequestsModel
proc appendItem*(self: View, item: Item) =
self.model.appendItem(item)
proc removeItem*(self: View, id: string) =
self.model.removeItemById(id)
proc prependItem*(self: View, item: Item) =
self.model.prependItem(item)
proc activeItemChanged*(self:View) {.signal.}
proc getActiveItem(self: View): QVariant {.slot.} =

View File

@ -480,6 +480,10 @@ method getCommunitySectionModule*[T](self: Module[T], communityId: string): QVar
method onActiveChatChange*[T](self: Module[T], sectionId: string, chatId: string) =
self.appSearchModule.onActiveChatChange(sectionId, chatId)
method onNotificationsUpdated[T](self: Module[T], sectionId: string, sectionHasUnreadMessages: bool,
sectionNotificationCount: int) =
self.view.model().udpateNotifications(sectionId, sectionHasUnreadMessages, sectionNotificationCount)
method getAppSearchModule*[T](self: Module[T]): QVariant =
self.appSearchModule.getModuleAsVariant()

View File

@ -5,4 +5,8 @@ method communitySectionDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method onActiveChatChange*(self: AccessInterface, sectionId: string, chatId: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onNotificationsUpdated*(self: AccessInterface, sectionId: string, sectionHasUnreadMessages: bool,
sectionNotificationCount: int) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -213,3 +213,12 @@ QtObject:
proc disableSection*(self: SectionModel, sectionType: SectionType) =
self.enableDisableSection(sectionType, false)
proc udpateNotifications*(self: SectionModel, id: string, hasNotification: bool, notificationsCount: int) =
for i in 0 ..< self.items.len:
if(self.items[i].id == id):
let index = self.createIndex(i, 0, nil)
self.items[i].hasNotification = hasNotification
self.items[i].notificationsCount = notificationsCount
self.dataChanged(index, index, @[ModelRole.HasNotification.int, ModelRole.NotificationsCount.int])
return

View File

@ -35,6 +35,9 @@ include async_tasks
type
MessagesArgs* = ref object of Args
chatId*: string
chatType*: ChatType
unviewedMessagesCount*: int
unviewedMentionsCount*: int
messages*: seq[MessageDto]
MessagesLoadedArgs* = ref object of Args
@ -98,6 +101,12 @@ QtObject:
# The first element from the `receivedData.chats` array contains details about the chat a messages received in
# `receivedData.messages` refer to.
let chatId = receivedData.chats[0].id
let chatType = receivedData.chats[0].chatType
let unviewedMessagesCount = receivedData.chats[0].unviewedMessagesCount
let unviewedMentionsCount = receivedData.chats[0].unviewedMentionsCount
if(chatType == ChatType.Unknown):
error "error: new message with an unknown chat type received for chat id ", chatId
return
# In case of reply to a message we're receiving 2 messages in the `receivedData.messages` array (replied message
# and a message one replied to) but we actually need only a new replied message, that's why we need to filter
@ -113,7 +122,12 @@ QtObject:
for msgId in messagesOneRepliedTo:
removeMessageWithId(receivedData.messages, msgId)
let data = MessagesArgs(chatId: chatId, messages: receivedData.messages)
let data = MessagesArgs(chatId: chatId,
chatType: chatType,
unviewedMessagesCount: unviewedMessagesCount,
unviewedMentionsCount: unviewedMentionsCount,
messages: receivedData.messages
)
self.events.emit(SIGNAL_NEW_MESSAGE_RECEIVED, data)
# Handling pinned messages updates
if (receivedData.pinnedMessages.len > 0):