From e9fdaa1e67ba67f35eb89d56b39d213c00326fac Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Mon, 22 Dec 2025 15:30:51 -0500 Subject: [PATCH] fix(community): disable anyone can be admin or member permissions Fixes #19620 We enabled users to create "anyone can be admin" or "anyone can join" permissions , which was wrong. The former would be chaotic and makes no sense to support. The latter is just the default of any community without permissions, so it was just cluttering the description. The solution was to disable the "Community" part of the "is allowed to" popup when "anyone" is active. I also improved the error and loading handling of the permission creation flow. We can make it async now if we want and we show a better error to the user if something goes wrong. Before, if there was an error, you lost the screen and had to start everything again. --- .../modules/main/chat_section/controller.nim | 5 +++ .../main/chat_section/io_interface.nim | 3 ++ src/app/modules/main/chat_section/module.nim | 8 ++++ src/app/modules/main/chat_section/view.nim | 41 ++++++++++++++++++- src/app_service/service/community/service.nim | 10 +++-- storybook/pages/EditPermissionViewPage.qml | 14 +++++++ .../panels/PermissionsSettingsPanel.qml | 13 ++++-- .../views/CommunitySettingsView.qml | 11 +++++ .../Communities/views/EditPermissionView.qml | 26 +++++++++++- ui/i18n/qml_base_en.ts | 4 ++ ui/i18n/qml_base_lokalise_en.ts | 5 +++ ui/i18n/qml_cs.ts | 4 ++ ui/i18n/qml_es.ts | 4 ++ ui/i18n/qml_ko.ts | 4 ++ vendor/status-go | 2 +- 15 files changed, 142 insertions(+), 12 deletions(-) diff --git a/src/app/modules/main/chat_section/controller.nim b/src/app/modules/main/chat_section/controller.nim index cc0adf49f58..3e5d84edf5f 100644 --- a/src/app/modules/main/chat_section/controller.nim +++ b/src/app/modules/main/chat_section/controller.nim @@ -258,6 +258,11 @@ proc init*(self: Controller) = if (args.communityId == self.sectionId): self.delegate.onCategoryUnmuted(args.categoryId) + self.events.on(SIGNAL_COMMUNITY_TOKEN_PERMISSION_CREATION_OR_UPDATE_SUCCEEDED) do(e: Args): + let args = CommunityTokenPermissionArgs(e) + if (args.communityId == self.sectionId): + self.delegate.onCommunityTokenPermissionCreationOrUpdateSucceeded(args.communityId) + self.events.on(SIGNAL_COMMUNITY_TOKEN_PERMISSION_CREATION_FAILED) do(e: Args): let args = CommunityTokenPermissionArgs(e) if (args.communityId == self.sectionId): diff --git a/src/app/modules/main/chat_section/io_interface.nim b/src/app/modules/main/chat_section/io_interface.nim index f0fecb881b4..93d02c6f8d8 100644 --- a/src/app/modules/main/chat_section/io_interface.nim +++ b/src/app/modules/main/chat_section/io_interface.nim @@ -349,6 +349,9 @@ method setCommunityMetrics*(self: AccessInterface, metrics: CommunityMetricsDto) method onCommunityTokenPermissionCreated*(self: AccessInterface, communityId: string, tokenPermission: CommunityTokenPermissionDto) {.base.} = raise newException(ValueError, "No implementation available") +method onCommunityTokenPermissionCreationOrUpdateSucceeded*(self: AccessInterface, communityId: string) {.base.} = + raise newException(ValueError, "No implementation available") + method onCommunityTokenPermissionCreationFailed*(self: AccessInterface, communityId: string) {.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 b25771459de..4faf9a294bc 100644 --- a/src/app/modules/main/chat_section/module.nim +++ b/src/app/modules/main/chat_section/module.nim @@ -1057,11 +1057,19 @@ method onCommunityTokenPermissionUpdated*(self: Module, communityId: string, tok if self.showPermissionUpdateNotification(community, tokenPermission): singletonInstance.globalEvents.showCommunityTokenPermissionUpdatedNotification(communityId, "Community permission updated", "A token permission has been updated") +method onCommunityTokenPermissionCreationOrUpdateSucceeded*(self: Module, communityId: string) = + self.view.setPermissionSaveInProgress(false) + self.view.permissionSavedSuccessfully() + method onCommunityTokenPermissionCreationFailed*(self: Module, communityId: string) = singletonInstance.globalEvents.showCommunityTokenPermissionCreationFailedNotification(communityId, "Failed to create community permission", "Something went wrong") + self.view.setPermissionSaveInProgress(false) + self.view.setErrorSavingPermission("Failed to create permission. Please try again.") method onCommunityTokenPermissionUpdateFailed*(self: Module, communityId: string) = singletonInstance.globalEvents.showCommunityTokenPermissionUpdateFailedNotification(communityId, "Failed to update community permission", "Something went wrong") + self.view.setPermissionSaveInProgress(false) + self.view.setErrorSavingPermission("Failed to update permission. Please try again.") method onCommunityTokenPermissionDeletionFailed*(self: Module, communityId: string) = singletonInstance.globalEvents.showCommunityTokenPermissionDeletionFailedNotification(communityId, "Failed to delete community permission", "Something went wrong") diff --git a/src/app/modules/main/chat_section/view.nim b/src/app/modules/main/chat_section/view.nim index bcc4a49ae9a..6099861e2bd 100644 --- a/src/app/modules/main/chat_section/view.nim +++ b/src/app/modules/main/chat_section/view.nim @@ -35,7 +35,11 @@ QtObject: memberMessagesModelVariant: QVariant requestToJoinState: RequestToJoinState communityMemberReevaluationStatus: int + permissionSaveInProgress: bool + errorSavingPermission: string + proc setPermissionSaveInProgress*(self: View, value: bool) + proc setErrorSavingPermission*(self: View, value: string) proc delete*(self: View) proc newView*(delegate: io_interface.AccessInterface): View = @@ -62,6 +66,8 @@ QtObject: result.memberMessagesModelVariant = newQVariant(result.memberMessagesModel) result.requestToJoinState = RequestToJoinState.None result.communityMemberReevaluationStatus = 0 + result.permissionSaveInProgress = false + result.errorSavingPermission = "" proc load*(self: View) = self.delegate.viewDidLoad() @@ -344,7 +350,8 @@ QtObject: read = getTokenPermissionsModel proc createOrEditCommunityTokenPermission*(self: View, permissionId: string, permissionType: int, tokenCriteriaJson: string, channelIDs: string, isPrivate: bool) {.slot.} = - + self.setPermissionSaveInProgress(true) + self.setErrorSavingPermission("") let chatIDs = channelIDs.split(',') self.delegate.createOrEditCommunityTokenPermission(permissionId, permissionType, tokenCriteriaJson, chatIDs, isPrivate) @@ -519,6 +526,36 @@ QtObject: proc markAllReadInCommunity*(self: View) {.slot.} = self.delegate.markAllReadInCommunity() + proc permissionSavedSuccessfully*(self: View) {.signal.} + + proc permissionSaveInProgressChanged*(self: View) {.signal.} + + proc getPermissionSaveInProgress*(self: View): bool {.slot.} = + return self.permissionSaveInProgress + + proc setPermissionSaveInProgress*(self: View, value: bool) = + if self.permissionSaveInProgress == value: + return + self.permissionSaveInProgress = value + self.permissionSaveInProgressChanged() + + QtProperty[bool] permissionSaveInProgress: + read = getPermissionSaveInProgress + notify = permissionSaveInProgressChanged + + proc errorSavingPermissionChanged*(self: View) {.signal.} + + proc setErrorSavingPermission*(self: View, value: string) = + if self.errorSavingPermission == value: + return + self.errorSavingPermission = value + self.errorSavingPermissionChanged() + + proc getErrorSavingPermission*(self: View): string {.slot.} = + return self.errorSavingPermission + QtProperty[string] errorSavingPermission: + read = getErrorSavingPermission + notify = errorSavingPermissionChanged + proc delete*(self: View) = self.QObject.delete - diff --git a/src/app_service/service/community/service.nim b/src/app_service/service/community/service.nim index e907af2c7cf..20c08a08e36 100644 --- a/src/app_service/service/community/service.nim +++ b/src/app_service/service/community/service.nim @@ -231,6 +231,7 @@ const SIGNAL_DISCORD_CHANNEL_IMPORT_CANCELED* = "discordChannelImportCanceled" const SIGNAL_MEMBER_REEVALUATION_STATUS* = "communityMemberReevaluationStatus" const SIGNAL_COMMUNITY_TOKEN_PERMISSION_CREATED* = "communityTokenPermissionCreated" +const SIGNAL_COMMUNITY_TOKEN_PERMISSION_CREATION_OR_UPDATE_SUCCEEDED* = "communityTokenPermissionCreationOrUpdateSucceeded" const SIGNAL_COMMUNITY_TOKEN_PERMISSION_CREATION_FAILED* = "communityTokenPermissionCreationFailed" const SIGNAL_COMMUNITY_TOKEN_PERMISSION_UPDATED* = "communityTokenPermissionUpdated" const SIGNAL_COMMUNITY_TOKEN_PERMISSION_UPDATE_FAILED* = "communityTokenPermissionUpdateFailed" @@ -2213,8 +2214,8 @@ QtObject: error "Error canceling discord channel import", msg = e.msg proc createOrEditCommunityTokenPermission*(self: Service, communityId: string, tokenPermission: CommunityTokenPermissionDto) = + let editing = tokenPermission.id != "" try: - let editing = tokenPermission.id != "" var response: RpcResponse[JsonNode] if editing: response = status_go.editCommunityTokenPermission(communityId, tokenPermission.id, int(tokenPermission.`type`), Json.encode(tokenPermission.tokenCriteria), tokenPermission.chatIDs, tokenPermission.isPrivate) @@ -2222,15 +2223,18 @@ QtObject: response = status_go.createCommunityTokenPermission(communityId, int(tokenPermission.`type`), Json.encode(tokenPermission.tokenCriteria), tokenPermission.chatIDs, tokenPermission.isPrivate) if response.result != nil and response.result.kind != JNull: + self.events.emit(SIGNAL_COMMUNITY_TOKEN_PERMISSION_CREATION_OR_UPDATE_SUCCEEDED, CommunityTokenPermissionArgs(communityId: communityId, tokenPermission: tokenPermission)) return + raise newException(RpcException, "No result returned from create/edit community token permission") + except Exception as e: + error "Error creating/editing community token permission", msg = e.msg + var signal = SIGNAL_COMMUNITY_TOKEN_PERMISSION_CREATION_FAILED if editing: signal = SIGNAL_COMMUNITY_TOKEN_PERMISSION_UPDATE_FAILED self.events.emit(signal, CommunityTokenPermissionArgs(communityId: communityId, tokenPermission: tokenPermission)) - except Exception as e: - error "Error creating/editing community token permission", msg = e.msg proc deleteCommunityTokenPermission*(self: Service, communityId: string, permissionId: string) = try: diff --git a/storybook/pages/EditPermissionViewPage.qml b/storybook/pages/EditPermissionViewPage.qml index c7831c28da6..38ac8595b72 100644 --- a/storybook/pages/EditPermissionViewPage.qml +++ b/storybook/pages/EditPermissionViewPage.qml @@ -28,6 +28,8 @@ SplitView { isPrivate: isPrivateCheckBox.checked permissionDuplicated: isPermissionDuplicatedCheckBox.checked permissionTypeLimitReached: isLimitReachedCheckBox.checked + saveInProgress: isSavingInProgressCheckBox.checked + errorSaving: errorSavingCheckBox.checked ? "Wrong permission data" : "" assetsModel: AssetsModel {} collectiblesModel: CollectiblesModel {} @@ -103,6 +105,18 @@ SplitView { text: "Is limit reached" } + + CheckBox { + id: isSavingInProgressCheckBox + + text: "Is saving in progress" + } + + CheckBox { + id: errorSavingCheckBox + + text: "Error saving" + } } Button { diff --git a/ui/app/AppLayouts/Communities/panels/PermissionsSettingsPanel.qml b/ui/app/AppLayouts/Communities/panels/PermissionsSettingsPanel.qml index b62e5152f30..ee10d901585 100644 --- a/ui/app/AppLayouts/Communities/panels/PermissionsSettingsPanel.qml +++ b/ui/app/AppLayouts/Communities/panels/PermissionsSettingsPanel.qml @@ -27,6 +27,8 @@ StackView { property bool showChannelSelector: true property bool ensCommunityPermissionsEnabled property alias initialPage: initialItem + property bool saveInProgress: false + property string errorSaving: "" // id, name, image, color, owner properties expected required property var communityDetails @@ -43,6 +45,11 @@ StackView { signal removePermissionRequested(string key) signal navigateToMintTokenSettings(bool isAssetType) + function permissionSavedSuccessfully() { + // Go back to the permissions list after successful save + root.pop(StackView.Immediate) + } + function navigateBack() { if (depth === 2 && currentItem.toast.active) currentItem.toast.notifyDirty() @@ -212,6 +219,8 @@ StackView { ensCommunityPermissionsEnabled: root.ensCommunityPermissionsEnabled holdingsRequired: selectedHoldingsModel ? selectedHoldingsModel.count > 0 : false + saveInProgress: root.saveInProgress + errorSaving: root.errorSaving permissionDuplicated: { // dependencies @@ -290,10 +299,6 @@ StackView { root.createPermissionRequested( dirtyValues.permissionType, holdings, channels, dirtyValues.isPrivate) - - if (root.showChannelSelector) { - root.pop(StackView.Immediate) - } } onNavigateToMintTokenSettings: root.navigateToMintTokenSettings(isAssetType) diff --git a/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml b/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml index dcea296ce26..1771e0f376d 100644 --- a/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml +++ b/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml @@ -350,6 +350,7 @@ StatusSectionLayout { // PERMISISONS Loader { + id: permissionsSettingsPanelLoader active: false readonly property int sectionKey: Constants.CommunitySettingsSections.Permissions readonly property string sectionName: qsTr("Permissions") @@ -372,6 +373,9 @@ StatusSectionLayout { collectiblesModel: rootStore.collectiblesModel channelsModel: rootStore.chatCommunitySectionModule.model + + saveInProgress: rootStore.chatCommunitySectionModule.permissionSaveInProgress + errorSaving: rootStore.chatCommunitySectionModule.errorSavingPermission ensCommunityPermissionsEnabled: root.ensCommunityPermissionsEnabled @@ -797,5 +801,12 @@ StatusSectionLayout { Global.openPopup(noPermissionsPopupCmp, properties) } + + function onPermissionSavedSuccessfully() { + if (!permissionsSettingsPanelLoader.active) { + return + } + permissionsSettingsPanelLoader.item.permissionSavedSuccessfully() + } } } diff --git a/ui/app/AppLayouts/Communities/views/EditPermissionView.qml b/ui/app/AppLayouts/Communities/views/EditPermissionView.qml index 6b42eb495f7..f591f8924a9 100644 --- a/ui/app/AppLayouts/Communities/views/EditPermissionView.qml +++ b/ui/app/AppLayouts/Communities/views/EditPermissionView.qml @@ -56,6 +56,9 @@ StatusScrollView { property bool showChannelSelector: true property bool ensCommunityPermissionsEnabled + property bool saveInProgress: false + property string errorSaving: "" + // roles: type, key, name, amount, imageSource property var selectedHoldingsModel: ListModel {} @@ -185,6 +188,7 @@ StatusScrollView { SequenceColumnLayout { id: sequenceColumnLayout + enabled: !root.saveInProgress width: root.availableWidth title: qsTr("Anyone") @@ -452,9 +456,12 @@ StatusScrollView { directParent: permissionsSelector.addButton - allowCommunityOptions: root.showChannelSelector + // Hide community options if we are in the Channel popup or if "Anyone is allowed to" is enabled + // since making everyone Admin doesn't make sense and anyone can join the community is the default. + allowCommunityOptions: root.showChannelSelector && d.dirtyValues.holdingsRequired initialPermissionType: d.dirtyValues.permissionType - enableAdminPermission: root.communityDetails.owner + // Only owners can assign Admin permissions + enableAdminPermission: root.communityDetails.owner onDone: function(permissionType) { if (d.dirtyValues.permissionType === permissionType) { @@ -654,6 +661,20 @@ StatusScrollView { iconColor: textColor } + StatusWarningBox { + Layout.fillWidth: true + Layout.maximumWidth: root.preferredContentWidth + Layout.rightMargin: root.internalRightPadding + + Layout.topMargin: Theme.padding + visible: root.errorSaving !== "" + icon: "close-circle" + text: qsTr("There was an error saving the permission: %1").arg(root.errorSaving) + borderColor: Theme.palette.baseColor1 + textColor: Theme.palette.dangerColor1 + iconColor: textColor + } + StatusButton { Layout.preferredHeight: 44 Layout.fillWidth: true @@ -665,6 +686,7 @@ StatusScrollView { objectName: "createPermissionButton" text: qsTr("Create permission") enabled: root.saveEnabled + loading: root.saveInProgress onClicked: root.createPermissionClicked() } diff --git a/ui/i18n/qml_base_en.ts b/ui/i18n/qml_base_en.ts index 1822357522b..8308722f72a 100644 --- a/ui/i18n/qml_base_en.ts +++ b/ui/i18n/qml_base_en.ts @@ -6620,6 +6620,10 @@ Remember your password and don't share it with anyone. Create permission + + There was an error saving the permission: %1 + + EditSlippagePanel diff --git a/ui/i18n/qml_base_lokalise_en.ts b/ui/i18n/qml_base_lokalise_en.ts index 188d8d29dc5..38aff2d2824 100644 --- a/ui/i18n/qml_base_lokalise_en.ts +++ b/ui/i18n/qml_base_lokalise_en.ts @@ -8086,6 +8086,11 @@ EditPermissionView Create permission + + There was an error saving the permission: %1 + EditPermissionView + There was an error saving the permission: %1 + EditSlippagePanel diff --git a/ui/i18n/qml_cs.ts b/ui/i18n/qml_cs.ts index 26114309344..2c51656da49 100644 --- a/ui/i18n/qml_cs.ts +++ b/ui/i18n/qml_cs.ts @@ -6657,6 +6657,10 @@ Pamatujte si své heslo a s nikým ho nesdílejte. Create permission Vytvořit oprávnění + + There was an error saving the permission: %1 + + EditSlippagePanel diff --git a/ui/i18n/qml_es.ts b/ui/i18n/qml_es.ts index 5c6d9d4a8bb..4ce964eeb42 100644 --- a/ui/i18n/qml_es.ts +++ b/ui/i18n/qml_es.ts @@ -6633,6 +6633,10 @@ Recuerda tu contraseña y no la compartas con nadie. Create permission Crear permiso + + There was an error saving the permission: %1 + + EditSlippagePanel diff --git a/ui/i18n/qml_ko.ts b/ui/i18n/qml_ko.ts index daaa56496cf..486a9b59786 100644 --- a/ui/i18n/qml_ko.ts +++ b/ui/i18n/qml_ko.ts @@ -6608,6 +6608,10 @@ Remember your password and don't share it with anyone. Create permission 권한 생성 + + There was an error saving the permission: %1 + + EditSlippagePanel diff --git a/vendor/status-go b/vendor/status-go index 63fe9e7227e..4df75ee7488 160000 --- a/vendor/status-go +++ b/vendor/status-go @@ -1 +1 @@ -Subproject commit 63fe9e7227e44394853f3f8b73ea655ef7903340 +Subproject commit 4df75ee7488f3f56ac68bfaa80e62b37cb243dc0