From 5352ba8c6ff1793ff28b13fd271e035aea5055d4 Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Wed, 8 Feb 2023 17:58:14 -0500 Subject: [PATCH] refactor(chat-model): simplify chat model and put category as an Item Fixes #9494 --- .../main/chat_section/io_interface.nim | 3 - src/app/modules/main/chat_section/item.nim | 11 - src/app/modules/main/chat_section/model.nim | 160 ++++---- src/app/modules/main/chat_section/module.nim | 100 ++--- src/app/modules/main/chat_section/view.nim | 6 - src/app_service/service/community/service.nim | 58 ++- .../src/StatusQ/Components/StatusChatList.qml | 381 +++++++++--------- .../Chat/views/CommunityColumnView.qml | 40 +- 8 files changed, 346 insertions(+), 413 deletions(-) diff --git a/src/app/modules/main/chat_section/io_interface.nim b/src/app/modules/main/chat_section/io_interface.nim index e3e77c8015..c9c5bb4a80 100644 --- a/src/app/modules/main/chat_section/io_interface.nim +++ b/src/app/modules/main/chat_section/io_interface.nim @@ -130,9 +130,6 @@ method onReorderChat*(self: AccessInterface, chattId: string, position: int, new method onReorderCategory*(self: AccessInterface, catId: string, position: int) {.base.} = raise newException(ValueError, "No implementation available") -method onCommunityCategoryChannelChanged*(self: AccessInterface, chatId: string, newCategoryIdForChat: string) {.base.} = - raise newException(ValueError, "No implementation available") - method onCommunityCategoryCreated*(self: AccessInterface, category: Category, chats: seq[ChatDto]) {.base.} = raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/main/chat_section/item.nim b/src/app/modules/main/chat_section/item.nim index 9eef4aff65..baf73ca089 100644 --- a/src/app/modules/main/chat_section/item.nim +++ b/src/app/modules/main/chat_section/item.nim @@ -27,7 +27,6 @@ type active: bool position: int categoryId: string - categoryName: string categoryPosition: int categoryOpened: bool highlight: bool @@ -51,7 +50,6 @@ proc initItem*( active: bool, position: int, categoryId: string = "", - categoryName: string = "", categoryPosition: int = -1, colorId: int = 0, colorHash: seq[ColorHashSegment] = @[], @@ -80,7 +78,6 @@ proc initItem*( result.active = active result.position = position result.categoryId = categoryId - result.categoryName = categoryName result.categoryPosition = categoryPosition result.highlight = highlight result.categoryOpened = categoryOpened @@ -106,7 +103,6 @@ proc `$`*(self: Item): string = active: {$self.active}, position: {$self.position}, categoryId: {$self.categoryId}, - categoryName: {$self.categoryName}, categoryPosition: {$self.categoryPosition}, highlight: {$self.highlight}, categoryOpened: {$self.categoryOpened}, @@ -132,7 +128,6 @@ proc toJsonNode*(self: Item): JsonNode = "active": self.active, "position": self.position, "categoryId": self.categoryId, - "categoryName": self.categoryName, "categoryPosition": self.categoryPosition, "highlight": self.highlight, "categoryOpened": self.categoryOpened, @@ -236,12 +231,6 @@ proc categoryId*(self: Item): string = proc `categoryId=`*(self: var Item, value: string) = self.categoryId = value -proc categoryName*(self: Item): string = - self.categoryName - -proc `categoryName=`*(self: var Item, value: string) = - self.categoryName = value - proc categoryPosition*(self: Item): int = self.categoryPosition diff --git a/src/app/modules/main/chat_section/model.nim b/src/app/modules/main/chat_section/model.nim index 87b00d250c..79d8289211 100644 --- a/src/app/modules/main/chat_section/model.nim +++ b/src/app/modules/main/chat_section/model.nim @@ -1,6 +1,6 @@ import NimQml, Tables, strutils, strformat, json, sequtils import ../../../../app_service/common/types -from ../../../../app_service/service/chat/dto/chat import ChatType +import ../../../../app_service/service/chat/dto/chat from ../../../../app_service/service/contacts/dto/contacts import TrustStatus import item @@ -24,12 +24,12 @@ type Active Position CategoryId - CategoryName CategoryPosition Highlight CategoryOpened TrustStatus OnlineStatus + IsCategory QtObject: type @@ -91,12 +91,12 @@ QtObject: ModelRole.Active.int:"active", ModelRole.Position.int:"position", ModelRole.CategoryId.int:"categoryId", - ModelRole.CategoryName.int:"categoryName", ModelRole.CategoryPosition.int:"categoryPosition", ModelRole.Highlight.int:"highlight", ModelRole.CategoryOpened.int:"categoryOpened", ModelRole.TrustStatus.int:"trustStatus", ModelRole.OnlineStatus.int:"onlineStatus", + ModelRole.IsCategory.int:"isCategory", }.toTable method data(self: Model, index: QModelIndex, role: int): QVariant = @@ -146,8 +146,6 @@ QtObject: result = newQVariant(item.position) of ModelRole.CategoryId: result = newQVariant(item.categoryId) - of ModelRole.CategoryName: - result = newQVariant(item.categoryName) of ModelRole.CategoryPosition: result = newQVariant(item.categoryPosition) of ModelRole.Highlight: @@ -158,6 +156,8 @@ QtObject: result = newQVariant(item.trustStatus.int) of ModelRole.OnlineStatus: result = newQVariant(item.onlineStatus.int) + of ModelRole.IsCategory: + result = newQVariant(item.`type` == CATEGORY_TYPE) proc appendItem*(self: Model, item: Item) = let parentModelIndex = newQModelIndex() @@ -177,32 +177,6 @@ QtObject: idx.inc return -1 - proc getItemIdxByCategory*(self: Model, categoryId: string): int = - var idx = 0 - for it in self.items: - if it.categoryId == categoryId: - return idx - idx.inc - return -1 - - proc hasEmptyChatItem*(self: Model, categoryId: string): bool = - for it in self.items: - if it.id == categoryId: - return true - return false - - proc getCategoryNameForCategoryId*(self: Model, categoryId: string): string {.slot.} = - let index = self.getItemIdxByCategory(categoryId) - if index == -1: - return - return self.items[index].categoryName - - proc getCategoryOpenedForCategoryId*(self: Model, categoryId: string): bool {.slot.} = - let index = self.getItemIdxByCategory(categoryId) - if index == -1: - return true # Default to true just in case - return self.items[index].categoryOpened - proc changeCategoryOpened*(self: Model, categoryId: string, opened: bool) {.slot.} = for i in 0 ..< self.items.len: if self.items[i].categoryId == categoryId: @@ -245,19 +219,15 @@ QtObject: if(it.id == id): return it - proc categoryHasUnreadMessagesChanged*(self: Model, categoryId: string, hasUnread: bool) {.signal.} - - proc getCategoryHasUnreadMessages*(self: Model, categoryId: string): bool {.slot.} = - return self.items.anyIt(it.categoryId == categoryId and it.hasUnreadMessages) - - proc setCategoryHasUnreadMessages*(self: Model, categoryId: string, value: bool) = - self.categoryHasUnreadMessagesChanged(categoryId, value) - - proc getCategoryAndPosition*(self: Model, id: string): (string, int) = - let item = self.getItemById(id) - if (not item.isNil): - return ("", item.position) - return ("", -1) + proc setCategoryHasUnreadMessages*(self: Model, categoryId: string, unread: bool) = + let index = self.getItemIdxById(categoryId) + if index == -1: + return + if self.items[index].hasUnreadMessages == unread: + return + self.items[index].hasUnreadMessages = unread + let modelIndex = self.createIndex(index, 0, nil) + self.dataChanged(modelIndex, modelIndex, @[ModelRole.HasUnreadMessages.int]) proc setActiveItem*(self: Model, id: string) = for i in 0 ..< self.items.len: @@ -339,65 +309,102 @@ QtObject: ModelRole.Icon.int, ]) - proc updateItemsWithCategoryDetailById*( + proc updateCategoryDetailsById*( self: Model, - ids: seq[string], categoryId, newCategoryName: string, newCategoryPosition: int, ) = - var indexesToDelete: seq[int] = @[] + let categoryIndex = self.getItemIdxById(categoryId) + if categoryIndex == -1: + return + self.items[categoryIndex].name = newCategoryName + self.items[categoryIndex].categoryPosition = newCategoryPosition + let modelIndex = self.createIndex(categoryIndex, 0, nil) + self.dataChanged(modelIndex, modelIndex, @[ + ModelRole.Name.int, + ModelRole.CategoryPosition.int, + ]) + + proc updateItemsWithCategoryDetailsById*( + self: Model, + chats: seq[ChatDto], + categoryId, + newCategoryName: string, + newCategoryPosition: int, + ) = for i in 0 ..< self.items.len: var item = self.items[i] - var hasCategory = item.categoryId == categoryId + if item.`type` == CATEGORY_TYPE: + continue + var hadCategory = item.categoryId == categoryId + var nowHasCategory = false var found = false - for id in ids: - if item.id != id: + for chat in chats: + if item.id != chat.id: continue found = true + nowHasCategory = chat.categoryId == categoryId + item.position = chat.position item.categoryId = categoryId - item.categoryName = newCategoryName item.categoryPosition = newCategoryPosition let modelIndex = self.createIndex(i, 0, nil) - # Also signal on CategoryId because the section header only knows the categoryId self.dataChanged(modelIndex, modelIndex, @[ + ModelRole.Position.int, ModelRole.CategoryId.int, - ModelRole.CategoryName.int, ModelRole.CategoryPosition.int, ]) break - if hasCategory and not found: - # This item was removed from the category - if (item.`type` == CATEGORY_TYPE): - # It was an empty item to show the category and it isn't needed anymore - indexesToDelete.add(i) - continue - + if (hadCategory and not found and not nowHasCategory) or (hadCategory and found and not nowHasCategory): item.categoryId = "" - item.categoryName = "" item.categoryPosition = -1 + echo "category changed ", item.name + item.categoryOpened = true let modelIndex = self.createIndex(i, 0, nil) self.dataChanged(modelIndex, modelIndex, @[ ModelRole.CategoryId.int, - ModelRole.CategoryName.int, ModelRole.CategoryPosition.int, + ModelRole.CategoryOpened.int, ]) - # Go through indexesToDelete from the highest to the bottom, that way the indexes stay correct - var f = indexesToDelete.len - 1 - while f > -1: - self.removeItemByIndex(indexesToDelete[f]) - dec(f) + proc removeCategory*( + self: Model, + categoryId: string, + chats: seq[ChatDto] + ) = + self.removeItemById(categoryId) - - proc renameCategoryOnItems*(self: Model, categoryId, newName: string) = for i in 0 ..< self.items.len: var item = self.items[i] if item.categoryId != categoryId: continue - item.categoryName = newName - let modelIndex = self.createIndex(i, 0, nil) - self.dataChanged(modelIndex, modelIndex, @[ModelRole.CategoryId.int, ModelRole.CategoryName.int]) + + for chat in chats: + if chat.id != item.id: + continue + + item.position = chat.position + item.categoryId = "" + item.categoryPosition = -1 + item.categoryOpened = true + let modelIndex = self.createIndex(i, 0, nil) + self.dataChanged(modelIndex, modelIndex, @[ + ModelRole.Position.int, + ModelRole.CategoryId.int, + ModelRole.CategoryPosition.int, + ModelRole.CategoryOpened.int, + ]) + break + + proc renameCategory*(self: Model, categoryId, newName: string) = + let index = self.getItemIdxById(categoryId) + if index == -1: + return + if self.items[index].name == newName: + return + self.items[index].name = newName + let modelIndex = self.createIndex(index, 0, nil) + self.dataChanged(modelIndex, modelIndex, @[ModelRole.Name.int]) proc renameItemById*(self: Model, id, name: string) = let index = self.getItemIdxById(id) @@ -451,7 +458,6 @@ QtObject: chatId: string, position: int, newCategoryId: string, - newCategoryName: string, newCategoryPosition: int, ) = let index = self.getItemIdxById(chatId) @@ -460,11 +466,9 @@ QtObject: var roles = @[ModelRole.Position.int] if(self.items[index].categoryId != newCategoryId): self.items[index].categoryId = newCategoryId - self.items[index].categoryName = newCategoryName self.items[index].categoryPosition = newCategoryPosition roles = roles.concat(@[ ModelRole.CategoryId.int, - ModelRole.CategoryName.int, ModelRole.CategoryPosition.int, ]) self.items[index].position = position @@ -497,9 +501,3 @@ QtObject: return return self.items[index].toJsonNode() - - proc getItemPartOfCategoryAsJsonById*(self: Model, categoryId: string): JsonNode = - let index = self.getItemIdxByCategory(categoryId) - if index == -1: - return - return self.items[index].toJsonNode() diff --git a/src/app/modules/main/chat_section/module.nim b/src/app/modules/main/chat_section/module.nim index 88d96e8833..9ea93ad011 100644 --- a/src/app/modules/main/chat_section/module.nim +++ b/src/app/modules/main/chat_section/module.nim @@ -29,8 +29,6 @@ import ../../../../app_service/service/contacts/dto/contacts as contacts_dto export io_interface -const CATEGORY_ID_PREFIX = "cat-" - logScope: topics = "chat-section-module" @@ -129,26 +127,24 @@ proc removeSubmodule(self: Module, chatId: string) = self.chatContentModules.del(chatId) -proc addEmptyChatItemForCategory(self: Module, category: Category) = - # Add an empty chat item that has the category info +proc addCategoryItem(self: Module, category: Category, amIAdmin: bool) = let emptyChatItem = chat_item.initItem( - id = CATEGORY_ID_PREFIX & category.id, - name = "", + id = category.id, + category.name, icon = "", color = "", emoji = "", description = "", `type` = chat_item.CATEGORY_TYPE, - amIChatAdmin = false, + amIAdmin, lastMessageTimestamp = 0, hasUnreadMessages = false, notificationsCount = 0, muted = false, blocked = false, active = false, - position = 99, + position = -1, # Set position as -1, so that the Category Item is on top of its Channels category.id, - category.name, category.position, ) self.view.chatsModel().appendItem(emptyChatItem) @@ -172,6 +168,10 @@ proc buildChatSectionUI( # If a category doesn't have a chat, we add it as an empty chat var categoriesWithAssociatedItems: seq[string] = @[] + for categoryDto in channelGroup.categories: + # Add items for the categories. We use a special type to identify categories + self.addCategoryItem(categoryDto, channelGroup.admin) + for chatDto in channelGroup.chats: let hasNotification = not chatDto.muted and (chatDto.unviewedMessagesCount > 0 or chatDto.unviewedMentionsCount > 0) let notificationsCount = chatDto.unviewedMentionsCount @@ -184,9 +184,7 @@ proc buildChatSectionUI( let isUsersListAvailable = chatDto.chatType != ChatType.OneToOne var blocked = false let belongToCommunity = chatDto.communityId != "" - var categoryName = "" var categoryPosition = -1 - var chatPosition = chatDto.position if chatDto.chatType == ChatType.OneToOne: let contactDetails = self.controller.getContactDetails(chatDto.id) @@ -218,11 +216,8 @@ proc buildChatSectionUI( for category in channelGroup.categories: if category.id == chatDto.categoryId: categoriesWithAssociatedItems.add(chatDto.categoryId) - categoryName = category.name categoryPosition = category.position break - if categoryName == "": - error "No category found in the channel group for chat", chatName=chatDto.name, categoryId=chatDto.categoryId let newChatItem = chat_item.initItem( chatDto.id, @@ -241,7 +236,6 @@ proc buildChatSectionUI( isActive, chatDto.position, chatDto.categoryId, - categoryName, categoryPosition, colorId, colorHash, @@ -265,19 +259,6 @@ proc buildChatSectionUI( mailserversService, ) - # Loop channelGroup categories to see if some of them don't have a chat - if categoriesWithAssociatedItems.len < channelGroup.categories.len: - for category in channelGroup.categories: - var found = false - for categoryId in categoriesWithAssociatedItems: - if categoryId == category.id: - found = true - break - if found: - continue - - self.addEmptyChatItemForCategory(category) - self.setActiveItem(selectedItemId) proc createItemFromPublicKey(self: Module, publicKey: string): UserItem = @@ -473,7 +454,6 @@ method addNewChat*( var colorHash: ColorHashDto = @[] var colorId: int = 0 var onlineStatus = OnlineStatus.Inactive - var categoryName = "" var categoryPosition = -1 var isUsersListAvailable = true @@ -495,7 +475,6 @@ method addNewChat*( if category.id == "": error "No category found for chat", chatName=chatDto.name, categoryId=chatDto.categoryId else: - categoryName = category.name categoryPosition = category.position let chat_item = chat_item.initItem( @@ -515,7 +494,6 @@ method addNewChat*( setChatAsActive, chatDto.position, chatDto.categoryId, - categoryName, categoryPosition, colorId, colorHash, @@ -565,44 +543,36 @@ method removeCommunityChat*(self: Module, chatId: string) = self.controller.removeCommunityChat(chatId) method onCommunityCategoryEdited*(self: Module, cat: Category, chats: seq[ChatDto]) = + # Update category itself + self.view.chatsModel().updateCategoryDetailsById( + cat.id, + cat.name, + cat.position + ) # Update chat items that have that category - let chatIds = chats.filterIt(it.categoryId == cat.id).mapIt(it.id) - self.view.chatsModel().updateItemsWithCategoryDetailById( - chatIds, + self.view.chatsModel().updateItemsWithCategoryDetailsById( + chats, cat.id, cat.name, cat.position, ) - - if chatIds.len == 0: - # New category with no chats associated, we created an empty chatItem with the category info - self.addEmptyChatItemForCategory(cat) - return method onCommunityCategoryCreated*(self: Module, cat: Category, chats: seq[ChatDto]) = if (self.doesCatOrChatExist(cat.id)): return - if chats.len == 0: - # New category with no chats associated, we created an empty chatItem with the category info - self.addEmptyChatItemForCategory(cat) - return - - # Consider the new category as an edit, we just change the chat items with the new cat info - self.onCommunityCategoryEdited(cat, chats) + # TODO get admin status + self.addCategoryItem(cat, false) + # Update chat items that now belong to that category + self.view.chatsModel().updateItemsWithCategoryDetailsById( + chats, + cat.id, + cat.name, + cat.position, + ) method onCommunityCategoryDeleted*(self: Module, cat: Category, chats: seq[ChatDto]) = - # Update chat positions and remove association with category - for chat in chats: - self.view.chatsModel().reorderChatById( - chat.id, - chat.position, - newCategoryId = "", - newCategoryName = "", - newCategoryPosition = -1, - ) - self.view.chatsModel().removeItemById(CATEGORY_ID_PREFIX & cat.id) - + self.view.chatsModel().removeCategory(cat.id, chats) method setFirstChannelAsActive*(self: Module) = if(self.view.chatsModel().getCount() == 0): @@ -611,31 +581,19 @@ method setFirstChannelAsActive*(self: Module) = let chat_item = self.view.chatsModel().getItemAtIndex(0) self.setActiveItem(chat_item.id) -method onCommunityCategoryChannelChanged*(self: Module, channelId: string, newCategoryIdForChat: string) = - if channelId == self.controller.getActiveChatId(): - if newCategoryIdForChat.len > 0: - self.setActiveItem(channelId) - else: - self.setActiveItem(channelId) - method onReorderChat*(self: Module, chatId: string, position: int, newCategoryIdForChat: string, prevCategoryId: string, prevCategoryDeleted: bool) = var newCategoryName = "" var newCategoryPos = -1 if newCategoryIdForChat != "": let newCategory = self.controller.getCommunityCategoryDetails(self.controller.getMySectionId(), newCategoryIdForChat) - newCategoryName = newCategory.name newCategoryPos = newCategory.position - self.view.chatsModel().reorderChatById(chatId, position, newCategoryIdForChat, newCategoryName, newCategoryPos) - if prevCategoryId != "" and not prevCategoryDeleted: - if not self.view.chatsModel().hasEmptyChatItem(CATEGORY_ID_PREFIX & prevCategoryId): - let category = self.controller.getCommunityCategoryDetails(self.controller.getMySectionId(), prevCategoryId) - self.addEmptyChatItemForCategory(category) + self.view.chatsModel().reorderChatById(chatId, position, newCategoryIdForChat, newCategoryPos) method onReorderCategory*(self: Module, catId: string, position: int) = self.view.chatsModel().reorderCategoryById(catId, position) method onCategoryNameChanged*(self: Module, category: Category) = - self.view.chatsModel().renameCategoryOnItems(category.id, category.name) + self.view.chatsModel().renameCategory(category.id, category.name) method onCommunityChannelDeletedOrChatLeft*(self: Module, chatId: string) = if(not self.chatContentModules.contains(chatId)): diff --git a/src/app/modules/main/chat_section/view.nim b/src/app/modules/main/chat_section/view.nim index 4426bab51f..2dfde6f6be 100644 --- a/src/app/modules/main/chat_section/view.nim +++ b/src/app/modules/main/chat_section/view.nim @@ -153,12 +153,6 @@ QtObject: return return $jsonObj - proc getItemPartOfCategoryAsJsonById*(self: View, categoryId: string): string {.slot.} = - let jsonObj = self.model.getItemPartOfCategoryAsJsonById(categoryId) - if jsonObj == nil or jsonObj.kind != JObject: - return - return $jsonObj - proc muteChat*(self: View, chatId: string) {.slot.} = self.delegate.muteChat(chatId) diff --git a/src/app_service/service/community/service.nim b/src/app_service/service/community/service.nim index be64b1f4a2..16de588a8c 100644 --- a/src/app_service/service/community/service.nim +++ b/src/app_service/service/community/service.nim @@ -353,6 +353,26 @@ QtObject: self.joinedCommunities[settings.id].settings = settings self.events.emit(SIGNAL_COMMUNITY_EDITED, CommunityArgs(community: self.joinedCommunities[settings.id])) + proc checkForCategoryPropertyUpdates(self: Service, community: CommunityDto, prev_community: CommunityDto) = + for category in community.categories: + # id is present + let index = findIndexById(category.id, prev_community.categories) + if index == -1: + continue + # but something is different + let prev_category = prev_community.categories[index] + + if category.position != prev_category.position: + self.events.emit(SIGNAL_COMMUNITY_CATEGORY_REORDERED, + CommunityChatOrderArgs( + communityId: community.id, + categoryId: category.id, + position: category.position)) + if category.name != prev_category.name: + self.events.emit(SIGNAL_COMMUNITY_CATEGORY_NAME_EDITED, + CommunityCategoryArgs(communityId: community.id, category: category)) + + proc handleCommunityUpdates(self: Service, communities: seq[CommunityDto], updatedChats: seq[ChatDto], removedChats: seq[string]) = try: var community = communities[0] @@ -403,22 +423,7 @@ QtObject: # some property has changed else: - for category in community.categories: - # id is present - let index = findIndexById(category.id, prev_community.categories) - if index == -1: - continue - # but something is different - let prev_category = prev_community.categories[index] - if category.position != prev_category.position: - self.events.emit(SIGNAL_COMMUNITY_CATEGORY_REORDERED, - CommunityChatOrderArgs( - communityId: community.id, - categoryId: category.id, - position: category.position)) - if category.name != prev_category.name: - self.events.emit(SIGNAL_COMMUNITY_CATEGORY_NAME_EDITED, - CommunityCategoryArgs(communityId: community.id, category: category)) + self.checkForCategoryPropertyUpdates(community, prev_community) # channel was added if(community.chats.len > prev_community.chats.len): @@ -1118,6 +1123,11 @@ QtObject: raise newException(RpcException, "Error creating community category: " & error.message) if response.result != nil and response.result.kind != JNull: + self.checkForCategoryPropertyUpdates( + response.result["communityChanges"].getElems()[0]["community"].toCommunityDto, + self.joinedCommunities[communityId] + ) + var chats: seq[ChatDto] = @[] for chatId, v in response.result["communityChanges"].getElems()[0]["chatsModified"].pairs(): let fullChatId = communityId & chatId @@ -1168,6 +1178,15 @@ QtObject: self.chatService.updateOrAddChat(chatDetails) # we have to update chats stored in the chat service. chats.add(chatDetails) + # Update communities objects + let updatedCommunity = response.result["communities"][0].toCommunityDto + self.checkForCategoryPropertyUpdates( + updatedCommunity, + self.joinedCommunities[communityId] + ) + self.joinedCommunities[communityId] = updatedCommunity + self.allCommunities[communityId] = updatedCommunity + for k, v in response.result["communityChanges"].getElems()[0]["categoriesModified"].pairs(): let category = v.toCategory() self.events.emit(SIGNAL_COMMUNITY_CATEGORY_EDITED, @@ -1184,8 +1203,13 @@ QtObject: let error = Json.decode($response.error, RpcError) raise newException(RpcException, "Error deleting community category: " & error.message) - # Update communities objetcts + # Update communities objects let updatedCommunity = response.result["communities"][0].toCommunityDto + + self.checkForCategoryPropertyUpdates( + updatedCommunity, + self.joinedCommunities[communityId] + ) self.joinedCommunities[communityId] = updatedCommunity self.allCommunities[communityId] = updatedCommunity diff --git a/ui/StatusQ/src/StatusQ/Components/StatusChatList.qml b/ui/StatusQ/src/StatusQ/Components/StatusChatList.qml index b5af365faf..9ccb75408e 100644 --- a/ui/StatusQ/src/StatusQ/Components/StatusChatList.qml +++ b/ui/StatusQ/src/StatusQ/Components/StatusChatList.qml @@ -39,248 +39,240 @@ Item { model: root.model spacing: 0 interactive: height !== contentHeight - section.property: "categoryId" - section.criteria: ViewSection.FullString - section.delegate: Loader { - id: statusChatListCategoryItemLoader - active: !!section + delegate: Loader { + id: chatLoader - required property string section - - sourceComponent: StatusChatListCategoryItem { - id: statusChatListCategoryItem - - function setupPopup() { - categoryPopupMenuSlot.item.categoryId = statusChatListCategoryItemLoader.section + sourceComponent: { + if (model.isCategory) { + return categoryItemComponent } - - function toggleCategory() { - root.model.sourceModel.changeCategoryOpened(statusChatListCategoryItemLoader.section, !opened) - opened = root.model.sourceModel.getCategoryOpenedForCategoryId(statusChatListCategoryItemLoader.section) - } - - Connections { - enabled: categoryPopupMenuSlot.active && statusChatListCategoryItem.highlighted - target: categoryPopupMenuSlot.item - function onClosed() { - statusChatListCategoryItem.highlighted = false - statusChatListCategoryItem.menuButton.highlighted = false + return channelItemComponent + } + + Component { + id: categoryItemComponent + StatusChatListCategoryItem { + id: statusChatListCategoryItem + + function setupPopup() { + categoryPopupMenuSlot.item.categoryItem = model } - } - title: root.model.sourceModel.getCategoryNameForCategoryId(statusChatListCategoryItemLoader.section) - - opened: root.model.sourceModel.getCategoryOpenedForCategoryId(statusChatListCategoryItemLoader.section) - - sensor.pressAndHoldInterval: 150 - propagateTitleClicks: true // title click is handled as a normal click (fallthru) - showAddButton: showCategoryActionButtons - showMenuButton: !!root.popupMenu - highlighted: false//statusChatListCategory.dragged // FIXME DND - - hasUnreadMessages: root.model.sourceModel.getCategoryHasUnreadMessages(statusChatListCategoryItemLoader.section) - Connections { - target: root.model.sourceModel - function onCategoryHasUnreadMessagesChanged(categoryId: string, hasUnread: bool) { - if (categoryId === statusChatListCategoryItemLoader.section) { - statusChatListCategoryItem.hasUnreadMessages = hasUnread + Connections { + enabled: categoryPopupMenuSlot.active && statusChatListCategoryItem.highlighted + target: categoryPopupMenuSlot.item + function onClosed() { + statusChatListCategoryItem.highlighted = false + statusChatListCategoryItem.menuButton.highlighted = false } } - } - onClicked: { - if (sensor.enabled) { + title: model.name + + opened: model.categoryOpened + + sensor.pressAndHoldInterval: 150 + propagateTitleClicks: true // title click is handled as a normal click (fallthru) + showAddButton: showCategoryActionButtons + showMenuButton: !!root.onPopupMenuChanged + highlighted: false//statusChatListCategory.dragged // FIXME DND + + hasUnreadMessages: model.hasUnreadMessages + + onClicked: { + if (!sensor.enabled) { + return + } if (mouse.button === Qt.RightButton && showCategoryActionButtons && !!root.categoryPopupMenu) { statusChatListCategoryItem.setupPopup() highlighted = true; categoryPopupMenuSlot.item.popup() } else if (mouse.button === Qt.LeftButton) { - toggleCategory() + root.model.sourceModel.changeCategoryOpened(model.categoryId, !statusChatListCategoryItem.opened) } } + onToggleButtonClicked: root.model.sourceModel.changeCategoryOpened(model.categoryId, !statusChatListCategoryItem.opened) + onMenuButtonClicked: { + statusChatListCategoryItem.setupPopup() + highlighted = true + menuButton.highlighted = true + let p = menuButton.mapToItem(statusChatListCategoryItem, menuButton.x, menuButton.y) + let menuWidth = categoryPopupMenuSlot.item.width + categoryPopupMenuSlot.item.popup() + } + onAddButtonClicked: { + root.categoryAddButtonClicked(categoryId) + } } - onToggleButtonClicked: toggleCategory() - onMenuButtonClicked: { - statusChatListCategoryItem.setupPopup() - highlighted = true - menuButton.highlighted = true - let p = menuButton.mapToItem(statusChatListCategoryItem, menuButton.x, menuButton.y) - let menuWidth = categoryPopupMenuSlot.item.width - categoryPopupMenuSlot.item.popup() - } - onAddButtonClicked: { - root.categoryAddButtonClicked(categoryId) - } + } - } + + Component { + id: channelItemComponent + QC.Control { + id: draggable + objectName: model.name + width: root.width + height: model.categoryOpened ? statusChatListItem.height : 0 + visible: height + verticalPadding: 2 - delegate: Loader { - id: chatLoader - active: model.type !== d.chatTypeCategory - height: active && item ? item.height : 0 - visible: height + property alias chatListItem: statusChatListItem - sourceComponent: QC.Control { - id: draggable - objectName: model.name - width: root.width - height: model.categoryOpened ? statusChatListItem.height : 0 - verticalPadding: 2 + contentItem: MouseArea { + id: dragSensor - property alias chatListItem: statusChatListItem + anchors.fill: parent + cursorShape: active ? Qt.ClosedHandCursor : Qt.PointingHandCursor + hoverEnabled: true + enabled: root.draggableItems - contentItem: MouseArea { - id: dragSensor + property bool active: false + property real startY: 0 + property real startX: 0 - anchors.fill: parent - cursorShape: active ? Qt.ClosedHandCursor : Qt.PointingHandCursor - hoverEnabled: true - enabled: root.draggableItems + drag.target: draggedListItemLoader.item + drag.filterChildren: true - property bool active: false - property real startY: 0 - property real startX: 0 - - drag.target: draggedListItemLoader.item - drag.filterChildren: true - - onPressed: { - startY = mouseY - startX = mouseX - } - onPressAndHold: active = true - onReleased: { - if (active && d.destinationPosition !== -1 && statusChatListItem.originalOrder !== d.destinationPosition) { - root.chatItemReordered(statusChatListItem.chatId, statusChatListItem.originalOrder, d.destinationPosition) + onPressed: { + startY = mouseY + startX = mouseX } - active = false - } - onMouseYChanged: { - if ((Math.abs(startY - mouseY) > 1) && pressed) { - active = true + onPressAndHold: active = true + onReleased: { + if (active && d.destinationPosition !== -1 && statusChatListItem.originalOrder !== d.destinationPosition) { + root.chatItemReordered(statusChatListItem.chatId, statusChatListItem.originalOrder, d.destinationPosition) + } + active = false } - } - onMouseXChanged: { - if ((Math.abs(startX - mouseX) > 1) && pressed) { - active = true + onMouseYChanged: { + if ((Math.abs(startY - mouseY) > 1) && pressed) { + active = true + } } - } - onActiveChanged: d.destinationPosition = -1 + onMouseXChanged: { + if ((Math.abs(startX - mouseX) > 1) && pressed) { + active = true + } + } + onActiveChanged: d.destinationPosition = -1 - StatusChatListItem { - id: statusChatListItem + StatusChatListItem { + id: statusChatListItem - width: parent.width - opacity: dragSensor.active ? 0.0 : 1.0 - originalOrder: model.position - chatId: model.itemId - categoryId: model.categoryId - name: model.name - type: !!model.type ? model.type : StatusChatListItem.Type.CommunityChat - muted: model.muted - hasUnreadMessages: model.hasUnreadMessages - notificationsCount: model.notificationsCount - highlightWhenCreated: !!model.highlight - selected: (model.active && root.highlightItem) - asset.emoji: !!model.emoji ? model.emoji : "" - asset.color: !!model.color ? model.color : Theme.palette.userCustomizationColors[model.colorId] - asset.isImage: model.icon.includes("data") - asset.name: model.icon - ringSettings.ringSpecModel: type === StatusChatListItem.Type.OneToOneChat && root.isEnsVerified(chatId) ? undefined : model.colorHash - onlineStatus: !!model.onlineStatus ? model.onlineStatus : StatusChatListItem.OnlineStatus.Inactive + width: parent.width + opacity: dragSensor.active ? 0.0 : 1.0 + originalOrder: model.position + chatId: model.itemId + categoryId: model.categoryId + name: model.name + type: model.type ?? StatusChatListItem.Type.CommunityChat + muted: model.muted + hasUnreadMessages: model.hasUnreadMessages + notificationsCount: model.notificationsCount + highlightWhenCreated: !!model.highlight + selected: (model.active && root.highlightItem) + asset.emoji: !!model.emoji ? model.emoji : "" + asset.color: !!model.color ? model.color : Theme.palette.userCustomizationColors[model.colorId] + asset.isImage: model.icon.includes("data") + asset.name: model.icon + ringSettings.ringSpecModel: type === StatusChatListItem.Type.OneToOneChat && root.isEnsVerified(chatId) ? undefined : model.colorHash + onlineStatus: !!model.onlineStatus ? model.onlineStatus : StatusChatListItem.OnlineStatus.Inactive - sensor.cursorShape: dragSensor.cursorShape + sensor.cursorShape: dragSensor.cursorShape - onClicked: { - highlightWhenCreated = false + onClicked: { + highlightWhenCreated = false - if (mouse.button === Qt.RightButton && !!root.popupMenu) { - statusChatListItem.highlighted = true + if (mouse.button === Qt.RightButton && !!root.popupMenu) { + statusChatListItem.highlighted = true - let originalOpenHandler = popupMenuSlot.item.openHandler - let originalCloseHandler = popupMenuSlot.item.closeHandler + const originalOpenHandler = popupMenuSlot.item.openHandler + const originalCloseHandler = popupMenuSlot.item.closeHandler - popupMenuSlot.item.openHandler = function () { - if (!!originalOpenHandler) { - originalOpenHandler(statusChatListItem.chatId) + popupMenuSlot.item.openHandler = function () { + if (!!originalOpenHandler) { + originalOpenHandler(statusChatListItem.chatId) + } } + + popupMenuSlot.item.closeHandler = function () { + if (statusChatListItem) { + statusChatListItem.highlighted = false + } + if (!!originalCloseHandler) { + originalCloseHandler() + } + } + + const p = statusChatListItem.mapToItem(root, mouse.x, mouse.y) + + popupMenuSlot.item.popup(p.x + 4, p.y + 6) + popupMenuSlot.item.openHandler = originalOpenHandler + return } - - popupMenuSlot.item.closeHandler = function () { - if (statusChatListItem) { - statusChatListItem.highlighted = false - } - if (!!originalCloseHandler) { - originalCloseHandler() - } + if (!statusChatListItem.selected) { + root.chatItemSelected(statusChatListItem.categoryId, statusChatListItem.chatId) } - - let p = statusChatListItem.mapToItem(root, mouse.x, mouse.y) - - popupMenuSlot.item.popup(p.x + 4, p.y + 6) - popupMenuSlot.item.openHandler = originalOpenHandler - return - } - if (!statusChatListItem.selected) { - root.chatItemSelected(statusChatListItem.categoryId, statusChatListItem.chatId) } + onUnmute: root.chatItemUnmuted(statusChatListItem.chatId) } - onUnmute: root.chatItemUnmuted(statusChatListItem.chatId) } - } - DropArea { - id: dropArea - width: dragSensor.active ? 0 : parent.width - height: dragSensor.active ? 0 : parent.height - keys: ["chat-item-category-" + statusChatListItem.categoryId] + DropArea { + id: dropArea + width: dragSensor.active ? 0 : parent.width + height: dragSensor.active ? 0 : parent.height + keys: ["chat-item-category-" + statusChatListItem.categoryId] - onEntered: reorderDelay.start() + onEntered: reorderDelay.start() - Timer { - id: reorderDelay - interval: 100 - repeat: false - onTriggered: { - if (dropArea.containsDrag) { - d.destinationPosition = root.model.get(draggable.DelegateModel.itemsIndex).position - statusChatListItems.items.move(dropArea.drag.source.DelegateModel.itemsIndex, draggable.DelegateModel.itemsIndex) + Timer { + id: reorderDelay + interval: 100 + repeat: false + onTriggered: { + if (dropArea.containsDrag) { + d.destinationPosition = root.model.get(draggable.DelegateModel.itemsIndex).position + statusChatListItems.items.move(dropArea.drag.source.DelegateModel.itemsIndex, draggable.DelegateModel.itemsIndex) + } } } } - } - Loader { - id: draggedListItemLoader - active: dragSensor.active - sourceComponent: StatusChatListItem { - property var globalPosition: Utils.getAbsolutePosition(draggable) - parent: QC.Overlay.overlay - sensor.cursorShape: dragSensor.cursorShape - Drag.active: dragSensor.active - Drag.hotSpot.x: width / 2 - Drag.hotSpot.y: height / 2 - Drag.keys: ["chat-item-category-" + categoryId] - Drag.source: draggable + Loader { + id: draggedListItemLoader + active: dragSensor.active + sourceComponent: StatusChatListItem { + property var globalPosition: Utils.getAbsolutePosition(draggable) + parent: QC.Overlay.overlay + sensor.cursorShape: dragSensor.cursorShape + Drag.active: dragSensor.active + Drag.hotSpot.x: width / 2 + Drag.hotSpot.y: height / 2 + Drag.keys: ["chat-item-category-" + categoryId] + Drag.source: draggable - Component.onCompleted: { - x = globalPosition.x - y = globalPosition.y + chatId: draggable.chatListItem.chatId + categoryId: draggable.chatListItem.categoryId + name: draggable.chatListItem.name + type: draggable.chatListItem.type + muted: draggable.chatListItem.muted + dragged: true + hasUnreadMessages: model.hasUnreadMessages + notificationsCount: model.notificationsCount + selected: draggable.chatListItem.selected + + asset.color: draggable.chatListItem.asset.color + asset.imgIsIdenticon: draggable.chatListItem.asset.imgIsIdenticon + asset.name: draggable.chatListItem.asset.name + + Component.onCompleted: { + x = globalPosition.x + y = globalPosition.y + } } - chatId: draggable.chatListItem.chatId - categoryId: draggable.chatListItem.categoryId - name: draggable.chatListItem.name - type: draggable.chatListItem.type - muted: draggable.chatListItem.muted - dragged: true - hasUnreadMessages: model.hasUnreadMessages - notificationsCount: model.notificationsCount - selected: draggable.chatListItem.selected - - asset.color: draggable.chatListItem.asset.color - asset.imgIsIdenticon: draggable.chatListItem.asset.imgIsIdenticon - asset.name: draggable.chatListItem.asset.name } } } @@ -303,7 +295,6 @@ Item { id: d property int destinationPosition: -1 - readonly property int chatTypeCategory: -1 } Loader { diff --git a/ui/app/AppLayouts/Chat/views/CommunityColumnView.qml b/ui/app/AppLayouts/Chat/views/CommunityColumnView.qml index 377e79bf9e..1bc113973f 100644 --- a/ui/app/AppLayouts/Chat/views/CommunityColumnView.qml +++ b/ui/app/AppLayouts/Chat/views/CommunityColumnView.qml @@ -254,35 +254,16 @@ Item { categoryPopupMenu: StatusMenu { - property var itemWithCategory - property string categoryId - - openHandler: function () { - let jsonObj = root.communitySectionModule.getItemPartOfCategoryAsJsonById(categoryId) - try { - let obj = JSON.parse(jsonObj) - if (obj.error) { - console.error("error parsing chat item json object, id: ", categoryId, " error: ", obj.error) - close() - return - } - itemWithCategory = obj - } catch (e) { - console.error("error parsing chat item json object, id: ", categoryId, " error: ", e) - close() - return - } - } + property var categoryItem StatusAction { - // TODO pass categoryMuted to Item - text: itemWithCategory.muted ? qsTr("Unmute category") : qsTr("Mute category") + text: categoryItem.muted ? qsTr("Unmute category") : qsTr("Mute category") icon.name: "notification" onTriggered: { - if (itemWithCategory.muted) { - root.communitySectionModule.unmuteCategory(itemWithCategory.categoryId) + if (categoryItem.muted) { + root.communitySectionModule.unmuteCategory(categoryItem.itemId) } else { - root.communitySectionModule.muteCategory(itemWithCategory.categoryId) + root.communitySectionModule.muteCategory(categoryItem.itemId) } } } @@ -296,8 +277,8 @@ Item { Global.openPopup(createCategoryPopup, { isEdit: true, channels: [], - categoryId: itemWithCategory.categoryId, - categoryName: itemWithCategory.categoryName + categoryId: categoryItem.itemId, + categoryName: categoryItem.name }) } } @@ -314,10 +295,10 @@ Item { type: StatusAction.Type.Danger onTriggered: { Global.openPopup(deleteCategoryConfirmationDialogComponent, { - title: qsTr("Delete %1 category").arg(itemWithCategory.categoryName), + title: qsTr("Delete %1 category").arg(categoryItem.name), confirmationText: qsTr("Are you sure you want to delete %1 category? Channels inside the category won't be deleted.") - .arg(itemWithCategory.categoryName), - categoryId: itemWithCategory.categoryId + .arg(categoryItem.name), + categoryId: categoryItem.itemId }) } } @@ -327,6 +308,7 @@ Item { id: chatContextMenuView emojiPopup: root.emojiPopup + // TODO pass the chatModel in its entirety instead of fetching the JSOn using just the id openHandler: function (id) { try { let jsonObj = root.communitySectionModule.getItemAsJson(id)