fix(CommunityPortal): don't show PermissionsRow for free communities

- hide the permissions row and tokens when the community is free to join
- fix evaluating the `requirementsMet` property which affects the lock
icon state; that role was never part of the model
- add a helper C++ method `isTokenGatedCommunity`
- adjust the SB models, adding different variations of the
permissionsModel for the CommunitiesPortalLayoutPage

Fixes #14671
This commit is contained in:
Lukáš Tinkl 2024-05-10 16:55:20 +02:00 committed by Lukáš Tinkl
parent 94ca1ff22a
commit 2537cdc2f2
8 changed files with 94 additions and 39 deletions

View File

@ -3,6 +3,9 @@ import QtQuick 2.14
import Models 1.0 import Models 1.0
ListModel { ListModel {
readonly property var emptyModel: ListModel {}
Component.onCompleted: append([ Component.onCompleted: append([
{ {
featured: true, featured: true,
@ -97,8 +100,6 @@ ListModel {
]), ]),
permissionsModel: PermissionsModel.moreThanTwoInitialShortPermissionsModel, permissionsModel: PermissionsModel.moreThanTwoInitialShortPermissionsModel,
allTokenRequirementsMet: false allTokenRequirementsMet: false
}, },
{ {
featured: false, featured: false,
@ -136,7 +137,7 @@ ListModel {
popularity: 4, popularity: 4,
available: true, available: true,
tags: JSON.stringify([]), tags: JSON.stringify([]),
permissionsModel: PermissionsModel.threeShortPermissionsModelData, permissionsModel: PermissionsModel.channelsOnlyPermissionsModelNotMet,
allTokenRequirementsMet: false allTokenRequirementsMet: false
}, },
{ {
@ -183,7 +184,7 @@ ListModel {
popularity: 4, popularity: 4,
available: true, available: true,
tags: JSON.stringify([]), tags: JSON.stringify([]),
permissionsModel: PermissionsModel.threeShortPermissionsModel, permissionsModel: PermissionsModel.channelsOnlyPermissionsModel,
allTokenRequirementsMet: false allTokenRequirementsMet: false
}, },
{ {
@ -233,7 +234,8 @@ ListModel {
activeMembers: 0, activeMembers: 0,
popularity: 4, popularity: 4,
available: true, available: true,
tags: JSON.stringify([]) tags: JSON.stringify([]),
permissionsModel: emptyModel
} }
]) ])
} }

View File

@ -32,9 +32,10 @@ SplitView {
SplitView.fillHeight: true SplitView.fillHeight: true
assetsModel: AssetsModel {} assetsModel: AssetsModel {}
collectiblesModel: CollectiblesModel {} collectiblesModel: CollectiblesModel {}
communitiesStore: CommunitiesStore { communitiesStore: CommunitiesStore {
readonly property int unreadNotificationsCount: 42 readonly property int unreadNotificationsCount: 42
readonly property bool createCommunityEnabled: true
readonly property string communityTags: ModelsData.communityTags readonly property string communityTags: ModelsData.communityTags
readonly property var curatedCommunitiesModel: SortFilterProxyModel { readonly property var curatedCommunitiesModel: SortFilterProxyModel {

View File

@ -92,7 +92,8 @@ QtObject {
channelsListModel: root.createChannelsModel2(), channelsListModel: root.createChannelsModel2(),
permissionType: PermissionTypes.Type.Member, permissionType: PermissionTypes.Type.Member,
permissionState: PermissionTypes.State.Approved, permissionState: PermissionTypes.State.Approved,
isPrivate: false isPrivate: false,
tokenCriteriaMet: false
} }
] ]
@ -103,7 +104,8 @@ QtObject {
channelsListModel: root.createChannelsModel(), channelsListModel: root.createChannelsModel(),
permissionType: PermissionTypes.Type.Admin, permissionType: PermissionTypes.Type.Admin,
permissionState: PermissionTypes.State.Approved, permissionState: PermissionTypes.State.Approved,
isPrivate: false isPrivate: false,
tokenCriteriaMet: true
} }
] ]
@ -113,21 +115,24 @@ QtObject {
channelsListModel: root.createChannelsModel2(), channelsListModel: root.createChannelsModel2(),
permissionType: PermissionTypes.Type.Member, permissionType: PermissionTypes.Type.Member,
permissionState: PermissionTypes.State.Approved, permissionState: PermissionTypes.State.Approved,
isPrivate: false isPrivate: false,
tokenCriteriaMet: false
}, },
{ {
holdingsListModel: root.createHoldingsModel2(), holdingsListModel: root.createHoldingsModel2(),
channelsListModel: root.createChannelsModel2(), channelsListModel: root.createChannelsModel2(),
permissionType: PermissionTypes.Type.Member, permissionType: PermissionTypes.Type.Member,
permissionState: PermissionTypes.State.Approved, permissionState: PermissionTypes.State.Approved,
isPrivate: false isPrivate: false,
tokenCriteriaMet: true
}, },
{ {
holdingsListModel: root.createHoldingsModel1(), holdingsListModel: root.createHoldingsModel1(),
channelsListModel: root.createChannelsModel2(), channelsListModel: root.createChannelsModel2(),
permissionType: PermissionTypes.Type.Member, permissionType: PermissionTypes.Type.Member,
permissionState: PermissionTypes.State.Approved, permissionState: PermissionTypes.State.Approved,
isPrivate: false isPrivate: false,
tokenCriteriaMet: false
} }
] ]
@ -137,14 +142,16 @@ QtObject {
channelsListModel: root.createChannelsModel1(), channelsListModel: root.createChannelsModel1(),
permissionType: PermissionTypes.Type.Admin, permissionType: PermissionTypes.Type.Admin,
permissionState: PermissionTypes.State.Approved, permissionState: PermissionTypes.State.Approved,
isPrivate: true isPrivate: true,
tokenCriteriaMet: false
}, },
{ {
holdingsListModel: root.createHoldingsModel2(), holdingsListModel: root.createHoldingsModel2(),
channelsListModel: root.createChannelsModel2(), channelsListModel: root.createChannelsModel2(),
permissionType: PermissionTypes.Type.Member, permissionType: PermissionTypes.Type.Member,
permissionState: PermissionTypes.State.Approved, permissionState: PermissionTypes.State.Approved,
isPrivate: false isPrivate: false,
tokenCriteriaMet: true
} }
] ]
@ -154,14 +161,16 @@ QtObject {
channelsListModel: root.createChannelsModel1(), channelsListModel: root.createChannelsModel1(),
permissionType: PermissionTypes.Type.Admin, permissionType: PermissionTypes.Type.Admin,
permissionState: PermissionTypes.State.Approved, permissionState: PermissionTypes.State.Approved,
isPrivate: true isPrivate: true,
tokenCriteriaMet: true
}, },
{ {
holdingsListModel: root.createHoldingsModel4(), holdingsListModel: root.createHoldingsModel4(),
channelsListModel: root.createChannelsModel2(), channelsListModel: root.createChannelsModel2(),
permissionType: PermissionTypes.Type.Member, permissionType: PermissionTypes.Type.Member,
permissionState: PermissionTypes.State.Approved, permissionState: PermissionTypes.State.Approved,
isPrivate: false isPrivate: false,
tokenCriteriaMet: false
} }
] ]
@ -171,21 +180,24 @@ QtObject {
channelsListModel: root.createChannelsModel1(), channelsListModel: root.createChannelsModel1(),
permissionType: PermissionTypes.Type.Admin, permissionType: PermissionTypes.Type.Admin,
permissionState: PermissionTypes.State.Approved, permissionState: PermissionTypes.State.Approved,
isPrivate: true isPrivate: true,
tokenCriteriaMet: false
}, },
{ {
holdingsListModel: root.createHoldingsModel1b(), holdingsListModel: root.createHoldingsModel1b(),
channelsListModel: root.createChannelsModel2(), channelsListModel: root.createChannelsModel2(),
permissionType: PermissionTypes.Type.Member, permissionType: PermissionTypes.Type.Member,
permissionState: PermissionTypes.State.Approved, permissionState: PermissionTypes.State.Approved,
isPrivate: false isPrivate: false,
tokenCriteriaMet: false
}, },
{ {
holdingsListModel: root.createHoldingsModel2(), holdingsListModel: root.createHoldingsModel2(),
channelsListModel: root.createChannelsModel2(), channelsListModel: root.createChannelsModel2(),
permissionType: PermissionTypes.Type.Member, permissionType: PermissionTypes.Type.Member,
permissionState: PermissionTypes.State.Approved, permissionState: PermissionTypes.State.Approved,
isPrivate: false isPrivate: false,
tokenCriteriaMet: false
} }
] ]
@ -195,28 +207,32 @@ QtObject {
channelsListModel: root.createChannelsModel1(), channelsListModel: root.createChannelsModel1(),
permissionType: PermissionTypes.Type.Admin, permissionType: PermissionTypes.Type.Admin,
permissionState: PermissionTypes.State.Approved, permissionState: PermissionTypes.State.Approved,
isPrivate: true isPrivate: true,
tokenCriteriaMet: false
}, },
{ {
holdingsListModel: root.createHoldingsModel2(), holdingsListModel: root.createHoldingsModel2(),
channelsListModel: root.createChannelsModel2(), channelsListModel: root.createChannelsModel2(),
permissionType: PermissionTypes.Type.Member, permissionType: PermissionTypes.Type.Member,
permissionState: PermissionTypes.State.Approved, permissionState: PermissionTypes.State.Approved,
isPrivate: false isPrivate: false,
tokenCriteriaMet: false
}, },
{ {
holdingsListModel: root.createHoldingsModel3(), holdingsListModel: root.createHoldingsModel3(),
channelsListModel: root.createChannelsModel2(), channelsListModel: root.createChannelsModel2(),
permissionType: PermissionTypes.Type.Member, permissionType: PermissionTypes.Type.Member,
permissionState: PermissionTypes.State.Approved, permissionState: PermissionTypes.State.Approved,
isPrivate: false isPrivate: false,
tokenCriteriaMet: true
}, },
{ {
holdingsListModel: root.createHoldingsModel5(), holdingsListModel: root.createHoldingsModel5(),
channelsListModel: root.createChannelsModel2(), channelsListModel: root.createChannelsModel2(),
permissionType: PermissionTypes.Type.Member, permissionType: PermissionTypes.Type.Member,
permissionState: PermissionTypes.State.Approved, permissionState: PermissionTypes.State.Approved,
isPrivate: false isPrivate: false,
tokenCriteriaMet: true
} }
] ]

View File

@ -3,6 +3,8 @@
#include <QObject> #include <QObject>
#include <QJsonArray> #include <QJsonArray>
#include <optional>
class QAbstractItemModel; class QAbstractItemModel;
namespace PermissionTypes { namespace PermissionTypes {
@ -27,9 +29,15 @@ public:
//!< Check whether the user can join the community and under which (highest possible) role //!< Check whether the user can join the community and under which (highest possible) role
//!< @return either: //!< @return either:
//! - `NoPermissions` if the permissionsModel is empty or malformed //! - `NoPermissions` if the permissionsModel is empty or malformed, or has no join type permissions
//! - `Member` if no such join permission(s) exist in the permissionsModel (e.g. when it has channel only permissions)
//! - if satisfied: `TokenMaster`, `Admin`, or `Member`, in this order of relevance //! - if satisfied: `TokenMaster`, `Admin`, or `Member`, in this order of relevance
//! - `Member` if no such join permission(s) exist in the permissionsModel (e.g. when it has channel only permissions)
//! - `None` if no permission to join is satisfied (user can't join at all) //! - `None` if no permission to join is satisfied (user can't join at all)
Q_INVOKABLE int /*PermissionTypes::Type*/ isEligibleToJoinAs(QAbstractItemModel *permissionsModel) const; Q_INVOKABLE int /*PermissionTypes::Type*/ isEligibleToJoinAs(QAbstractItemModel *permissionsModel) const;
//!< @return true when the @p permissionsModel contains some kind of "join" permission; false when the community is free to join
Q_INVOKABLE bool isTokenGatedCommunity(QAbstractItemModel *permissionsModel) const;
private:
std::optional<PermissionTypes::Type> isEligibleToJoinAsInternal(QAbstractItemModel *permissionsModel) const;
}; };

View File

@ -143,7 +143,8 @@ QJsonArray PermissionUtilsInternal::getUniquePermissionChannels(QAbstractItemMod
return result; return result;
} }
int /*PermissionTypes::Type*/ PermissionUtilsInternal::isEligibleToJoinAs(QAbstractItemModel *permissionsModel) const std::optional<PermissionTypes::Type> PermissionUtilsInternal::isEligibleToJoinAsInternal(
QAbstractItemModel *permissionsModel) const
{ {
if (!permissionsModel) if (!permissionsModel)
return PermissionTypes::Type::NoPermissions; return PermissionTypes::Type::NoPermissions;
@ -160,8 +161,6 @@ int /*PermissionTypes::Type*/ PermissionUtilsInternal::isEligibleToJoinAs(QAbstr
return PermissionTypes::Type::NoPermissions; return PermissionTypes::Type::NoPermissions;
} }
QSet<PermissionTypes::Type> tmpRes;
bool hasMemberPermission{false};
constexpr auto isJoinTypePermission = [](PermissionTypes::Type type) { constexpr auto isJoinTypePermission = [](PermissionTypes::Type type) {
return type == PermissionTypes::Type::TokenMaster || return type == PermissionTypes::Type::TokenMaster ||
type == PermissionTypes::Type::Admin || type == PermissionTypes::Type::Admin ||
@ -173,6 +172,8 @@ int /*PermissionTypes::Type*/ PermissionUtilsInternal::isEligibleToJoinAs(QAbstr
}; };
const auto permissionsCount = permissionsModel->rowCount(); const auto permissionsCount = permissionsModel->rowCount();
bool hasMemberPermission{false};
QSet<PermissionTypes::Type> tmpRes;
for (int i = 0; i < permissionsCount; i++) { for (int i = 0; i < permissionsCount; i++) {
const auto permissionType = static_cast<PermissionTypes::Type>(permissionsModel->data(permissionsModel->index(i, 0), permissionTypeRole).toInt()); const auto permissionType = static_cast<PermissionTypes::Type>(permissionsModel->data(permissionsModel->index(i, 0), permissionTypeRole).toInt());
if (isJoinTypePermission(permissionType)) { if (isJoinTypePermission(permissionType)) {
@ -191,10 +192,24 @@ int /*PermissionTypes::Type*/ PermissionUtilsInternal::isEligibleToJoinAs(QAbstr
if (tmpRes.contains(PermissionTypes::Type::Admin)) if (tmpRes.contains(PermissionTypes::Type::Admin))
return PermissionTypes::Type::Admin; return PermissionTypes::Type::Admin;
if (tmpRes.contains(PermissionTypes::Type::Member) || !hasMemberPermission) if (tmpRes.contains(PermissionTypes::Type::Member))
return PermissionTypes::Type::Member; return PermissionTypes::Type::Member;
return PermissionTypes::Type::None; if (!hasMemberPermission)
return {}; // no join permissions -> free to join
return PermissionTypes::Type::None; // not allowed to join due to permissions not satisfied
}
int /*PermissionTypes::Type*/ PermissionUtilsInternal::isEligibleToJoinAs(QAbstractItemModel *permissionsModel) const
{
return isEligibleToJoinAsInternal(permissionsModel).value_or(PermissionTypes::Type::Member);
}
bool PermissionUtilsInternal::isTokenGatedCommunity(QAbstractItemModel *permissionsModel) const
{
const auto result = isEligibleToJoinAsInternal(permissionsModel);
return result && *result > PermissionTypes::Type::NoPermissions;
} }
QVariantMap PermissionUtilsInternal::getTokenByKey(QAbstractItemModel *model, QVariantMap PermissionUtilsInternal::getTokenByKey(QAbstractItemModel *model,

View File

@ -194,7 +194,6 @@ StatusSectionLayout {
visible: (d.searchMode && filteredCommunitiesModel.count === 0) || communitiesGrid.isEmpty visible: (d.searchMode && filteredCommunitiesModel.count === 0) || communitiesGrid.isEmpty
text: qsTr("No communities found") text: qsTr("No communities found")
color: Theme.palette.baseColor1 color: Theme.palette.baseColor1
font.pixelSize: 15
} }
} }
} }

View File

@ -63,6 +63,10 @@ QtObject {
return Internal.PermissionUtils.isEligibleToJoinAs(model) return Internal.PermissionUtils.isEligibleToJoinAs(model)
} }
function isTokenGatedCommunity(model) {
return Internal.PermissionUtils.isTokenGatedCommunity(model)
}
function setHoldingsTextFormat(type, name, amount, decimals) { function setHoldingsTextFormat(type, name, amount, decimals) {
if (typeof amount === "string") { if (typeof amount === "string") {
amount = AmountsArithmetic.toNumber(AmountsArithmetic.fromString(amount), decimals) amount = AmountsArithmetic.toNumber(AmountsArithmetic.fromString(amount), decimals)

View File

@ -11,6 +11,7 @@ import utils 1.0
import SortFilterProxyModel 0.2 import SortFilterProxyModel 0.2
import AppLayouts.Communities.controls 1.0 import AppLayouts.Communities.controls 1.0
import AppLayouts.Communities.helpers 1.0
StatusScrollView { StatusScrollView {
id: root id: root
@ -65,7 +66,7 @@ StatusScrollView {
readonly property string tags: model.tags readonly property string tags: model.tags
readonly property var permissionsList: model.permissionsModel readonly property var permissionsList: model.permissionsModel
readonly property bool requirementsMet: !!model.allTokenRequirementsMet ? model.allTokenRequirementsMet : false readonly property bool isTokenGatedCommunity: PermissionsHelpers.isTokenGatedCommunity(permissionsList)
JSONListModel { JSONListModel {
id: tagsJson id: tagsJson
@ -85,15 +86,24 @@ StatusScrollView {
categories: tagsJson.model categories: tagsJson.model
memberCountVisible: model.joined || !model.encrypted memberCountVisible: model.joined || !model.encrypted
// Community restrictions // Community restrictions
rigthHeaderComponent: PermissionsRow { Binding {
visible: !!card.permissionsList && card.permissionsList.count > 0 target: card
assetsModel: root.assetsModel property: "rigthHeaderComponent"
collectiblesModel: root.collectiblesModel when: card.isTokenGatedCommunity
model: card.permissionsList value: Component {
requirementsMet: card.requirementsMet PermissionsRow {
overlappingBorder: 0 readonly property int eligibleToJoinAs: PermissionsHelpers.isEligibleToJoinAs(card.permissionsList)
assetsModel: root.assetsModel
collectiblesModel: root.collectiblesModel
model: card.permissionsList
requirementsMet: eligibleToJoinAs === PermissionTypes.Type.Member
|| eligibleToJoinAs === PermissionTypes.Type.Admin
|| eligibleToJoinAs === PermissionTypes.Type.Owner
overlappingBorder: 0
}
}
} }
onClicked: root.cardClicked(communityId) onClicked: root.cardClicked(communityId)