fix(community): Token gating info when permission not met and set to private

- hide the permission from the `PermissionsRow` when it's set to private
and the conditions are not met
- display a tooltip "(Not) eligible to join" over the lock icon
- show the same info in both community portal and profile dialog's
community showcase tab
- speedup searching/filtering in the community portal
- fixup and extend the SB pages to demonstrate the new behavior

Fixes #14747
This commit is contained in:
Lukáš Tinkl 2024-10-03 18:50:43 +02:00 committed by Lukáš Tinkl
parent fe9308fa1e
commit 235162dc01
8 changed files with 124 additions and 61 deletions

View File

@ -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
}
])
}

View File

@ -22,7 +22,7 @@ SplitView {
Popups {
popupParent: root
sharedRootStore: SharedStores.RootStore {}
rootStore: AppLayoutStores.RootStore
rootStore: AppLayoutStores.RootStore {}
communityTokensStore: SharedStores.CommunityTokensStore {}
}

View File

@ -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 {

View File

@ -115,7 +115,7 @@ ListModel {
members: [{ pubKey: "0xdeadbeef" }],
membersCount: 1,
loading: false,
permissionsModel: null,
permissionsModel: PermissionsModel.privatePermissionsMemberNotMetModel,
allTokenRequirementsMet: false
},
{

View File

@ -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

View File

@ -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
}
]
}

View File

@ -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

View File

@ -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()
}
}
}