From 9ed38bddb74a90b6db16995cfcf8b8715c6ca52d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Iskierko?= <61889657+endulab@users.noreply.github.com> Date: Mon, 18 Mar 2024 18:33:07 +0100 Subject: [PATCH] 13291 hide channels (#13876) * feat(@desktop/communities): Hide channels if the user is not permitted to view and hideIfPermissionsNotMet is set Extend chats model with channel permissions info and hideIfPermissionsNotMet. Visibility of chat item is based on: member roles, channel permissions, hideIfPermissionsNotMet. If all channels from category are hidden, category item is also hidden. If all chats in community are hidden, infomration label is displayed. Issue #13291 * chore(@desktop): Upgrade status-go Issue #13291 --- .../chat_content/chat_details.nim | 14 +++ .../main/chat_section/chat_content/module.nim | 2 +- .../main/chat_section/chat_content/view.nim | 4 +- .../modules/main/chat_section/controller.nim | 8 +- .../main/chat_section/io_interface.nim | 4 +- src/app/modules/main/chat_section/item.nim | 36 +++++++ src/app/modules/main/chat_section/model.nim | 93 ++++++++++++++++++- src/app/modules/main/chat_section/module.nim | 58 ++++++++++-- src/app/modules/main/chat_section/view.nim | 23 ++++- src/app_service/service/chat/dto/chat.nim | 5 +- src/app_service/service/community/service.nim | 24 +++-- src/backend/communities.nim | 4 + .../StatusChatListAndCategories.qml | 6 ++ ui/app/AppLayouts/Chat/stores/RootStore.qml | 11 ++- .../Chat/views/ChatHeaderContentView.qml | 7 +- ui/app/AppLayouts/Chat/views/ChatView.qml | 22 ++++- .../Communities/popups/CreateChannelPopup.qml | 13 +-- .../Communities/views/CommunityColumnView.qml | 11 ++- .../shared/views/chat/ChatContextMenuView.qml | 1 + vendor/status-go | 2 +- 20 files changed, 299 insertions(+), 49 deletions(-) diff --git a/src/app/modules/main/chat_section/chat_content/chat_details.nim b/src/app/modules/main/chat_section/chat_content/chat_details.nim index 75e21fa912..c4a695e1b2 100644 --- a/src/app/modules/main/chat_section/chat_content/chat_details.nim +++ b/src/app/modules/main/chat_section/chat_content/chat_details.nim @@ -24,6 +24,7 @@ QtObject: active: bool blocked: bool canPostReactions: bool + hideIfPermissionsNotMet: bool proc delete*(self: ChatDetails) = self.QObject.delete @@ -50,6 +51,7 @@ QtObject: isContact: bool = false, blocked: bool = false, canPostReactions: bool = true, + hideIfPermissionsNotMet: bool ) = self.id = id self.`type` = `type` @@ -70,6 +72,7 @@ QtObject: self.active = false self.blocked = blocked self.canPostReactions = canPostReactions + self.hideIfPermissionsNotMet = hideIfPermissionsNotMet proc getId(self: ChatDetails): string {.slot.} = return self.id @@ -255,3 +258,14 @@ QtObject: proc setCanPostReactions*(self: ChatDetails, value: bool) = self.canPostReactions = value self.canPostReactionsChanged() + + proc hideIfPermissionsNotMetChanged(self: ChatDetails) {.signal.} + proc getHideIfPermissionsNotMet(self: ChatDetails): bool {.slot.} = + return self.hideIfPermissionsNotMet + QtProperty[bool] hideIfPermissionsNotMet: + read = getHideIfPermissionsNotMet + notify = hideIfPermissionsNotMetChanged + + proc setHideIfPermissionsNotMet*(self: ChatDetails, value: bool) = + self.hideIfPermissionsNotMet = value + self.hideIfPermissionsNotMetChanged() diff --git a/src/app/modules/main/chat_section/chat_content/module.nim b/src/app/modules/main/chat_section/chat_content/module.nim index e45917712c..3363485a6f 100644 --- a/src/app/modules/main/chat_section/chat_content/module.nim +++ b/src/app/modules/main/chat_section/chat_content/module.nim @@ -95,7 +95,7 @@ method load*(self: Module, chatItem: chat_item.Item) = self.controller.isUsersListAvailable(), chatName, chatImage, chatItem.color, chatItem.description, chatItem.emoji, chatItem.hasUnreadMessages, chatItem.notificationsCount, chatItem.highlight, chatItem.muted, chatItem.position, isUntrustworthy = trustStatus == TrustStatus.Untrustworthy, - isContact, chatItem.blocked, chatItem.canPostReactions) + isContact, chatItem.blocked, chatItem.canPostReactions, chatItem.hideIfPermissionsNotMet) self.inputAreaModule.load() self.messagesModule.load() diff --git a/src/app/modules/main/chat_section/chat_content/view.nim b/src/app/modules/main/chat_section/chat_content/view.nim index 4238e6a4e8..6d61e94bd2 100644 --- a/src/app/modules/main/chat_section/chat_content/view.nim +++ b/src/app/modules/main/chat_section/chat_content/view.nim @@ -40,10 +40,10 @@ QtObject: proc load*(self: View, id: string, `type`: int, belongsToCommunity, isUsersListAvailable: bool, name, icon: string, color, description, emoji: string, hasUnreadMessages: bool, notificationsCount: int, highlight, muted: bool, position: int, isUntrustworthy: bool, - isContact: bool, blocked: bool, canPostReactions: bool) = + isContact: bool, blocked: bool, canPostReactions: bool, hideIfPermissionsNotMet: bool) = self.chatDetails.setChatDetails(id, `type`, belongsToCommunity, isUsersListAvailable, name, icon, color, description, emoji, hasUnreadMessages, notificationsCount, highlight, muted, position, - isUntrustworthy, isContact, blocked, canPostReactions) + isUntrustworthy, isContact, blocked, canPostReactions, hideIfPermissionsNotMet) self.delegate.viewDidLoad() self.chatDetailsChanged() diff --git a/src/app/modules/main/chat_section/controller.nim b/src/app/modules/main/chat_section/controller.nim index ed52f86a49..63ec733200 100644 --- a/src/app/modules/main/chat_section/controller.nim +++ b/src/app/modules/main/chat_section/controller.nim @@ -566,9 +566,10 @@ proc createCommunityChannel*( color: string, categoryId: string, viewersCanPostReactions: bool, + hideIfPermissionsNotMet: bool, ) = self.communityService.createCommunityChannel(self.sectionId, name, description, emoji, color, - categoryId, viewersCanPostReactions) + categoryId, viewersCanPostReactions, hideIfPermissionsNotMet) proc editCommunityChannel*( self: Controller, @@ -580,6 +581,7 @@ proc editCommunityChannel*( categoryId: string, position: int, viewersCanPostReactions: bool, + hideIfPermissionsNotMet: bool, ) = self.communityService.editCommunityChannel( self.sectionId, @@ -590,8 +592,8 @@ proc editCommunityChannel*( color, categoryId, position, - viewersCanPostReactions - ) + viewersCanPostReactions, + hideIfPermissionsNotMet) proc createCommunityCategory*(self: Controller, name: string, channels: seq[string]) = self.communityService.createCommunityCategory(self.sectionId, name, channels) diff --git a/src/app/modules/main/chat_section/io_interface.nim b/src/app/modules/main/chat_section/io_interface.nim index 61437f5080..18dd7f45d7 100644 --- a/src/app/modules/main/chat_section/io_interface.nim +++ b/src/app/modules/main/chat_section/io_interface.nim @@ -266,11 +266,11 @@ method declineRequestToJoinCommunity*(self: AccessInterface, requestId: string, raise newException(ValueError, "No implementation available") method createCommunityChannel*(self: AccessInterface, name: string, description: string, - emoji: string, color: string, categoryId: string, viewersCanPostReactions: bool) {.base.} = + emoji: string, color: string, categoryId: string, viewersCanPostReactions: bool, hideIfPermissionsNotMet: bool) {.base.} = raise newException(ValueError, "No implementation available") method editCommunityChannel*(self: AccessInterface, channelId, name, description, emoji, color, - categoryId: string, position: int, viewersCanPostReactions: bool) {.base.} = + categoryId: string, position: int, viewersCanPostReactions: bool, hideIfPermissionsNotMet: bool) {.base.} = raise newException(ValueError, "No implementation available") method leaveCommunity*(self: AccessInterface) {.base.} = diff --git a/src/app/modules/main/chat_section/item.nim b/src/app/modules/main/chat_section/item.nim index 7e1b50f814..f8affeb7a0 100644 --- a/src/app/modules/main/chat_section/item.nim +++ b/src/app/modules/main/chat_section/item.nim @@ -37,6 +37,9 @@ type requiresPermissions: bool canPostReactions: bool viewersCanPostReactions: bool + hideIfPermissionsNotMet: bool + viewOnlyPermissionsSatisfied: bool + viewAndPostPermissionsSatisfied: bool proc initItem*( id, @@ -67,6 +70,9 @@ proc initItem*( requiresPermissions = false, canPostReactions = true, viewersCanPostReactions = true, + hideIfPermissionsNotMet: bool, + viewOnlyPermissionsSatisfied: bool, + viewAndPostPermissionsSatisfied: bool ): Item = result = Item() result.id = id @@ -98,6 +104,9 @@ proc initItem*( result.requiresPermissions = requiresPermissions result.canPostReactions = canPostReactions result.viewersCanPostReactions = viewersCanPostReactions + result.hideIfPermissionsNotMet = hideIfPermissionsNotMet + result.viewOnlyPermissionsSatisfied = viewOnlyPermissionsSatisfied + result.viewAndPostPermissionsSatisfied = viewAndPostPermissionsSatisfied proc `$`*(self: Item): string = result = fmt"""chat_section/Item( @@ -128,6 +137,9 @@ proc `$`*(self: Item): string = requiresPermissions: {$self.requiresPermissions}, canPostReactions: {$self.canPostReactions}, viewersCanPostReactions: {$self.viewersCanPostReactions}, + hideIfPermissionsNotMet: {$self.hideIfPermissionsNotMet}, + viewOnlyPermissionsSatisfied: {$self.viewOnlyPermissionsSatisfied}, + viewAndPostPermissionsSatisfied: {$self.viewAndPostPermissionsSatisfied} ]""" proc toJsonNode*(self: Item): JsonNode = @@ -159,6 +171,9 @@ proc toJsonNode*(self: Item): JsonNode = "requiresPermissions": self.requiresPermissions, "canPostReactions": self.canPostReactions, "viewersCanPostReactions": self.viewersCanPostReactions, + "hideIfPermissionsNotMet": self.hideIfPermissionsNotMet, + "viewOnlyPermissionsSatisfied": self.viewOnlyPermissionsSatisfied, + "viewAndPostPermissionsSatisfied": self.viewAndPostPermissionsSatisfied } proc delete*(self: Item) = @@ -257,6 +272,24 @@ proc categoryId*(self: Item): string = proc `categoryId=`*(self: var Item, value: string) = self.categoryId = value +proc hideIfPermissionsNotMet*(self: Item): bool = + self.hideIfPermissionsNotMet + +proc `hideIfPermissionsNotMet=`*(self: var Item, value: bool) = + self.hideIfPermissionsNotMet = value + +proc viewAndPostPermissionsSatisfied*(self: Item): bool = + self.viewAndPostPermissionsSatisfied + +proc `viewAndPostPermissionsSatisfied=`*(self: var Item, value: bool) = + self.viewAndPostPermissionsSatisfied = value + +proc viewOnlyPermissionsSatisfied*(self: Item): bool = + self.viewOnlyPermissionsSatisfied + +proc `viewOnlyPermissionsSatisfied=`*(self: var Item, value: bool) = + self.viewOnlyPermissionsSatisfied = value + proc categoryPosition*(self: Item): int = self.categoryPosition @@ -322,3 +355,6 @@ proc viewersCanPostReactions*(self: Item): bool = proc `viewersCanPostReactions=`*(self: Item, value: bool) = self.viewersCanPostReactions = value + +proc hideBecausePermissionsAreNotMet*(self: Item): bool = + self.hideIfPermissionsNotMet and not self.viewOnlyPermissionsSatisfied and not self.viewAndPostPermissionsSatisfied diff --git a/src/app/modules/main/chat_section/model.nim b/src/app/modules/main/chat_section/model.nim index 31dfbb8837..42445a136f 100644 --- a/src/app/modules/main/chat_section/model.nim +++ b/src/app/modules/main/chat_section/model.nim @@ -37,6 +37,11 @@ type RequiresPermissions CanPostReactions ViewersCanPostReactions + HideIfPermissionsNotMet + ViewOnlyPermissionsSatisfied + ViewAndPostPermissionsSatisfied + ShouldBeHiddenBecausePermissionsAreNotMet #this is a complex role which depends on other roles + #(MemberRole , HideIfPermissionsNotMet, ViewOnlyPermissionsSatisfied, ViewAndPostPermissionsSatisfied) QtObject: type @@ -78,6 +83,28 @@ QtObject: proc items*(self: Model): seq[Item] = return self.items + proc categoryShouldBeHiddenBecauseNotPermitted(self: Model, categoryId: string): bool = + for i in 0 ..< self.items.len: + if not self.items[i].isCategory and self.items[i].categoryId == categoryId: + if not self.items[i].hideBecausePermissionsAreNotMet(): + return false + return true + + proc itemShouldBeHiddenBecauseNotPermitted*(self: Model, item: Item): bool = + let isRegularUser = item.memberRole != MemberRole.Owner and item.memberRole != MemberRole.Admin and item.memberRole != MemberRole.TokenMaster + if not isRegularUser: + return false + if item.isCategory: + return self.categoryShouldBeHiddenBecauseNotPermitted(item.id) + else: + return item.hideBecausePermissionsAreNotMet() + + proc firstNotHiddenItemId*(self: Model): string = + for i in 0 ..< self.items.len: + if not self.items[i].isCategory and not self.itemShouldBeHiddenBecauseNotPermitted(self.items[i]): + return self.items[i].id + return "" + method roleNames(self: Model): Table[int, string] = { ModelRole.Id.int:"itemId", @@ -98,6 +125,9 @@ QtObject: ModelRole.Active.int:"active", ModelRole.Position.int:"position", ModelRole.CategoryId.int:"categoryId", + ModelRole.HideIfPermissionsNotMet.int:"hideIfPermissionsNotMet", + ModelRole.ViewOnlyPermissionsSatisfied.int:"viewOnlyPermissionsSatisfied", + ModelRole.ViewAndPostPermissionsSatisfied.int:"viewAndPostPermissionsSatisfied", ModelRole.CategoryPosition.int:"categoryPosition", ModelRole.Highlight.int:"highlight", ModelRole.CategoryOpened.int:"categoryOpened", @@ -109,6 +139,7 @@ QtObject: ModelRole.RequiresPermissions.int:"requiresPermissions", ModelRole.CanPostReactions.int:"canPostReactions", ModelRole.ViewersCanPostReactions.int:"viewersCanPostReactions", + ModelRole.ShouldBeHiddenBecausePermissionsAreNotMet.int:"shouldBeHiddenBecausePermissionsAreNotMet" }.toTable method data(self: Model, index: QModelIndex, role: int): QVariant = @@ -180,6 +211,14 @@ QtObject: result = newQVariant(item.canPostReactions) of ModelRole.ViewersCanPostReactions: result = newQVariant(item.viewersCanPostReactions) + of ModelRole.HideIfPermissionsNotMet: + result = newQVariant(item.hideIfPermissionsNotMet) + of ModelRole.ViewAndPostPermissionsSatisfied: + result = newQVariant(item.viewAndPostPermissionsSatisfied) + of ModelRole.ViewOnlyPermissionsSatisfied: + result = newQVariant(item.viewOnlyPermissionsSatisfied) + of ModelRole.ShouldBeHiddenBecausePermissionsAreNotMet: + return newQVariant(self.itemShouldBeHiddenBecauseNotPermitted(item)) proc getItemIdxById(items: seq[Item], id: string): int = var idx = 0 @@ -235,6 +274,20 @@ QtObject: defer: index.delete self.dataChanged(index, index, @[ModelRole.CategoryOpened.int]) + # This function only refreshes ShouldBeHiddenBecausePermissionsAreNotMet. + # Then itemShouldBeHiddenBecauseNotPermitted() is used in data() to determined whether category is hidden or not. + proc updateHiddenFlagForCategory(self: Model, id: string) = + if id == "": + return + let index = self.getItemIdxById(id) + if index == -1: + return + if not self.items[index].isCategory: + return + let modelIndex = self.createIndex(index, 0, nil) + defer: modelIndex.delete + self.dataChanged(modelIndex, modelIndex, @[ModelRole.ShouldBeHiddenBecausePermissionsAreNotMet.int]) + proc removeItemByIndex(self: Model, idx: int) = if idx == -1: return @@ -295,6 +348,11 @@ QtObject: else: self.dataChanged(index, index, @[ModelRole.Active.int]) + proc activeItem*(self: Model): Item = + for i in 0 ..< self.items.len: + if self.items[i].active: + return self.items[i] + proc setItemLocked*(self: Model, id: string, locked: bool) = let index = self.getItemIdxById(id) if index == -1: @@ -304,6 +362,28 @@ QtObject: defer: modelIndex.delete self.dataChanged(modelIndex, modelIndex, @[ModelRole.Locked.int]) + proc setViewOnlyPermissionsSatisfied*(self: Model, id: string, satisfied: bool) = + let index = self.getItemIdxById(id) + if index == -1: + return + self.items[index].viewOnlyPermissionsSatisfied = satisfied + let modelIndex = self.createIndex(index, 0, nil) + defer: modelIndex.delete + # refresh also ShouldBeHiddenBecausePermissionsAreNotMet because it depends on ViewOnlyPermissionsSatisfied + self.dataChanged(modelIndex, modelIndex, @[ModelRole.ViewOnlyPermissionsSatisfied.int, ModelRole.ShouldBeHiddenBecausePermissionsAreNotMet.int]) + self.updateHiddenFlagForCategory(self.items[index].categoryId) + + proc setViewAndPostPermissionsSatisfied*(self: Model, id: string, satisfied: bool) = + let index = self.getItemIdxById(id) + if index == -1: + return + self.items[index].viewAndPostPermissionsSatisfied = satisfied + let modelIndex = self.createIndex(index, 0, nil) + defer: modelIndex.delete + # refresh also ShouldBeHiddenBecausePermissionsAreNotMet because it depends on ViewAndPostPermissionsSatisfied + self.dataChanged(modelIndex, modelIndex, @[ModelRole.ViewAndPostPermissionsSatisfied.int, ModelRole.ShouldBeHiddenBecausePermissionsAreNotMet.int]) + self.updateHiddenFlagForCategory(self.items[index].categoryId) + proc setItemPermissionsRequired*(self: Model, id: string, value: bool) = let index = self.getItemIdxById(id) if index == -1: @@ -356,6 +436,12 @@ QtObject: self.items[i].muted = muted self.dataChanged(index, index, @[ModelRole.Muted.int]) + proc allChannelsAreHiddenBecauseNotPermitted*(self: Model): bool = + for i in 0 ..< self.items.len: + if not self.items[i].isCategory and not self.itemShouldBeHiddenBecauseNotPermitted(self.items[i]): + return false + return true + proc changeBlockedOnItemById*(self: Model, id: string, blocked: bool) = let index = self.getItemIdxById(id) if index == -1: @@ -385,7 +471,7 @@ QtObject: ModelRole.TrustStatus.int, ]) - proc updateItemDetailsById*(self: Model, id, name, description, emoji, color: string) = + proc updateItemDetailsById*(self: Model, id, name, description, emoji, color: string, hideIfPermissionsNotMet: bool) = let index = self.getItemIdxById(id) if index == -1: return @@ -393,6 +479,7 @@ QtObject: self.items[index].description = description self.items[index].emoji = emoji self.items[index].color = color + self.items[index].hideIfPermissionsNotMet = hideIfPermissionsNotMet let modelIndex = self.createIndex(index, 0, nil) defer: modelIndex.delete self.dataChanged(modelIndex, modelIndex, @[ @@ -400,7 +487,11 @@ QtObject: ModelRole.Description.int, ModelRole.Emoji.int, ModelRole.Color.int, + ModelRole.HideIfPermissionsNotMet.int, + # refresh also ShouldBeHiddenBecausePermissionsAreNotMet because it depends on HideIfPermissionsNotMet + ModelRole.ShouldBeHiddenBecausePermissionsAreNotMet.int ]) + self.updateHiddenFlagForCategory(self.items[index].categoryId) proc updateNameColorIconOnItemById*(self: Model, id, name, color, icon: string) = let index = self.getItemIdxById(id) diff --git a/src/app/modules/main/chat_section/module.nim b/src/app/modules/main/chat_section/module.nim index 45bc245702..7b20acf53e 100644 --- a/src/app/modules/main/chat_section/module.nim +++ b/src/app/modules/main/chat_section/module.nim @@ -186,6 +186,9 @@ proc addCategoryItem(self: Module, category: Category, memberRole: MemberRole, c position = -1, # Set position as -1, so that the Category Item is on top of its Channels category.id, category.position, + hideIfPermissionsNotMet = false, + viewOnlyPermissionsSatisfied = true, + viewAndPostPermissionsSatisfied = true ) if insertIntoModel: self.view.chatsModel().appendItem(result) @@ -463,6 +466,16 @@ proc updateChatLocked(self: Module, chatId: string) = let locked = self.controller.checkChatIsLocked(communityId, chatId) self.view.chatsModel().setItemLocked(chatId, locked) +proc updateViewOnlyPermissionsSatisfied(self: Module, chatId: string, satisifed: bool) = + if not self.controller.isCommunity(): + return + self.view.chatsModel().setViewOnlyPermissionsSatisfied(chatId, satisifed) + +proc updateViewAndPostPermissionsSatisfied(self: Module, chatId: string, satisifed: bool) = + if not self.controller.isCommunity(): + return + self.view.chatsModel().setViewAndPostPermissionsSatisfied(chatId, satisifed) + proc updateChatRequiresPermissions(self: Module, chatId: string) = if not self.controller.isCommunity(): return @@ -514,7 +527,7 @@ method onActiveSectionChange*(self: Module, sectionId: string) = method chatsModel*(self: Module): chats_model.Model = return self.view.chatsModel() -proc addNewChat*( +proc addNewChat( self: Module, chatDto: ChatDto, channelGroup: ChannelGroupDto, @@ -560,11 +573,15 @@ proc addNewChat*( var memberRole = self.getUserMemberRole(chatDto.members) - if memberRole == MemberRole.None and len(chatDto.communityId) != 0: - memberRole = channelGroup.memberRole if chatDto.chatType != ChatType.PrivateGroupChat: memberRole = channelGroup.memberRole + if memberRole == MemberRole.None and len(chatDto.communityId) != 0: + memberRole = channelGroup.memberRole + if memberRole == MemberRole.None: + let community = communityService.getCommunityById(chatDto.communityId) + memberRole = community.memberRole + var categoryOpened = true if chatDto.categoryId != "": let categoryItem = self.view.chatsModel.getItemById(chatDto.categoryId) @@ -615,6 +632,9 @@ proc addNewChat*( false, canPostReactions = chatDto.canPostReactions, viewersCanPostReactions = chatDto.viewersCanPostReactions, + hideIfPermissionsNotMet = chatDto.hideIfPermissionsNotMet, + viewOnlyPermissionsSatisfied = true, # will be updated in async call + viewAndPostPermissionsSatisfied = true # will be updated in async call ) self.addSubmodule( @@ -720,11 +740,24 @@ method onCommunityChannelDeletedOrChatLeft*(self: Module, chatId: string) = self.setFirstChannelAsActive() +proc refreshHiddenBecauseNotPermittedState(self: Module) = + self.view.refreshAllChannelsAreHiddenBecauseNotPermittedChanged() + + let activeChatItem = self.view.chatsModel().activeItem() + if activeChatItem == nil: + return + + let activeItemShouldBeHidden = self.view.chatsModel().itemShouldBeHiddenBecauseNotPermitted(activeChatItem) + if activeItemShouldBeHidden: + let firstNotHiddenItemId = self.view.chatsModel().firstNotHiddenItemId() + self.setActiveItem(firstNotHiddenItemId) + method onCommunityChannelEdited*(self: Module, chat: ChatDto) = if(not self.chatContentModules.contains(chat.id)): return - self.view.chatsModel().updateItemDetailsById(chat.id, chat.name, chat.description, chat.emoji, chat.color) self.changeCanPostValues(chat.id, chat.canPostReactions, chat.viewersCanPostReactions) + self.view.chatsModel().updateItemDetailsById(chat.id, chat.name, chat.description, chat.emoji, chat.color, chat.hideIfPermissionsNotMet) + self.refreshHiddenBecauseNotPermittedState() method switchToOrCreateOneToOneChat*(self: Module, chatId: string) = # One To One chat is available only in the `Chat` section @@ -869,6 +902,9 @@ proc updateChannelPermissionViewData*(self: Module, chatId: string, viewOnlyPerm self.chatContentModules[chatId].onUpdateViewOnlyPermissionsSatisfied(viewOnlyPermissions.satisfied) self.chatContentModules[chatId].onUpdateViewAndPostPermissionsSatisfied(viewAndPostPermissions.satisfied) self.chatContentModules[chatId].setPermissionsCheckOngoing(false) + self.updateViewOnlyPermissionsSatisfied(chatId, viewOnlyPermissions.satisfied) + self.updateViewAndPostPermissionsSatisfied(chatId, viewAndPostPermissions.satisfied) + self.refreshHiddenBecauseNotPermittedState() method onCommunityCheckPermissionsToJoinResponse*(self: Module, checkPermissionsToJoinResponse: CheckPermissionsToJoinResponseDto) = let community = self.controller.getMyCommunity() @@ -1093,13 +1129,13 @@ method onAcceptRequestToJoinFailedNoPermission*(self: Module, communityId: strin let contact = self.controller.getContactById(memberKey) self.view.emitOpenNoPermissionsToJoinPopupSignal(community.name, contact.displayName, community.id, requestId) -method createCommunityChannel*(self: Module, name, description, emoji, color, categoryId: string, viewersCanPostReactions: bool) = - self.controller.createCommunityChannel(name, description, emoji, color, categoryId, viewersCanPostReactions) +method createCommunityChannel*(self: Module, name, description, emoji, color, categoryId: string, viewersCanPostReactions: bool, hideIfPermissionsNotMet: bool) = + self.controller.createCommunityChannel(name, description, emoji, color, categoryId, viewersCanPostReactions, hideIfPermissionsNotMet) method editCommunityChannel*(self: Module, channelId, name, description, emoji, color, - categoryId: string, position: int, viewersCanPostReactions: bool) = + categoryId: string, position: int, viewersCanPostReactions: bool, hideIfPermissionsNotMet: bool) = self.controller.editCommunityChannel(channelId, name, description, emoji, color, categoryId, - position, viewersCanPostReactions) + position, viewersCanPostReactions, hideIfPermissionsNotMet) method createCommunityCategory*(self: Module, name: string, channels: seq[string]) = self.controller.createCommunityCategory(name, channels) @@ -1164,6 +1200,9 @@ method prepareEditCategoryModel*(self: Module, categoryId: string) = active=false, c.position, categoryId="", + hideIfPermissionsNotMet=false, + viewOnlyPermissionsSatisfied = true, + viewAndPostPermissionsSatisfied = true ) self.view.editCategoryChannelsModel().appendItem(chatItem) let catChats = self.controller.getChats(communityId, categoryId) @@ -1186,6 +1225,9 @@ method prepareEditCategoryModel*(self: Module, categoryId: string) = active=false, c.position, categoryId, + hideIfPermissionsNotMet=false, + viewOnlyPermissionsSatisfied = true, + viewAndPostPermissionsSatisfied = true ) self.view.editCategoryChannelsModel().appendItem(chatItem, ignoreCategory = true) diff --git a/src/app/modules/main/chat_section/view.nim b/src/app/modules/main/chat_section/view.nim index 2ef8432855..b4762b571f 100644 --- a/src/app/modules/main/chat_section/view.nim +++ b/src/app/modules/main/chat_section/view.nim @@ -31,6 +31,7 @@ QtObject: permissionsCheckOngoing: bool isWaitingOnNewCommunityOwnerToConfirmRequestToRejoin: bool shardingInProgress: bool + allChannelsAreHiddenBecauseNotPermitted: bool proc delete*(self: View) = self.model.delete @@ -269,9 +270,10 @@ QtObject: emoji: string, color: string, categoryId: string, - viewersCanPostReactions: bool + viewersCanPostReactions: bool, + hideIfPermissionsNotMet: bool, ) {.slot.} = - self.delegate.createCommunityChannel(name, description, emoji, color, categoryId, viewersCanPostReactions) + self.delegate.createCommunityChannel(name, description, emoji, color, categoryId, viewersCanPostReactions, hideIfPermissionsNotMet) proc editCommunityChannel*( self: View, @@ -283,6 +285,7 @@ QtObject: categoryId: string, position: int, viewersCanPostReactions: bool, + hideIfPermissionsNotMet: bool ) {.slot.} = self.delegate.editCommunityChannel( channelId, @@ -293,6 +296,7 @@ QtObject: categoryId, position, viewersCanPostReactions, + hideIfPermissionsNotMet ) proc leaveCommunity*(self: View) {.slot.} = @@ -490,3 +494,18 @@ QtObject: self.setShardingInProgress(true) self.delegate.setCommunityShard(shardIndex) + proc allChannelsAreHiddenBecauseNotPermittedChanged*(self: View) {.signal.} + + proc getAllChannelsAreHiddenBecauseNotPermitted*(self: View): bool {.slot.} = + return self.allChannelsAreHiddenBecauseNotPermitted + + QtProperty[bool] allChannelsAreHiddenBecauseNotPermitted: + read = getAllChannelsAreHiddenBecauseNotPermitted + notify = allChannelsAreHiddenBecauseNotPermittedChanged + + proc refreshAllChannelsAreHiddenBecauseNotPermittedChanged*(self: View) = + let allAreHidden = self.model.allChannelsAreHiddenBecauseNotPermitted() + if (allAreHidden == self.allChannelsAreHiddenBecauseNotPermitted): + return + self.allChannelsAreHiddenBecauseNotPermitted = allAreHidden + self.allChannelsAreHiddenBecauseNotPermittedChanged() \ No newline at end of file diff --git a/src/app_service/service/chat/dto/chat.nim b/src/app_service/service/chat/dto/chat.nim index 95ca43c753..b7c87d57b6 100644 --- a/src/app_service/service/chat/dto/chat.nim +++ b/src/app_service/service/chat/dto/chat.nim @@ -89,6 +89,7 @@ type ChatDto* = object categoryId*: string highlight*: bool permissions*: Permission + hideIfPermissionsNotMet*: bool type ChannelGroupDto* = object id*: string @@ -152,7 +153,8 @@ proc `$`*(self: ChatDto): string = firstMessageTimestamp: {self.firstMessageTimestamp}, categoryId: {self.categoryId}, position: {self.position}, - highlight: {self.highlight} + highlight: {self.highlight}, + hideIfPermissionsNotMet: {self.hideIfPermissionsNotMet} )""" proc toCheckPermissionsResultDto*(jsonObj: JsonNode): CheckPermissionsResultDto = @@ -280,6 +282,7 @@ proc toChatDto*(jsonObj: JsonNode): ChatDto = # Communities have `categoryID` and chats have `categoryId` # This should be fixed in status-go, but would be a breaking change discard jsonObj.getProp("categoryID", result.categoryId) + discard jsonObj.getProp("hideIfPermissionsNotMet", result.hideIfPermissionsNotMet) discard jsonObj.getProp("position", result.position) discard jsonObj.getProp("communityId", result.communityId) discard jsonObj.getProp("profile", result.profile) diff --git a/src/app_service/service/community/service.nim b/src/app_service/service/community/service.nim index 9494f8749e..ef9c372592 100644 --- a/src/app_service/service/community/service.nim +++ b/src/app_service/service/community/service.nim @@ -637,7 +637,8 @@ QtObject: ) if chat.name != prevChat.name or chat.description != prevChat.description or chat.color != prevChat.color or - chat.emoji != prevChat.emoji or chat.viewersCanPostReactions != prevChat.viewersCanPostReactions: + chat.emoji != prevChat.emoji or chat.viewersCanPostReactions != prevChat.viewersCanPostReactions or + chat.hideIfPermissionsNotMet != prevChat.hideIfPermissionsNotMet: var updatedChat = chat # TODO improve this in https://github.com/status-im/status-desktop/issues/12595 @@ -1278,10 +1279,11 @@ QtObject: color: string, categoryId: string, viewersCanPostReactions: bool, + hideIfPermissionsNotMet: bool ) = try: let response = status_go.createCommunityChannel(communityId, name, description, emoji, color, categoryId, - viewersCanPostReactions) + viewersCanPostReactions, hideIfPermissionsNotMet) if not response.error.isNil: let error = Json.decode($response.error, RpcError) @@ -1318,6 +1320,7 @@ QtObject: categoryId: string, position: int, viewersCanPostReactions: bool, + hideIfPermissionsNotMet: bool ) = try: let response = status_go.editCommunityChannel( @@ -1329,7 +1332,8 @@ QtObject: color, categoryId, position, - viewersCanPostReactions + viewersCanPostReactions, + hideIfPermissionsNotMet ) if response.error != nil: @@ -2282,6 +2286,13 @@ QtObject: else: return community.declinedRequestsToJoin[indexDeclined].publicKey + proc checkChatIsLocked*(self: Service, communityId: string, chatId: string): bool = + if not self.communities.hasKey(communityId): + return false + + let community = self.getCommunityById(communityId) + return community.channelPermissions.channels.hasKey(chatId) and not community.channelPermissions.channels[chatId].viewAndPostPermissions.satisfied + proc checkChatHasPermissions*(self: Service, communityId: string, chatId: string): bool = let community = self.getCommunityById(communityId) for id, tokenPermission in community.tokenPermissions: @@ -2291,13 +2302,6 @@ QtObject: return true return false - proc checkChatIsLocked*(self: Service, communityId: string, chatId: string): bool = - if not self.communities.hasKey(communityId): - return false - - let community = self.getCommunityById(communityId) - return community.channelPermissions.channels.hasKey(chatId) and not community.channelPermissions.channels[chatId].viewAndPostPermissions.satisfied - proc shareCommunityUrlWithChatKey*(self: Service, communityId: string): string = try: let response = status_go.shareCommunityUrlWithChatKey(communityId) diff --git a/src/backend/communities.nim b/src/backend/communities.nim index 4306561128..a0d1461ba9 100644 --- a/src/backend/communities.nim +++ b/src/backend/communities.nim @@ -304,6 +304,7 @@ proc createCommunityChannel*( color: string, categoryId: string, viewersCanPostReactions: bool, + hideIfPermissionsNotMet: bool ): RpcResponse[JsonNode] = result = callPrivateRPC("createCommunityChat".prefix, %*[ communityId, @@ -319,6 +320,7 @@ proc createCommunityChannel*( }, "category_id": categoryId, "viewers_can_post_reactions": viewersCanPostReactions, + "hide_if_permissions_not_met": hideIfPermissionsNotMet }]) proc editCommunityChannel*( @@ -331,6 +333,7 @@ proc editCommunityChannel*( categoryId: string, position: int, viewersCanPostReactions: bool, + hideIfPermissionsNotMet: bool ): RpcResponse[JsonNode] = result = callPrivateRPC("editCommunityChat".prefix, %*[ communityId, @@ -348,6 +351,7 @@ proc editCommunityChannel*( "category_id": categoryId, "position": position, "viewers_can_post_reactions": viewersCanPostReactions, + "hide_if_permissions_not_met": hideIfPermissionsNotMet }]) proc reorderCommunityCategories*(communityId: string, categoryId: string, position: int): RpcResponse[JsonNode] = diff --git a/ui/StatusQ/src/StatusQ/Components/StatusChatListAndCategories.qml b/ui/StatusQ/src/StatusQ/Components/StatusChatListAndCategories.qml index 1a51b6cffd..5167739d06 100644 --- a/ui/StatusQ/src/StatusQ/Components/StatusChatListAndCategories.qml +++ b/ui/StatusQ/src/StatusQ/Components/StatusChatListAndCategories.qml @@ -56,6 +56,12 @@ Item { model: SortFilterProxyModel { sourceModel: root.model + filters: [ + ValueFilter { + roleName: "shouldBeHiddenBecausePermissionsAreNotMet" + value: false + } + ] sorters: [ RoleSorter { roleName: "categoryPosition" diff --git a/ui/app/AppLayouts/Chat/stores/RootStore.qml b/ui/app/AppLayouts/Chat/stores/RootStore.qml index 5edb045351..e50bb75001 100644 --- a/ui/app/AppLayouts/Chat/stores/RootStore.qml +++ b/ui/app/AppLayouts/Chat/stores/RootStore.qml @@ -61,6 +61,8 @@ QtObject { root.communitiesModuleInst.prepareTokenModelForCommunity(publicKey) } + readonly property bool allChannelsAreHiddenBecauseNotPermitted: root.chatCommunitySectionModule.allChannelsAreHiddenBecauseNotPermitted + readonly property bool requirementsCheckPending: root.communitiesModuleInst.requirementsCheckPending readonly property var permissionsModel: !!root.communitiesModuleInst.spectatedCommunityPermissionModel ? @@ -336,13 +338,13 @@ QtObject { } function createCommunityChannel(channelName, channelDescription, channelEmoji, channelColor, - categoryId, viewersCanPostReactions) { + categoryId, viewersCanPostReactions, hideIfPermissionsNotMet) { chatCommunitySectionModule.createCommunityChannel(channelName, channelDescription, - channelEmoji.trim(), channelColor, categoryId, viewersCanPostReactions); + channelEmoji.trim(), channelColor, categoryId, viewersCanPostReactions, hideIfPermissionsNotMet); } function editCommunityChannel(chatId, newName, newDescription, newEmoji, newColor, - newCategory, channelPosition, viewOnlyCanAddReaction) { + newCategory, channelPosition, viewOnlyCanAddReaction, hideIfPermissionsNotMet) { chatCommunitySectionModule.editCommunityChannel( chatId, newName, @@ -351,7 +353,8 @@ QtObject { newColor, newCategory, channelPosition, - viewOnlyCanAddReaction + viewOnlyCanAddReaction, + hideIfPermissionsNotMet ) } diff --git a/ui/app/AppLayouts/Chat/views/ChatHeaderContentView.qml b/ui/app/AppLayouts/Chat/views/ChatHeaderContentView.qml index 58c10a06f9..ef5bdc62e3 100644 --- a/ui/app/AppLayouts/Chat/views/ChatHeaderContentView.qml +++ b/ui/app/AppLayouts/Chat/views/ChatHeaderContentView.qml @@ -34,7 +34,8 @@ Item { string chatColor, string chatCategoryId, int channelPosition, - var deleteDialog) + var deleteDialog, + bool hideIfPermissionsNotMet) function addRemoveGroupMember() { root.state = d.stateMembersSelectorContent @@ -166,6 +167,7 @@ Item { chatType = chatContentModule.chatDetails.type chatMuted = chatContentModule.chatDetails.muted channelPosition = chatContentModule.chatDetails.position + hideIfPermissionsNotMet = chatContentModule.chatDetails.hideIfPermissionsNotMet } onMuteChat: { @@ -229,7 +231,8 @@ Item { root.displayEditChannelPopup(chatId, chatName, chatDescription, chatEmoji, chatColor, chatCategoryId, channelPosition, - contextMenu.deleteChatConfirmationDialog); + contextMenu.deleteChatConfirmationDialog, + hideIfPermissionsNotMet); } onAddRemoveGroupMember: { root.addRemoveGroupMember() diff --git a/ui/app/AppLayouts/Chat/views/ChatView.qml b/ui/app/AppLayouts/Chat/views/ChatView.qml index 16ae9d47ae..4da02a6d4e 100644 --- a/ui/app/AppLayouts/Chat/views/ChatView.qml +++ b/ui/app/AppLayouts/Chat/views/ChatView.qml @@ -12,9 +12,11 @@ import shared.views.chat 1.0 import shared.stores.send 1.0 import SortFilterProxyModel 0.2 +import StatusQ.Core 0.1 import StatusQ.Layout 0.1 import StatusQ.Popups 0.1 import StatusQ.Controls 0.1 +import QtQuick.Layouts 1.15 import "." import "../panels" @@ -52,6 +54,7 @@ StatusSectionLayout { property bool hasViewAndPostPermissions: false property bool amIMember: false property bool amISectionAdmin: false + readonly property bool allChannelsAreHiddenBecauseNotPermitted: rootStore.allChannelsAreHiddenBecauseNotPermitted property bool isInvitationPending: false @@ -139,6 +142,7 @@ StatusSectionLayout { hasUnseenNotifications: activityCenterStore.hasUnseenNotifications headerContent: Loader { + visible: !root.allChannelsAreHiddenBecauseNotPermitted id: headerContentLoader sourceComponent: root.contentLocked ? joinCommunityHeaderPanelComponent : chatHeaderContentViewComponent } @@ -152,7 +156,8 @@ StatusSectionLayout { centerPanel: Loader { anchors.fill: parent - sourceComponent: root.contentLocked ? joinCommunityCenterPanelComponent : chatColumnViewComponent + sourceComponent: root.allChannelsAreHiddenBecauseNotPermitted ? allChatsAreHiddenComponent : + (root.contentLocked ? joinCommunityCenterPanelComponent : chatColumnViewComponent) } showRightPanel: { @@ -201,7 +206,8 @@ StatusSectionLayout { channelColor: chatColor, categoryId: chatCategoryId, channelPosition: channelPosition, - deleteChatConfirmationDialog: deleteDialog + deleteChatConfirmationDialog: deleteDialog, + hideIfPermissionsNotMet: hideIfPermissionsNotMet }); } } @@ -260,6 +266,18 @@ StatusSectionLayout { } } + Component { + id: allChatsAreHiddenComponent + + StatusBaseText { + Layout.fillWidth: true + Layout.fillHeight: true + visible: root.allChannelsAreHiddenBecauseNotPermitted + text: qsTr("Sorry, you don't hodl the necessary tokens to view or post in any of %1 channels").arg(sectionItemModel.name) + color: Theme.palette.dangerColor1 + } + } + Component { id: contactsColumnComponent ContactsColumnView { diff --git a/ui/app/AppLayouts/Communities/popups/CreateChannelPopup.qml b/ui/app/AppLayouts/Communities/popups/CreateChannelPopup.qml index 11817e620d..05daf48a50 100644 --- a/ui/app/AppLayouts/Communities/popups/CreateChannelPopup.qml +++ b/ui/app/AppLayouts/Communities/popups/CreateChannelPopup.qml @@ -32,7 +32,7 @@ StatusStackModal { property bool isEdit: false property bool isDeleteable: false property bool viewOnlyCanAddReaction - property bool hideIfPermissionsNotMet + property bool hideIfPermissionsNotMet: false property string communityId: "" property string chatId: "_newChannel" @@ -58,8 +58,8 @@ StatusStackModal { readonly property int maxChannelDescLength: 140 // channel signals - signal createCommunityChannel(string chName, string chDescription, string chEmoji, string chColor, string chCategoryId, bool viewOnlyCanAddReaction) - signal editCommunityChannel(string chName, string chDescription, string chEmoji, string chColor, string chCategoryId, bool viewOnlyCanAddReaction) + signal createCommunityChannel(string chName, string chDescription, string chEmoji, string chColor, string chCategoryId, bool viewOnlyCanAddReaction, bool hideIfPermissionsNotMet) + signal editCommunityChannel(string chName, string chDescription, string chEmoji, string chColor, string chCategoryId, bool viewOnlyCanAddReaction, bool hideIfPermissionsNotMet) signal deleteCommunityChannel() // Permissions signals: @@ -187,14 +187,16 @@ StatusStackModal { emoji, colorPanel.color.toString().toUpperCase(), root.categoryId, - d.viewOnlyCanAddReaction) + d.viewOnlyCanAddReaction, + d.hideIfPermissionsNotMet) } else { root.editCommunityChannel(StatusQUtils.Utils.filterXSS(nameInput.input.text), StatusQUtils.Utils.filterXSS(descriptionTextArea.text), emoji, colorPanel.color.toString().toUpperCase(), root.categoryId, - d.viewOnlyCanAddReaction) + d.viewOnlyCanAddReaction, + d.hideIfPermissionsNotMet) } if (d.channelEditModel.dirtyPermissions) { @@ -795,7 +797,6 @@ StatusStackModal { Layout.rightMargin: Style.current.padding leftSide: false text: qsTr("Hide channel from members who don't have permissions to view the channel") - visible: false //TODO: Enable connect to the backend when it's ready https://github.com/status-im/status-desktop/issues/13291 checked: d.hideIfPermissionsNotMet onToggled: { d.hideIfPermissionsNotMet = checked; diff --git a/ui/app/AppLayouts/Communities/views/CommunityColumnView.qml b/ui/app/AppLayouts/Communities/views/CommunityColumnView.qml index a08a5d7c10..96ece0bf82 100644 --- a/ui/app/AppLayouts/Communities/views/CommunityColumnView.qml +++ b/ui/app/AppLayouts/Communities/views/CommunityColumnView.qml @@ -332,6 +332,7 @@ Item { channelPosition = obj.position chatCategoryId = obj.categoryId viewersCanPostReactions = obj.viewersCanPostReactions + hideIfPermissionsNotMet = obj.hideIfPermissionsNotMet } catch (e) { console.error("error parsing chat item json object, id: ", id, " error: ", e) close() @@ -387,7 +388,8 @@ Item { chatId: chatContextMenuView.chatId, channelPosition: channelPosition, viewOnlyCanAddReaction: viewersCanPostReactions, - deleteChatConfirmationDialog: deleteChatConfirmationDialog + deleteChatConfirmationDialog: deleteChatConfirmationDialog, + hideIfPermissionsNotMet: hideIfPermissionsNotMet }); } } @@ -628,9 +630,9 @@ Item { property var deleteChatConfirmationDialog onCreateCommunityChannel: function (chName, chDescription, chEmoji, chColor, - chCategoryId) { + chCategoryId, hideIfPermissionsNotMet) { root.store.createCommunityChannel(chName, chDescription, chEmoji, chColor, - chCategoryId, viewOnlyCanAddReaction) + chCategoryId, viewOnlyCanAddReaction, hideIfPermissionsNotMet) chatId = root.store.currentChatContentModule().chatDetails.id } onEditCommunityChannel: { @@ -641,7 +643,8 @@ Item { chColor, chCategoryId, channelPosition, - viewOnlyCanAddReaction); + viewOnlyCanAddReaction, + hideIfPermissionsNotMet); } onAddPermissions: function (permissions) { diff --git a/ui/imports/shared/views/chat/ChatContextMenuView.qml b/ui/imports/shared/views/chat/ChatContextMenuView.qml index de506ae9c2..424476ab6e 100644 --- a/ui/imports/shared/views/chat/ChatContextMenuView.qml +++ b/ui/imports/shared/views/chat/ChatContextMenuView.qml @@ -29,6 +29,7 @@ StatusMenu { property bool viewersCanPostReactions: true property bool showDebugOptions: false property alias deleteChatConfirmationDialog: deleteChatConfirmationDialogComponent + property bool hideIfPermissionsNotMet: false signal displayProfilePopup(string publicKey) signal displayEditChannelPopup(string chatId) diff --git a/vendor/status-go b/vendor/status-go index b0a0f078c4..b9d9938209 160000 --- a/vendor/status-go +++ b/vendor/status-go @@ -1 +1 @@ -Subproject commit b0a0f078c43b38e555d2683ef56b58e412928e13 +Subproject commit b9d9938209471c67092e95e4f5d1ebace3070a46