diff --git a/storybook/pages/CommunitiesPortalDummyModel.qml b/storybook/pages/CommunitiesPortalDummyModel.qml index 05c88ebb1a..22f841dbe7 100644 --- a/storybook/pages/CommunitiesPortalDummyModel.qml +++ b/storybook/pages/CommunitiesPortalDummyModel.qml @@ -8,14 +8,14 @@ ListModel { Component.onCompleted: append([ { - featured: true, + featured: false, id: "id1", loaded: true, icon: ModelsData.icons.status, banner: ModelsData.banners.status, color: "blue", - name: "Status.im", - description: "Your portal to Web3. Secure wallet. dApp browser. Private messaging. All-in-one.", + name: "Status.app", + description: "Your portal to Web3. Secure wallet. Private messaging. Requires secret tokens to join", members: 130, activeMembers: 61, popularity: 4, @@ -42,8 +42,8 @@ ListModel { "emoji": "💼", }, ]), - permissionsModel: PermissionsModel.shortPermissionsModel, - allTokenRequirementsMet: true + permissionsModel: PermissionsModel.privatePermissionsMemberNotMetModel, + allTokenRequirementsMet: false }, { featured: true, @@ -168,7 +168,7 @@ ListModel { }, ]), permissionsModel: PermissionsModel.threeShortPermissionsModel, - allTokenRequirementsMet: true + allTokenRequirementsMet: false }, { featured: false, @@ -218,7 +218,7 @@ ListModel { "emoji": "💼", }, ]), - permissionsModel: PermissionsModel.longPermissionsModel, + permissionsModel: PermissionsModel.twoLongPermissionsModel, allTokenRequirementsMet: true }, { @@ -235,7 +235,8 @@ ListModel { popularity: 4, available: true, tags: JSON.stringify([]), - permissionsModel: emptyModel + permissionsModel: emptyModel, + allTokenRequirementsMet: false } ]) } diff --git a/storybook/pages/CommunitiesPortalLayoutPage.qml b/storybook/pages/CommunitiesPortalLayoutPage.qml index 01d1871386..0407fb97ed 100644 --- a/storybook/pages/CommunitiesPortalLayoutPage.qml +++ b/storybook/pages/CommunitiesPortalLayoutPage.qml @@ -22,7 +22,7 @@ SplitView { Popups { popupParent: root sharedRootStore: SharedStores.RootStore {} - rootStore: AppLayoutStores.RootStore + rootStore: AppLayoutStores.RootStore {} communityTokensStore: SharedStores.CommunityTokensStore {} } diff --git a/storybook/pages/PermissionsRowPage.qml b/storybook/pages/PermissionsRowPage.qml index 9f4b7ca947..6238013bc5 100644 --- a/storybook/pages/PermissionsRowPage.qml +++ b/storybook/pages/PermissionsRowPage.qml @@ -101,6 +101,22 @@ SplitView { requirementsMet: permissionsMetCheckEditor.checked } + Label { + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + + text: "Private + unmet permissions:" + } + + PermissionsRow { + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + Layout.bottomMargin: spacing + + assetsModel: root.assetsModel + collectiblesModel: root.collectiblesModel + model: PermissionsModel.privatePermissionsMemberNotMetModel + requirementsMet: false + } + Label { Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter @@ -139,7 +155,7 @@ SplitView { } Label { - text: "Row heigh:" + text: "Row height:" } Slider { diff --git a/storybook/src/Models/CommunitiesModel.qml b/storybook/src/Models/CommunitiesModel.qml index 96b1f02fc2..6ac1957a3e 100644 --- a/storybook/src/Models/CommunitiesModel.qml +++ b/storybook/src/Models/CommunitiesModel.qml @@ -115,7 +115,7 @@ ListModel { members: [{ pubKey: "0xdeadbeef" }], membersCount: 1, loading: false, - permissionsModel: null, + permissionsModel: PermissionsModel.privatePermissionsMemberNotMetModel, allTokenRequirementsMet: false }, { diff --git a/storybook/src/Models/PermissionsModel.qml b/storybook/src/Models/PermissionsModel.qml index eca6cc5792..17b0d3a057 100644 --- a/storybook/src/Models/PermissionsModel.qml +++ b/storybook/src/Models/PermissionsModel.qml @@ -85,6 +85,24 @@ QtObject { } ] + readonly property var privatePermissionsMemberModelNotMetData: [ + { + holdingsListModel: root.createHoldingsModel4(), + channelsListModel: root.createChannelsModel1(), + permissionType: PermissionTypes.Type.Member, + permissionState: PermissionTypes.State.Approved, + isPrivate: true, + tokenCriteriaMet: false + }, + { + holdingsListModel: root.createHoldingsModel2(), + channelsListModel: root.createChannelsModel2(), + permissionType: PermissionTypes.Type.Member, + permissionState: PermissionTypes.State.Approved, + isPrivate: true, + tokenCriteriaMet: false + } + ] readonly property var shortPermissionsModelData: [ { @@ -554,6 +572,17 @@ QtObject { } } + readonly property ListModel privatePermissionsMemberNotMetModel: ListModel { + readonly property ModelChangeGuard guard: ModelChangeGuard { + model: root.privatePermissionsMemberNotMetModel + } + + Component.onCompleted: { + append(privatePermissionsMemberModelNotMetData) + guard.enabled = true + } + } + readonly property var shortPermissionsModel: ListModel { readonly property ModelChangeGuard guard: ModelChangeGuard { model: root.shortPermissionsModel diff --git a/ui/app/AppLayouts/Communities/CommunitiesPortalLayout.qml b/ui/app/AppLayouts/Communities/CommunitiesPortalLayout.qml index ba10718d85..3ff060cabc 100644 --- a/ui/app/AppLayouts/Communities/CommunitiesPortalLayout.qml +++ b/ui/app/AppLayouts/Communities/CommunitiesPortalLayout.qml @@ -1,10 +1,10 @@ -import QtQuick 2.13 -import QtQuick.Layouts 1.13 +import QtQuick 2.15 +import QtQuick.Layouts 1.15 import StatusQ 0.1 import StatusQ.Core 0.1 +import StatusQ.Core.Utils 0.1 as SQUtils import StatusQ.Core.Theme 0.1 -import StatusQ.Core.Utils 0.1 import StatusQ.Controls 0.1 import StatusQ.Components 0.1 import StatusQ.Popups 0.1 @@ -71,21 +71,19 @@ StatusSectionLayout { sourceModel: root.communitiesStore.curatedCommunitiesModel filters: [ - ExpressionFilter { - enabled: d.searchMode - expression: { - searcher.text - return name.toLowerCase().includes(searcher.text.toLowerCase()) - } + SQUtils.SearchFilter { + roleName: "name" + searchPhrase: searcher.text }, - ExpressionFilter { + FastExpressionFilter { expression: { return filteredCommunitiesModel.selectedTagsPredicate(communityTags.selectedTagsNames, model.tags) } + expectedRoles: ["tags"] }, - FastExpressionFilter { - expression: !model.amIBanned - expectedRoles: ["amIBanned"] + ValueFilter { + roleName: "amIBanned" + value: false } ] } diff --git a/ui/app/AppLayouts/Communities/controls/PermissionsRow.qml b/ui/app/AppLayouts/Communities/controls/PermissionsRow.qml index accccfac5e..d91a3d21f5 100644 --- a/ui/app/AppLayouts/Communities/controls/PermissionsRow.qml +++ b/ui/app/AppLayouts/Communities/controls/PermissionsRow.qml @@ -1,7 +1,8 @@ -import QtQuick 2.14 -import QtQuick.Controls 2.14 -import QtQuick.Layouts 1.14 +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import StatusQ 0.1 import StatusQ.Core 0.1 import StatusQ.Core.Theme 0.1 import StatusQ.Core.Utils 0.1 @@ -10,6 +11,8 @@ import StatusQ.Controls 0.1 import AppLayouts.Communities.views 1.0 +import SortFilterProxyModel 0.2 + /*! \qmltype PermissionsRow \inherits Control @@ -17,7 +20,7 @@ import AppLayouts.Communities.views 1.0 \brief It is a permissions row control that provides information about community tokens permissions. Inherits \l{https://doc.qt.io/qt-5/qml-qtquick-controls2-control.html}{Control}. The \c PermissionsRow is the token permissions representation row component. - It has different ui abreviations / permutations depending on the tokens and permissons the permissions model provides. + It has different ui abreviations / permutations depending on the tokens and permissions the permissions model provides. Example of how to use it: \qml @@ -112,8 +115,22 @@ Control { property bool dotsVisible: false + readonly property var filteredModel: SortFilterProxyModel { + sourceModel: root.model + filters: FastExpressionFilter { + expression: { + if (model.isPrivate) { + return model.tokenCriteriaMet + } + return true + } + expectedRoles: ["isPrivate", "tokenCriteriaMet"] + } + } + function buildShortModel(model) { shortModel.clear() + dotsVisible = false if(!model) return @@ -170,7 +187,7 @@ Control { implicitHeight: 24 spacing: 4 - padding: 1 + padding: 4 background: Rectangle { color: root.backgroundColor @@ -179,19 +196,14 @@ Control { } contentItem: RowLayout { - id: container - - anchors.centerIn: parent - anchors.margins: root.padding spacing: root.spacing StatusIcon { - Layout.preferredHeight: container.height - 6 - Layout.preferredWidth: Layout.preferredHeight - Layout.leftMargin: 4 + Layout.fillHeight: true + Layout.preferredWidth: height icon: root.requirementsMet ? "tiny/unlocked" : "tiny/locked" - color: Theme.palette.baseColor1 + color: root.hovered ? Theme.palette.directColor1 : Theme.palette.baseColor1 } Repeater { @@ -216,8 +228,8 @@ Control { } StatusRoundedComponent { - Layout.preferredHeight: container.height - Layout.preferredWidth: Layout.preferredHeight + Layout.fillHeight: true + Layout.preferredWidth: height visible: d.dotsVisible color: Theme.palette.baseColor3 @@ -232,21 +244,21 @@ Control { width: height } } - } - onModelChanged: d.buildShortModel(root.model) - Connections { - target: root.model - function onCountChanged() { - d.buildShortModel(root.model) + StatusToolTip { + text: root.requirementsMet ? qsTr("Eligible to join") : qsTr("Not eligible to join") + visible: root.hovered } } - Component.onCompleted: d.buildShortModel(root.model) + + ModelChangeTracker { + model: d.filteredModel + onRevisionChanged: d.buildShortModel(d.filteredModel) + } ListModel { id: shortModel } - - component SinglePermissionRow: RowLayout { + component SinglePermissionRow: RowLayout { id: singlePermissionItem readonly property int maxVisualTokens: 3 @@ -284,7 +296,7 @@ Control { Connections { target: singlePermissionItem.model function onCountChanged() { - buildTokensRowModel(singlePermissionItem.model) + singlePermissionItem.buildTokensRowModel(singlePermissionItem.model) } } Component.onCompleted: buildTokensRowModel(singlePermissionItem.model) @@ -300,8 +312,8 @@ Control { } StatusRoundedImage { - Layout.preferredHeight: container.height - Layout.preferredWidth: Layout.preferredHeight + Layout.fillHeight: true + Layout.preferredWidth: height z: index image.source: model.imageSource @@ -313,8 +325,8 @@ Control { StatusRoundedComponent { visible: singlePermissionItem.plusElementVisible - Layout.preferredHeight: container.height - Layout.preferredWidth: Layout.preferredHeight + Layout.fillHeight: true + Layout.preferredWidth: height z: d.maxTokens color: Theme.palette.baseColor3 diff --git a/ui/imports/shared/views/profile/ProfileShowcaseCommunitiesView.qml b/ui/imports/shared/views/profile/ProfileShowcaseCommunitiesView.qml index 58d387b46e..56f20fd5e5 100644 --- a/ui/imports/shared/views/profile/ProfileShowcaseCommunitiesView.qml +++ b/ui/imports/shared/views/profile/ProfileShowcaseCommunitiesView.qml @@ -1,8 +1,6 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 -import AppLayouts.Communities.controls 1.0 - import StatusQ.Components 0.1 import StatusQ.Controls 0.1 import StatusQ.Core 0.1 @@ -11,6 +9,9 @@ import StatusQ.Popups 0.1 import utils 1.0 +import AppLayouts.Communities.controls 1.0 +import AppLayouts.Communities.helpers 1.0 + Item { id: root @@ -48,7 +49,8 @@ Item { delegate: StatusCommunityCard { id: profileDialogCommunityCard readonly property var permissionsList: model.permissionsModel - readonly property bool requirementsMet: !!model.allTokenRequirementsMet ? model.allTokenRequirementsMet : false + readonly property bool isTokenGatedCommunity: PermissionsHelpers.isTokenGatedCommunity(permissionsList) + cardSize: StatusCommunityCard.Size.Small implicitWidth: GridView.view.cellWidth - Style.current.padding implicitHeight: GridView.view.cellHeight - Style.current.padding @@ -60,7 +62,7 @@ Item { asset.width: 32 asset.height: 32 name: model.name ?? "" - memberCountVisible: false + memberCountVisible: model.joined || !model.encrypted banner: model.bannerImageData ?? "" descriptionFontSize: 12 descriptionFontColor: Theme.palette.baseColor1 @@ -80,8 +82,7 @@ Item { // Community restrictions bottomRowComponent: (model.joined && !root.readOnly) ? communityMembershipComponent : - !!profileDialogCommunityCard.permissionsList && profileDialogCommunityCard.permissionsList.count > 0 ? - permissionsRowComponent : null + isTokenGatedCommunity ? permissionsRowComponent : null Component { id: communityMembershipComponent @@ -116,13 +117,17 @@ Item { Component { id: permissionsRowComponent PermissionsRow { - hoverEnabled: false + readonly property int eligibleToJoinAs: PermissionsHelpers.isEligibleToJoinAs(profileDialogCommunityCard.permissionsList) + assetsModel: root.globalAssetsModel collectiblesModel: root.globalCollectiblesModel model: profileDialogCommunityCard.permissionsList - requirementsMet: profileDialogCommunityCard.requirementsMet + requirementsMet: eligibleToJoinAs === PermissionTypes.Type.Member + || eligibleToJoinAs === PermissionTypes.Type.Admin + || eligibleToJoinAs === PermissionTypes.Type.Owner backgroundBorderColor: Theme.palette.baseColor2 backgroundRadius: 20 + fontPixelSize: 10 } } @@ -174,6 +179,8 @@ Item { root.copyToClipboard(contextMenu.url) } } + + onClosed: destroy() } } }