From eccc9b3bd763b630161f752525c0f7d2e0451577 Mon Sep 17 00:00:00 2001 From: Godfrain Jacques Date: Mon, 15 Apr 2024 11:34:44 -0700 Subject: [PATCH] fix(category) Implement collapsed categories (#14135) This PR implements the nim part for enabling having collapsed categories that persist into the database This fixes #13944 --- .../modules/main/chat_section/controller.nim | 9 ++++++ .../main/chat_section/io_interface.nim | 6 ++++ src/app/modules/main/chat_section/module.nim | 29 +++++++++++-------- src/app/modules/main/chat_section/view.nim | 3 ++ .../modules/main/communities/io_interface.nim | 3 ++ src/app_service/service/chat/dto/chat.nim | 2 ++ .../service/community/async_tasks.nim | 2 ++ .../service/community/dto/community.nim | 20 +++++++++++-- src/app_service/service/community/service.nim | 29 ++++++++++++++++++- src/backend/communities.nim | 11 +++++++ .../src/StatusQ/Components/StatusChatList.qml | 7 +++-- .../StatusChatListAndCategories.qml | 2 ++ ui/app/AppLayouts/Chat/stores/RootStore.qml | 4 +++ .../Communities/views/CommunityColumnView.qml | 2 ++ 14 files changed, 111 insertions(+), 18 deletions(-) diff --git a/src/app/modules/main/chat_section/controller.nim b/src/app/modules/main/chat_section/controller.nim index 74341ca731..827e9487c2 100644 --- a/src/app/modules/main/chat_section/controller.nim +++ b/src/app/modules/main/chat_section/controller.nim @@ -184,6 +184,12 @@ proc init*(self: Controller) = self.mailserversService, self.sharedUrlsService, setChatAsActive = true) if (self.isCommunitySection): + self.events.on(SIGNAL_COMMUNITY_CATEGORY_COLLAPSED_TOGGLED) do(e: Args): + let args = CommunityCategoryCollapsedArgs(e) + + if (args.communityId == self.sectionId): + self.delegate.onToggleCollapsedCommunityCategory(args.categoryId, args.collapsed) + self.events.on(SIGNAL_COMMUNITY_CHANNEL_CREATED) do(e:Args): let args = CommunityChatArgs(e) let belongsToCommunity = args.chat.communityId.len > 0 @@ -691,6 +697,9 @@ proc shareCommunityToUsers*(self: Controller, pubKeys: string, inviteMessage: st proc reorderCommunityCategories*(self: Controller, categoryId: string, position: int) = self.communityService.reorderCommunityCategories(self.sectionId, categoryId, position) +proc toggleCollapsedCommunityCategory*(self: Controller, categoryId: string, collapsed: bool) = + self.communityService.toggleCollapsedCommunityCategory(self.sectionId, categoryId, collapsed) + proc reorderCommunityChat*(self: Controller, categoryId: string, chatId: string, position: int) = self.communityService.reorderCommunityChat(self.sectionId, categoryId, chatId, position) diff --git a/src/app/modules/main/chat_section/io_interface.nim b/src/app/modules/main/chat_section/io_interface.nim index bb09facf59..9bd66681b7 100644 --- a/src/app/modules/main/chat_section/io_interface.nim +++ b/src/app/modules/main/chat_section/io_interface.nim @@ -312,6 +312,12 @@ method prepareEditCategoryModel*(self: AccessInterface, categoryId: string) {.ba method reorderCommunityCategories*(self: AccessInterface, categoryId: string, position: int) {.base.} = raise newException(ValueError, "No implementation available") +method toggleCollapsedCommunityCategory*(self: AccessInterface, categoryId: string, collapsed: bool) {.base.} = + raise newException(ValueError, "No implementation available") + +method onToggleCollapsedCommunityCategory*(self: AccessInterface, categoryId: string, collapsed: bool) {.base.} = + raise newException(ValueError, "No implementation available") + method reorderCommunityChat*(self: AccessInterface, categoryId: string, chatId: string, position: int) {.base.} = raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/main/chat_section/module.nim b/src/app/modules/main/chat_section/module.nim index e00caf11da..a712a82b24 100644 --- a/src/app/modules/main/chat_section/module.nim +++ b/src/app/modules/main/chat_section/module.nim @@ -277,6 +277,7 @@ proc addCategoryItem(self: Module, category: Category, memberRole: MemberRole, c viewOnlyPermissionsSatisfied = true, viewAndPostPermissionsSatisfied = true ) + if insertIntoModel: self.view.chatsModel().appendItem(result) @@ -679,20 +680,18 @@ proc addNewChat( memberRole = community.memberRole var categoryOpened = true + if chatDto.categoryId != "": - let categoryItem = self.view.chatsModel.getItemById(chatDto.categoryId) - categoryOpened = categoryItem.categoryOpened - if channelGroup.id != "": - for category in channelGroup.categories: - if category.id == chatDto.categoryId: - categoryPosition = category.position - break + let category = self.controller.getCommunityCategoryDetails(self.controller.getMySectionId(), chatDto.categoryId) + if category.id == "": + error "No category found for chat", chatName=chatDto.name, categoryId=chatDto.categoryId else: - let category = self.controller.getCommunityCategoryDetails(self.controller.getMySectionId(), chatDto.categoryId) - if category.id == "": - error "No category found for chat", chatName=chatDto.name, categoryId=chatDto.categoryId - else: - categoryPosition = category.position + categoryOpened = category.categoryOpened + categoryPosition = category.position + + # TODO: This call will update the model for each category in channels which is not + # preferable. Please fix-me in https://github.com/status-im/status-desktop/issues/14431 + self.view.chatsModel().changeCategoryOpened(category.id, category.categoryOpened) result = chat_item.initItem( chatDto.id, @@ -1367,6 +1366,12 @@ method reorderCommunityCategories*(self: Module, categoryId: string, categoryPos self.controller.reorderCommunityCategories(categoryId, finalPosition) +method toggleCollapsedCommunityCategory*(self: Module, categoryId: string, collapsed: bool) = + self.controller.toggleCollapsedCommunityCategory(categoryId, collapsed) + +method onToggleCollapsedCommunityCategory*(self: Module, categoryId: string, collapsed: bool) = + self.view.chatsModel().changeCategoryOpened(categoryId, collapsed) + method reorderCommunityChat*(self: Module, categoryId: string, chatId: string, toPosition: int) = self.controller.reorderCommunityChat(categoryId, chatId, toPosition + 1) diff --git a/src/app/modules/main/chat_section/view.nim b/src/app/modules/main/chat_section/view.nim index 985e7c0013..d7cbf0d5bf 100644 --- a/src/app/modules/main/chat_section/view.nim +++ b/src/app/modules/main/chat_section/view.nim @@ -354,6 +354,9 @@ QtObject: proc reorderCommunityCategories*(self: View, categoryId: string, categoryPosition: int) {.slot} = self.delegate.reorderCommunityCategories(categoryId, categoryPosition) + proc toggleCollapsedCommunityCategory*(self: View, categoryId: string, collapsed: bool) {.slot} = + self.delegate.toggleCollapsedCommunityCategory(categoryId, collapsed) + proc reorderCommunityChat*(self: View, categoryId: string, chatId: string, position: int) {.slot} = self.delegate.reorderCommunityChat(categoryId, chatId, position) diff --git a/src/app/modules/main/communities/io_interface.nim b/src/app/modules/main/communities/io_interface.nim index 312caf4b06..a3332ac402 100644 --- a/src/app/modules/main/communities/io_interface.nim +++ b/src/app/modules/main/communities/io_interface.nim @@ -122,6 +122,9 @@ method communityImported*(self: AccessInterface, community: CommunityDto) {.base method communityDataImported*(self: AccessInterface, community: CommunityDto) {.base.} = raise newException(ValueError, "No implementation available") +method toggleCollapsedCommunityCategory*(self: AccessInterface, communityId:string, categoryId: string, collapsed: bool) {.base.} = + raise newException(ValueError, "No implementation available") + method communityInfoRequestFailed*(self: AccessInterface, communityId: string, errorMsg: string) {.base.} = raise newException(ValueError, "No implementation available") diff --git a/src/app_service/service/chat/dto/chat.nim b/src/app_service/service/chat/dto/chat.nim index 74885807ab..98a4c047fa 100644 --- a/src/app_service/service/chat/dto/chat.nim +++ b/src/app_service/service/chat/dto/chat.nim @@ -25,6 +25,7 @@ type Category* = object id*: string name*: string position*: int + categoryOpened*: bool type Permission* = object @@ -217,6 +218,7 @@ proc toCategory*(jsonObj: JsonNode): Category = discard jsonObj.getProp("id", result.id) discard jsonObj.getProp("name", result.name) discard jsonObj.getProp("position", result.position) + discard jsonObj.getProp("categoryOpened", result.categoryOpened) proc toChatMember*(jsonObj: JsonNode, memberId: string): ChatMember = # Parse status-go "Member" type diff --git a/src/app_service/service/community/async_tasks.nim b/src/app_service/service/community/async_tasks.nim index a03f626ba5..b92373d262 100644 --- a/src/app_service/service/community/async_tasks.nim +++ b/src/app_service/service/community/async_tasks.nim @@ -11,12 +11,14 @@ const asyncLoadCommunitiesDataTask: Task = proc(argEncoded: string) {.gcsafe, ni let responseCommunities = status_go.getAllCommunities() let responseSettings = status_go.getCommunitiesSettings() let responseNonApprovedRequestsToJoin = status_go.allNonApprovedCommunitiesRequestsToJoin() + let responseCollapsedCommunityCategories = status_go.collapsedCommunityCategories() arg.finish(%* { "tags": responseTags, "communities": responseCommunities, "settings": responseSettings, "nonAprrovedRequestsToJoin": responseNonApprovedRequestsToJoin, + "collapsedCommunityCategories": responseCollapsedCommunityCategories, "error": "", }) except Exception as e: diff --git a/src/app_service/service/community/dto/community.nim b/src/app_service/service/community/dto/community.nim index ce55299e13..52877ba0d7 100644 --- a/src/app_service/service/community/dto/community.nim +++ b/src/app_service/service/community/dto/community.nim @@ -391,6 +391,11 @@ proc toCommunityMembershipRequestDto*(jsonObj: JsonNode): CommunityMembershipReq discard jsonObj.getProp("communityId", result.communityId) discard jsonObj.getProp("our", result.our) +proc toCategoryDto*(jsonObj: JsonNode): Category = + result = Category() + discard jsonObj.getProp("categoryId", result.id) + discard jsonObj.getProp("collapsed", result.categoryOpened) + proc toCommunityDto*(jsonObj: JsonNode): CommunityDto = result = CommunityDto() discard jsonObj.getProp("id", result.id) @@ -493,9 +498,18 @@ proc toCommunitySettingsDto*(jsonObj: JsonNode): CommunitySettingsDto = discard jsonObj.getProp("communityId", result.id) discard jsonObj.getProp("historyArchiveSupportEnabled", result.historyArchiveSupportEnabled) -proc parseCommunities*(response: JsonNode): seq[CommunityDto] = - result = map(response["result"].getElems(), - proc(x: JsonNode): CommunityDto = x.toCommunityDto()) +proc parseCommunities*(response: JsonNode, categories: seq[Category]): seq[CommunityDto] = + var categoryMap = initTable[string, bool]() + for category in categories: + categoryMap[category.id] = true + + result = newSeq[CommunityDto]() + for communityNode in response["result"].getElems(): + var community = communityNode.toCommunityDto() + for category in community.categories.mitems: + if categoryMap.hasKey(category.id): + category.categoryOpened = true + result.add(community) proc parseKnownCuratedCommunities(jsonCommunities: JsonNode): seq[CommunityDto] = for _, communityJson in jsonCommunities.pairs(): diff --git a/src/app_service/service/community/service.nim b/src/app_service/service/community/service.nim index db7360dcb8..2884a68f10 100644 --- a/src/app_service/service/community/service.nim +++ b/src/app_service/service/community/service.nim @@ -72,6 +72,11 @@ type categoryId*: string position*: int + CommunityCategoryCollapsedArgs* = ref object of Args + communityId*: string + categoryId*: string + collapsed*: bool + CommunityCategoryArgs* = ref object of Args communityId*: string category*: Category @@ -190,6 +195,7 @@ const SIGNAL_COMMUNITY_CATEGORY_EDITED* = "communityCategoryEdited" const SIGNAL_COMMUNITY_CATEGORY_NAME_EDITED* = "communityCategoryNameEdited" const SIGNAL_COMMUNITY_CATEGORY_DELETED* = "communityCategoryDeleted" const SIGNAL_COMMUNITY_CATEGORY_REORDERED* = "communityCategoryReordered" +const SIGNAL_COMMUNITY_CATEGORY_COLLAPSED_TOGGLED* = "communityCategoryCollapsedToggled" const SIGNAL_COMMUNITY_CHANNEL_CATEGORY_CHANGED* = "communityChannelCategoryChanged" const SIGNAL_COMMUNITY_MEMBER_APPROVED* = "communityMemberApproved" const SIGNAL_COMMUNITY_MEMBER_STATUS_CHANGED* = "communityMemberStatusChanged" @@ -813,8 +819,15 @@ QtObject: toUgly(resultTags, responseObj["tags"]["result"]) self.communityTags = resultTags + # Categories + var categories: seq[Category] + let collapsedCommunityCategories = responseObj["collapsedCommunityCategories"] + if collapsedCommunityCategories{"result"}.kind != JNull: + for jsonCategory in collapsedCommunityCategories["result"]: + categories.add(jsonCategory.toCategoryDto()) + # All communities - let communities = parseCommunities(responseObj["communities"]) + let communities = parseCommunities(responseObj["communities"], categories) for community in communities: self.communities[community.id] = community @@ -1464,6 +1477,20 @@ QtObject: except Exception as e: error "Error creating community category", msg = e.msg, communityId, name + proc toggleCollapsedCommunityCategory*(self: Service, communityId: string, categoryId: string, collapsed: bool) = + try: + let response = status_go.toggleCollapsedCommunityCategory(communityId, categoryId, collapsed) + + if response.error != nil: + let error = Json.decode($response.error, RpcError) + raise newException(RpcException, "Error toggling collapsed community category: " & error.message) + + self.events.emit(SIGNAL_COMMUNITY_CATEGORY_COLLAPSED_TOGGLED, CommunityCategoryCollapsedArgs( + communityId: communityId, categoryId: categoryId, collapsed: collapsed)) + + except Exception as e: + error "Error toggling collapsed community category ", msg = e.msg, communityId, collapsed + proc editCommunityCategory*( self: Service, communityId: string, diff --git a/src/backend/communities.nim b/src/backend/communities.nim index a0d1461ba9..fc46869308 100644 --- a/src/backend/communities.nim +++ b/src/backend/communities.nim @@ -408,6 +408,17 @@ proc editCommunityCategory*( "chatIds": channels }]) +proc toggleCollapsedCommunityCategory*(communityId: string, categoryId: string, collapsed: bool): RpcResponse[JsonNode] = + result = callPrivateRPC("toggleCollapsedCommunityCategory".prefix, %*[ + { + "communityId": communityId, + "categoryId": categoryId, + "collapsed": collapsed + }]) + +proc collapsedCommunityCategories*(): RpcResponse[JsonNode] = + result = callPrivateRPC("collapsedCommunityCategories".prefix, %*[]) + proc deleteCommunityCategory*( communityId: string, categoryId: string diff --git a/ui/StatusQ/src/StatusQ/Components/StatusChatList.qml b/ui/StatusQ/src/StatusQ/Components/StatusChatList.qml index b069aa11fe..bd6633d575 100644 --- a/ui/StatusQ/src/StatusQ/Components/StatusChatList.qml +++ b/ui/StatusQ/src/StatusQ/Components/StatusChatList.qml @@ -32,6 +32,7 @@ Item { signal categoryReordered(string categoryId, int to) signal chatItemReordered(string categoryId, string chatId, int to) signal categoryAddButtonClicked(string id) + signal toggleCollapsedCommunityCategory(string categoryId, bool collapsed) StatusListView { id: statusChatListItems @@ -146,10 +147,12 @@ Item { highlighted = true; categoryPopupMenuSlot.item.popup() } else if (mouse.button === Qt.LeftButton) { - root.model.sourceModel.changeCategoryOpened(model.categoryId, !statusChatListCategoryItem.opened) + root.toggleCollapsedCommunityCategory(model.categoryId, !statusChatListCategoryItem.opened) } } - onToggleButtonClicked: root.model.sourceModel.changeCategoryOpened(model.categoryId, !statusChatListCategoryItem.opened) + onToggleButtonClicked: { + root.toggleCollapsedCommunityCategory(model.categoryId, !statusChatListCategoryItem.opened) + } onMenuButtonClicked: { statusChatListCategoryItem.setupPopup() highlighted = true diff --git a/ui/StatusQ/src/StatusQ/Components/StatusChatListAndCategories.qml b/ui/StatusQ/src/StatusQ/Components/StatusChatListAndCategories.qml index 5167739d06..0a6695a362 100644 --- a/ui/StatusQ/src/StatusQ/Components/StatusChatListAndCategories.qml +++ b/ui/StatusQ/src/StatusQ/Components/StatusChatListAndCategories.qml @@ -29,6 +29,7 @@ Item { signal chatItemReordered(string categoryId, string chatId, int to) signal chatListCategoryReordered(string categoryId, int to) signal categoryAddButtonClicked(string id) + signal toggleCollapsedCommunityCategory(string categoryId, bool collapsed) MouseArea { id: sensor @@ -53,6 +54,7 @@ Item { draggableItems: root.draggableItems showCategoryActionButtons: root.showCategoryActionButtons onCategoryAddButtonClicked: root.categoryAddButtonClicked(id) + onToggleCollapsedCommunityCategory: root.toggleCollapsedCommunityCategory(categoryId, collapsed) model: SortFilterProxyModel { sourceModel: root.model diff --git a/ui/app/AppLayouts/Chat/stores/RootStore.qml b/ui/app/AppLayouts/Chat/stores/RootStore.qml index 7d83442cdc..215650bfb6 100644 --- a/ui/app/AppLayouts/Chat/stores/RootStore.qml +++ b/ui/app/AppLayouts/Chat/stores/RootStore.qml @@ -392,6 +392,10 @@ QtObject { chatCommunitySectionModule.reorderCommunityCategories(categoryId, to) } + function toggleCollapsedCommunityCategory(categoryId, collapsed) { + chatCommunitySectionModule.toggleCollapsedCommunityCategory(categoryId, collapsed) + } + function reorderCommunityChat(categoryId, chatId, to) { chatCommunitySectionModule.reorderCommunityChat(categoryId, chatId, to) } diff --git a/ui/app/AppLayouts/Communities/views/CommunityColumnView.qml b/ui/app/AppLayouts/Communities/views/CommunityColumnView.qml index 0ef66e7c03..443a37ebd4 100644 --- a/ui/app/AppLayouts/Communities/views/CommunityColumnView.qml +++ b/ui/app/AppLayouts/Communities/views/CommunityColumnView.qml @@ -200,6 +200,8 @@ Item { categoryId: id }) + onToggleCollapsedCommunityCategory: root.store.toggleCollapsedCommunityCategory(categoryId, collapsed) + popupMenu: StatusMenu { hideDisabledItems: false StatusAction {