feat: Add missing eligible to join tag in CommunityMembershipSetupDialog

- implement the eligibility check in C++, returning the highest possible
role the user would be allowed to join under
- enable/disable the "Share" button based on the above permissions check
- remove all the locally placed components, access teh popup only via
Global/Popups
- calculate the `accessType` internally based on the permissions present
- update the eligibility as the async check for permissions is finished
- fix the permissions panel background color
- partially revert the share/finish/cancel buttons behavior; it must be
one button due to StatusStackModal limitations
- fix some other minor UI issues or differences to current Figma designs
- adjust SB, add the possibility to play around with different
permission models

Fixes #14100
This commit is contained in:
Lukáš Tinkl 2024-03-27 13:48:17 +01:00 committed by Lukáš Tinkl
parent 255f318627
commit b191caaec6
24 changed files with 415 additions and 427 deletions

View File

@ -137,6 +137,8 @@ QtObject:
return self.items[idx]
proc tokenCriteriaUpdated(self: TokenPermissionsModel) {.signal.}
proc updateItem*(self: TokenPermissionsModel, permissionId: string, item: TokenPermissionItem) =
let idx = self.findIndexById(permissionId)
if(idx == -1):
@ -159,3 +161,4 @@ QtObject:
ModelRole.TokenCriteriaMet.int,
ModelRole.State.int
])
self.tokenCriteriaUpdated()

View File

@ -23,49 +23,41 @@ SplitView {
assetsWithFilteredBalances: groupedAccountsAssetsModel
}
Item {
SplitView.fillWidth: true
SplitView.fillHeight: true
function openDialog() {
popupComponent.createObject(popupBg)
}
PopupBackground {
anchors.fill: parent
}
Button {
anchors.centerIn: parent
text: "Reopen"
onClicked: dialog.open()
}
Component.onCompleted: openDialog()
Component {
id: popupComponent
CommunityMembershipSetupDialog {
id: dialog
anchors.centerIn: parent
visible: true
modal: false
visible: true
closePolicy: Popup.NoAutoClose
isEditMode: ctrlIsEditMode.checked
communityName: "Status"
communityIcon: ModelsData.icons.status
introMessage: "%1 sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
communityName: ctrlCommunityName.text
communityIcon: {
if (ctrlIconStatus.checked)
return ModelsData.icons.status
if (ctrlIconCryptoPunks.checked)
return ModelsData.icons.cryptPunks
if (ctrlIconRarible.checked)
return ModelsData.icons.rarible
if (ctrlIconNone.checked)
return ""
}
1. Ut enim ad minim veniam
2. Excepteur sint occaecat cupidatat non proident
3. Duis aute irure
4. Dolore eu fugiat nulla pariatur
5. 🚗 consectetur adipiscing elit
Nemo enim 😋 ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.".arg(dialog.communityName)
introMessage: ctrlIntro.text.arg(communityName)
isInvitationPending: ctrlIsInvitationPending.checked
requirementsCheckPending: ctrlRequirementsCheckPending.checked
walletAccountsModel: WalletAccountsModel {}
walletAssetsModel: root.walletAssetStore.groupedAccountAssetsModel
permissionsModel: dialog.accessType === Constants.communityChatOnRequestAccess ? PermissionsModel.complexPermissionsModel
: null
permissionsModel: ctrlPermissionsModel.currentValue
assetsModel: AssetsModel {}
collectiblesModel: CollectiblesModel {}
@ -86,6 +78,23 @@ Nemo enim 😋 ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit,
})
}
}
}
Item {
SplitView.fillWidth: true
SplitView.fillHeight: true
PopupBackground {
id: popupBg
anchors.fill: parent
}
Button {
anchors.centerIn: parent
text: "Reopen"
onClicked: root.openDialog()
}
}
LogsAndControlsPanel {
@ -112,21 +121,29 @@ Nemo enim 😋 ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit,
}
TextField {
id: ctrlCommunityName
Layout.fillWidth: true
text: dialog.communityName
onTextChanged: dialog.communityName = text
text: "Status"
}
Label {
Layout.fillWidth: true
text: "Intro message"
text: "Intro"
font.weight: Font.Bold
}
TextField {
id: ctrlIntro
Layout.fillWidth: true
text: dialog.introMessage
onTextChanged: dialog.introMessage = text
text: "%1 sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
1. Ut enim ad minim veniam
2. Excepteur sint occaecat cupidatat non proident
3. Duis aute irure
4. Dolore eu fugiat nulla pariatur
5. 🚗 consectetur adipiscing elit
Nemo enim 😋 ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt."
}
ColumnLayout {
@ -136,21 +153,21 @@ Nemo enim 😋 ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit,
font.weight: Font.Bold
}
RadioButton {
id: ctrlIconStatus
checked: true
text: "Status"
onCheckedChanged: if(checked) dialog.communityIcon = ModelsData.icons.status
}
RadioButton {
id: ctrlIconCryptoPunks
text: "Crypto Punks"
onCheckedChanged: if(checked) dialog.communityIcon = ModelsData.icons.cryptPunks
}
RadioButton {
id: ctrlIconRarible
text: "Rarible"
onCheckedChanged: if(checked) dialog.communityIcon = ModelsData.icons.rarible
}
RadioButton {
id: ctrlIconNone
text: "None"
onCheckedChanged: if(checked) dialog.communityIcon = ""
}
}
@ -161,31 +178,41 @@ Nemo enim 😋 ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit,
CheckBox {
id: ctrlIsEditMode
visible: !dialog.isInvitationPending
visible: !ctrlIsInvitationPending.checked
text: "Is edit mode"
}
ColumnLayout {
visible: !dialog.isInvitationPending
Label {
Layout.fillWidth: true
text: "Access type:"
}
visible: !ctrlIsInvitationPending.checked
RadioButton {
checked: true
text: qsTr("Public access")
onCheckedChanged: dialog.accessType = Constants.communityChatPublicAccess
}
RadioButton {
text: qsTr("On request")
onCheckedChanged: dialog.accessType = Constants.communityChatOnRequestAccess
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: 12
Label {
text: "Permissions:"
}
ComboBox {
Layout.fillWidth: true
id: ctrlPermissionsModel
textRole: "text"
valueRole: "value"
model: [
{ value: PermissionsModel.complexCombinedPermissionsModel, text: "complexCombined" },
{ value: PermissionsModel.complexCombinedPermissionsModelNotMet, text: "complexCombinedNotMet" },
{ value: PermissionsModel.complexPermissionsModel, text: "complex" },
{ value: PermissionsModel.complexPermissionsModelNotMet, text: "complexNotMet" },
{ value: PermissionsModel.channelsOnlyPermissionsModel, text: "channelsOnly" },
{ value: PermissionsModel.channelsOnlyPermissionsModelNotMet, text: "channelsOnlyNotMet" },
{ value: null, text: "null" }
]
}
}
}
CheckBox {
Layout.leftMargin: 12
id: ctrlRequirementsCheckPending
visible: !dialog.isInvitationPending && dialog.accessType == Constants.communityChatOnRequestAccess
visible: !ctrlIsInvitationPending.checked
text: "Requirements check pending"
}

View File

@ -66,6 +66,24 @@ ListModel {
symbol: "SNT",
category: TokenCategories.Category.General,
communityId: ""
},
{
key: "stt",
iconSource: ModelsData.assets.snt,
name: "stt",
shortName: "stt",
symbol: "STT",
category: TokenCategories.Category.Own,
communityId: ""
},
{
key: "eth",
iconSource: ModelsData.assets.eth,
name: "eth",
shortName: "eth",
symbol: "ETH",
category: TokenCategories.Category.General,
communityId: ""
}
]

View File

@ -239,6 +239,22 @@ QtObject {
isPrivate: false,
tokenCriteriaMet: false
},
{
id: "tmaster1",
holdingsListModel: root.createHoldingsModel2(),
permissionType: PermissionTypes.Type.TokenMaster,
permissionState: PermissionTypes.State.Approved,
isPrivate: false,
tokenCriteriaMet: true
},
{
id: "tmaster2",
holdingsListModel: root.createHoldingsModel3(),
permissionType: PermissionTypes.Type.TokenMaster,
permissionState: PermissionTypes.State.Approved,
isPrivate: false,
tokenCriteriaMet: false
},
{
id: "member1",
holdingsListModel: root.createHoldingsModel2(),
@ -276,6 +292,14 @@ QtObject {
isPrivate: false,
tokenCriteriaMet: false
},
{
id: "tmaster1",
holdingsListModel: root.createHoldingsModel2(),
permissionType: PermissionTypes.Type.TokenMaster,
permissionState: PermissionTypes.State.Approved,
isPrivate: false,
tokenCriteriaMet: false
},
{
id: "member1",
holdingsListModel: root.createHoldingsModel1(),

View File

@ -5,6 +5,10 @@
class QAbstractItemModel;
namespace PermissionTypes {
enum Type { NoPermissions = -1, None = 0, Admin, Member, Read, ViewAndPost, TokenMaster, Owner };
}
class PermissionUtilsInternal : public QObject
{
Q_OBJECT
@ -18,4 +22,12 @@ public:
//!< traverse the permissions @p model, and look for unique channels recursively under channelsListModel->key; filtering out @p permissionTypes ([PermissionTypes.Type.FOO])
//! @return an array of `array<key,channelName>`, sorted by `channelName`
Q_INVOKABLE QJsonArray getUniquePermissionChannels(QAbstractItemModel *model, const QList<int> &permissionTypes = {}) const;
//!< Check whether the user can join the community and under which (highest possible) role
//!< @return either:
//! - `NoPermissions` if the permissionsModel is empty or malformed
//! - `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
//! - `None` if no permission to join is satisfied (user can't join at all)
Q_INVOKABLE int /*PermissionTypes::Type*/ isEligibleToJoinAs(QAbstractItemModel *permissionsModel) const;
};

View File

@ -112,6 +112,7 @@
<file>assets/img/icons/tiny/tiny-checkmark.svg</file>
<file>assets/img/icons/tiny/tiny-contact.svg</file>
<file>assets/img/icons/tiny/tiny-exclamation.svg</file>
<file>assets/img/icons/tiny/token-master.svg</file>
<file>assets/img/icons/tiny/tribute-to-talk.svg</file>
<file>assets/img/icons/tiny/unlocked.svg</file>
<file>assets/img/icons/tiny/warning.svg</file>

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.5 6.51587C13.5 6.85724 13.2457 7.4564 12.2033 8.05443C11.2022 8.62875 9.71934 9.03175 8 9.03175C6.28066 9.03175 4.79778 8.62875 3.79668 8.05443C2.75427 7.4564 2.5 6.85724 2.5 6.51587C2.5 6.1745 2.75427 5.57534 3.79668 4.97731C4.79778 4.40299 6.28066 4 8 4C9.71934 4 11.2022 4.40299 12.2033 4.97731C13.2457 5.57534 13.5 6.1745 13.5 6.51587ZM13.5 9.48413C13.5 9.26091 13.2309 9.18924 13.0391 9.30335C11.7659 10.0607 9.97884 10.5317 8 10.5317C6.02116 10.5317 4.2341 10.0607 2.96094 9.30335C2.7691 9.18924 2.5 9.26091 2.5 9.48413C2.5 9.8255 2.75427 10.4247 3.79668 11.0227C4.79778 11.597 6.28066 12 8 12C9.71934 12 11.2022 11.597 12.2033 11.0227C13.2457 10.4247 13.5 9.8255 13.5 9.48413ZM15 6.51587V9.48413V9.49997C15 9.49999 15 9.5 15 9.5C15 9.5 14.9999 9.50001 14.9999 9.50003C14.985 11.7106 11.8568 13.5 8 13.5C4.14325 13.5 1.01499 11.7106 1.00005 9.50003C1.00005 9.50001 1.00004 9.5 1.00003 9.5C1.00001 9.5 1 9.49999 1 9.49997V9.48413V6.51587V6.50003C1 6.50001 1.00001 6.5 1.00003 6.5C1.00004 6.5 1.00005 6.49999 1.00005 6.49997C1.01499 4.28938 4.14325 2.5 8 2.5C11.8568 2.5 14.985 4.28938 14.9999 6.49997C14.9999 6.49999 15 6.5 15 6.5C15 6.5 15 6.50001 15 6.50003V6.51587Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -17,6 +17,10 @@ int roleByName(QAbstractItemModel* model, const QString &roleName)
}
}
Q_DECL_CONST_FUNCTION Q_DECL_CONSTEXPR inline uint qHash(PermissionTypes::Type key, uint seed = 0) noexcept {
return qHash(static_cast<int>(key), seed);
}
PermissionUtilsInternal::PermissionUtilsInternal(QObject* parent)
: QObject(parent)
{
@ -124,3 +128,55 @@ QJsonArray PermissionUtilsInternal::getUniquePermissionChannels(QAbstractItemMod
return result;
}
int /*PermissionTypes::Type*/ PermissionUtilsInternal::isEligibleToJoinAs(QAbstractItemModel *permissionsModel) const
{
if (!permissionsModel)
return PermissionTypes::Type::NoPermissions;
const auto permissionTypeRole = roleByName(permissionsModel, QStringLiteral("permissionType"));
if (permissionTypeRole == -1) {
qWarning() << Q_FUNC_INFO << "Requested roleName 'permissionType' not found; no permissions at all";
return PermissionTypes::Type::NoPermissions;
}
const auto tokenCriteriaMetRole = roleByName(permissionsModel, QStringLiteral("tokenCriteriaMet"));
if (tokenCriteriaMetRole == -1) {
qWarning() << Q_FUNC_INFO << "Requested roleName 'tokenCriteriaMet' not found; no permissions at all";
return PermissionTypes::Type::NoPermissions;
}
QSet<PermissionTypes::Type> tmpRes;
bool hasAnyJoinPermission{false};
constexpr auto isJoinTypePermission = [](PermissionTypes::Type type) {
return type == PermissionTypes::Type::TokenMaster ||
type == PermissionTypes::Type::Admin ||
type == PermissionTypes::Type::Member;
};
const auto permissionsCount = permissionsModel->rowCount();
for (int i = 0; i < permissionsCount; i++) {
const auto permissionType = static_cast<PermissionTypes::Type>(permissionsModel->data(permissionsModel->index(i, 0), permissionTypeRole).toInt());
if (isJoinTypePermission(permissionType)) {
hasAnyJoinPermission = true;
const auto tokenCriteriaMet = permissionsModel->data(permissionsModel->index(i, 0), tokenCriteriaMetRole).toBool();
if (tokenCriteriaMet) {
tmpRes.insert(permissionType);
}
}
}
if (!hasAnyJoinPermission)
return PermissionTypes::Type::Member;
if (tmpRes.contains(PermissionTypes::Type::TokenMaster))
return PermissionTypes::Type::TokenMaster;
if (tmpRes.contains(PermissionTypes::Type::Admin))
return PermissionTypes::Type::Admin;
if (tmpRes.contains(PermissionTypes::Type::Member))
return PermissionTypes::Type::Member;
return PermissionTypes::Type::None;
}

View File

@ -60,6 +60,8 @@ StackLayout {
Loader {
id: mainViewLoader
readonly property var sectionItem: root.rootStore.chatCommunitySectionModule
readonly property int accessType: sectionItem.requiresTokenPermissionToJoin ? Constants.communityChatOnRequestAccess
: Constants.communityChatPublicAccess
sourceComponent: {
if (sectionItem.isCommunity() && !sectionItem.amIMember) {
@ -97,7 +99,7 @@ StackLayout {
color: communityData.color
image: communityData.image
membersCount: communityData.members.count
accessType: communityData.access
accessType: mainViewLoader.accessType
joinCommunity: true
amISectionAdmin: communityData.memberRole === Constants.memberRole.owner ||
communityData.memberRole === Constants.memberRole.admin ||
@ -118,24 +120,13 @@ StackLayout {
onNotificationButtonClicked: Global.openActivityCenterPopup()
onAdHocChatButtonClicked: rootStore.openCloseCreateChatView()
onRequestToJoinClicked: {
Global.openPopup(communityMembershipSetupDialogComponent, {
communityId: joinCommunityView.communityId,
isInvitationPending: joinCommunityView.isInvitationPending,
communityName: communityData.name,
introMessage: communityData.introMessage,
communityIcon: communityData.image,
accessType: communityData.access
})
Global.communityIntroPopupRequested(joinCommunityView.communityId, communityData.name, communityData.introMessage,
communityData.image, root.rootStore.isMyCommunityRequestPending(communityId))
}
onInvitationPendingClicked: {
Global.openPopup(communityMembershipSetupDialogComponent, {
communityId: joinCommunityView.communityId,
isInvitationPending: joinCommunityView.isInvitationPending,
communityName: communityData.name,
introMessage: communityData.introMessage,
communityIcon: communityData.image,
accessType: communityData.access
})
Global.communityIntroPopupRequested(joinCommunityView.communityId, communityData.name, communityData.introMessage,
communityData.image, root.rootStore.isMyCommunityRequestPending(communityId))
joinCommunityView.isInvitationPending = root.rootStore.isMyCommunityRequestPending(communityId)
}
Connections {
@ -192,24 +183,13 @@ StackLayout {
root.openAppSearch()
}
onRequestToJoinClicked: {
Global.openPopup(communityMembershipSetupDialogComponent, {
communityId: chatView.communityId,
isInvitationPending: root.rootStore.isMyCommunityRequestPending(chatView.communityId),
communityName: root.sectionItemModel.name,
introMessage: root.sectionItemModel.introMessage,
communityIcon: root.sectionItemModel.image,
accessType: root.sectionItemModel.access
})
Global.communityIntroPopupRequested(communityId, root.sectionItemModel.name, root.sectionItemModel.introMessage,
root.sectionItemModel.image, root.rootStore.isMyCommunityRequestPending(chatView.communityId))
}
onInvitationPendingClicked: {
Global.openPopup(communityMembershipSetupDialogComponent, {
communityId: chatView.communityId,
isInvitationPending: root.rootStore.isMyCommunityRequestPending(chatView.communityId),
communityName: root.sectionItemModel.name,
introMessage: root.sectionItemModel.introMessage,
communityIcon: root.sectionItemModel.image,
accessType: root.sectionItemModel.access
})
Global.communityIntroPopupRequested(communityId, root.sectionItemModel.name, root.sectionItemModel.introMessage,
root.sectionItemModel.image, root.rootStore.isMyCommunityRequestPending(chatView.communityId))
chatView.isInvitationPending = root.rootStore.isMyCommunityRequestPending(dialogRoot.communityId)
}
}
}
@ -276,85 +256,6 @@ StackLayout {
}
}
Component {
id: communityMembershipSetupDialogComponent
CommunityMembershipSetupDialog {
id: dialogRoot
property string communityId
walletAccountsModel: WalletStore.RootStore.nonWatchAccounts
canProfileProveOwnershipOfProvidedAddressesFn: WalletStore.RootStore.canProfileProveOwnershipOfProvidedAddresses
walletAssetsModel: walletAssetsStore.groupedAccountAssetsModel
requirementsCheckPending: root.rootStore.requirementsCheckPending
permissionsModel: {
root.rootStore.prepareTokenModelForCommunity(dialogRoot.communityId)
return root.rootStore.permissionsModel
}
assetsModel: root.rootStore.assetsModel
collectiblesModel: root.rootStore.collectiblesModel
getCurrencyAmount: function (balance, symbol){
return currencyStore.getCurrencyAmount(balance, symbol)
}
onPrepareForSigning: {
root.rootStore.prepareKeypairsForSigning(dialogRoot.communityId, dialogRoot.name, sharedAddresses, airdropAddress)
dialogRoot.keypairSigningModel = root.rootStore.communitiesModuleInst.keypairsSigningModel
}
onSignProfileKeypairAndAllNonKeycardKeypairs: {
root.rootStore.signProfileKeypairAndAllNonKeycardKeypairs()
}
onSignSharedAddressesForKeypair: {
root.rootStore.signSharedAddressesForKeypair(keyUid)
}
onJoinCommunity: {
root.rootStore.joinCommunityOrEditSharedAddresses()
}
onCancelMembershipRequest: {
root.rootStore.cancelPendingRequest(dialogRoot.communityId)
mainViewLoader.item.isInvitationPending = root.rootStore.isMyCommunityRequestPending(dialogRoot.communityId)
}
onSharedAddressesUpdated: {
root.rootStore.updatePermissionsModel(dialogRoot.communityId, sharedAddresses)
}
onClosed: {
root.rootStore.cleanJoinEditCommunityData()
}
Connections {
target: root.rootStore.communitiesModuleInst
function onAllSharedAddressesSigned() {
if (dialogRoot.profileProvesOwnershipOfSelectedAddresses) {
dialogRoot.joinCommunity()
dialogRoot.close()
return
}
if (dialogRoot.allAddressesToRevealBelongToSingleNonProfileKeypair) {
dialogRoot.joinCommunity()
dialogRoot.close()
return
}
if (!!dialogRoot.replaceItem) {
dialogRoot.replaceLoader.item.allSigned()
}
}
}
}
}
Connections {
target: root.rootStore
enabled: mainViewLoader.item

View File

@ -65,7 +65,7 @@ StatusSectionLayout {
property var collectiblesModel
readonly property var pendingViewOnlyPermissionsModel: SortFilterProxyModel {
sourceModel: root.viewOnlyPermissionsModel
sourceModel: root.viewOnlyPermissionsModel
filters: [
ValueFilter {
roleName: "permissionState"
@ -75,7 +75,7 @@ StatusSectionLayout {
]
}
readonly property var pendingViewAndPostPermissionsModel: SortFilterProxyModel {
sourceModel: root.viewAndPostPermissionsModel
sourceModel: root.viewAndPostPermissionsModel
filters: [
ValueFilter {
roleName: "permissionState"

View File

@ -0,0 +1,50 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import AppLayouts.Communities.helpers 1.0
import utils 1.0
Rectangle {
id: root
required property int /*PermissionTypes.Type*/ eligibleToJoinAs
implicitWidth: hintRow.implicitWidth + 2*Style.current.padding
implicitHeight: 40
color: Theme.palette.baseColor2
radius: height/2
border.width: 1
border.color: Theme.palette.indirectColor4
QtObject {
id: d
readonly property var joinHint: PermissionTypes.getJoinEligibilityHint(root.eligibleToJoinAs)
}
RowLayout {
id: hintRow
spacing: 4
anchors.centerIn: parent
StatusBaseText {
text: d.joinHint[0]
}
StatusIcon {
Layout.preferredWidth: 16
Layout.preferredHeight: 16
Layout.leftMargin: 2
visible: !!icon
icon: d.joinHint[2]
color: Theme.palette.directColor1
}
StatusBaseText {
text: d.joinHint[1]
visible: !!text
font.weight: Font.Medium
}
}
}

View File

@ -32,6 +32,21 @@ QtObject {
return ""
}
function getJoinEligibilityHint(type) {
const template = qsTr("You are eligible to join as")
switch (type) {
case PermissionTypes.Type.TokenMaster:
return [template, qsTr("TokenMaster"), "tiny/token-master"]
case PermissionTypes.Type.Admin:
return [template, qsTr("Admin"), "tiny/public-chat"]
case PermissionTypes.Type.Member:
return [template, qsTr("Member"), "tiny/group"]
case PermissionTypes.Type.None:
return [qsTr("Not eligible to join with current address selections"), "", ""]
}
return ["", "", ""]
}
function getIcon(type) {
switch (type) {
case PermissionTypes.Type.Admin:

View File

@ -7,6 +7,7 @@ BannerPicker 1.0 BannerPicker.qml
CategoryListItem 1.0 CategoryListItem.qml
ColorPicker 1.0 ColorPicker.qml
CommunityListItem 1.0 CommunityListItem.qml
CommunityEligibilityTag 1.0 CommunityEligibilityTag.qml
DescriptionInput 1.0 DescriptionInput.qml
EditCommunitySettingsForm 1.0 EditCommunitySettingsForm.qml
EnsPanel 1.0 EnsPanel.qml

View File

@ -78,8 +78,8 @@ QtObject {
return Internal.PermissionUtils.getUniquePermissionChannels(model, permissionsTypesArray)
}
function getUniqueChannelPermission(model, channelKey) {
return Internal.PermissionUtils.getUniquePermissionChannels(model, channelKey)
function isEligibleToJoinAs(model) {
return Internal.PermissionUtils.isEligibleToJoinAs(model)
}
function setHoldingsTextFormat(type, name, amount, decimals) {

View File

@ -47,7 +47,7 @@ Control {
readonly property string communityMembershipRequestPendingText: qsTr("Membership Request Pending...")
readonly property string channelRequirementsNotMetText: qsTr("Channel requirements not met")
readonly property string channelMembershipRequestPendingText: qsTr("Channel Membership Request Pending...")
readonly property string memberchipRequestRejectedText: qsTr("Membership Request Rejected")
readonly property string membershipRequestRejectedText: qsTr("Membership Request Rejected")
readonly property string allChannelsAreHiddenBecauseNotPermittedText: qsTr("Sorry, you don't hodl the necessary tokens to view or post in any of <b>%1</b> channels").arg(root.communityName)
readonly property bool onlyPrivateNotMetPermissions: (d.visiblePermissionsModel.count === 0) && root.communityHoldingsModel.count > 0
@ -160,12 +160,11 @@ Control {
&& root.requiresRequest
&& !d.onlyPrivateNotMetPermissions
&& !root.allChannelsAreHiddenBecauseNotPermitted
text: root.isInvitationPending ? (root.joinCommunity ? d.communityMembershipRequestPendingText : d.channelMembershipRequestPendingText)
: d.communityRequestToJoinText
font.pixelSize: 13
enabled: root.requirementsMet || (root.joinCommunity && d.visiblePermissionsModel.count === 0)
text: root.isInvitationPending ? (root.joinCommunity ? d.communityMembershipRequestPendingText : d.channelMembershipRequestPendingText)
: d.communityRequestToJoinText
font.pixelSize: 13
onClicked: root.isInvitationPending ? root.invitationPendingClicked() : root.requestToJoinClicked()
onClicked: root.isInvitationPending ? root.invitationPendingClicked() : root.requestToJoinClicked()
}
StatusBaseText {
@ -175,8 +174,8 @@ Control {
&& (root.isJoinRequestRejected || !root.requirementsMet)
&& !d.onlyPrivateNotMetPermissions
&& !root.allChannelsAreHiddenBecauseNotPermitted
text: root.isJoinRequestRejected ? d.memberchipRequestRejectedText :
(root.joinCommunity ? d.communityRequirementsNotMetText : d.channelRequirementsNotMetText)
text: root.isJoinRequestRejected ? d.membershipRequestRejectedText
: (root.joinCommunity ? d.communityRequirementsNotMetText : d.channelRequirementsNotMetText)
color: Theme.palette.dangerColor1
}

View File

@ -66,7 +66,6 @@ StatusListView {
statusListItemTitle.font.weight: Font.Medium
title: model.name
tertiaryTitle: !walletAccountAssetsModel.count && root.hasPermissions ? qsTr("No relevant tokens") : ""
property string accountAddress: model.address
SubmodelProxyModel {
id: filteredBalances
@ -75,7 +74,7 @@ StatusListView {
delegateModel: SortFilterProxyModel {
sourceModel: submodel
filters: FastExpressionFilter {
expression: listItem.accountAddress === model.account
expression: listItem.address === model.account.toLowerCase()
expectedRoles: ["account"]
}
}

View File

@ -31,6 +31,7 @@ Control {
required property int totalNumOfAddressesForSharing
required property bool profileProvesOwnershipOfSelectedAddresses
required property bool allAddressesToRevealBelongToSingleNonProfileKeypair
required property int /*PermissionTypes.Type*/ eligibleToJoinAs
property bool requirementsCheckPending: false
@ -135,6 +136,7 @@ Control {
readonly property var shareAddressesButton: StatusButton {
visible: !root.isEditMode
enabled: root.eligibleToJoinAs !== PermissionTypes.Type.None
text: {
if (d.selectedSharedAddressesCount === root.totalNumOfAddressesForSharing) {
return qsTr("Share all addresses to join")
@ -177,7 +179,8 @@ Control {
text: d.lostCommunityPermission ? qsTr("Selected addresses have insufficient tokens to maintain %1 membership").arg(root.communityName) :
d.lostChannelPermissions ? qsTr("By deselecting these addresses, you will lose channel permissions") :
""
visible: d.lostCommunityPermission || d.lostChannelPermissions
active: d.lostCommunityPermission || d.lostChannelPermissions
visible: active
closeBtnVisible: false
}
@ -202,7 +205,7 @@ Control {
root.airdropAddressSelected(address)
}
getCurrencyAmount: function (balance, symbol){
getCurrencyAmount: function (balance, symbol) {
return root.getCurrencyAmount(balance, symbol)
}
}
@ -211,7 +214,7 @@ Control {
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: Style.current.padding * 2
color: Theme.palette.baseColor2
color: permissionsView.color
radius: Style.current.padding
border.width: 1
border.color: Theme.palette.baseColor3
@ -237,6 +240,7 @@ Control {
requirementsCheckPending: root.requirementsCheckPending
communityName: root.communityName
communityIcon: root.communityIcon
eligibleToJoinAs: root.eligibleToJoinAs
Layout.fillHeight: true
Layout.fillWidth: true

View File

@ -18,6 +18,8 @@ import utils 1.0
Rectangle {
id: root
required property int /*PermissionTypes.Type*/ eligibleToJoinAs
property bool isEditMode
property string communityName
@ -33,7 +35,7 @@ Rectangle {
readonly property bool lostChannelPermissions: d.lostChannelPermissions
implicitHeight: permissionsScrollView.contentHeight - permissionsScrollView.anchors.topMargin
color: Theme.palette.baseColor2
color: Theme.palette.baseColor4
readonly property bool hasAnyVisiblePermission: root.permissionsModel && root.permissionsModel.count &&
(d.tokenMasterPermissionsModel.count > 0 || d.adminPermissionsModel.count > 0 ||
@ -124,6 +126,8 @@ Rectangle {
id: permissionsScrollView
anchors.fill: parent
anchors.topMargin: -Style.current.padding
bottomPadding: eligibilityHintBubble.visible ? eligibilityHintBubble.height + eligibilityHintBubble.anchors.bottomMargin*2
: 16
contentWidth: availableWidth
ColumnLayout {
@ -163,11 +167,6 @@ Rectangle {
}
// permission types
PermissionPanel {
id: tokenMasterPermissionPanel
permissionType: PermissionTypes.Type.TokenMaster
permissionsModel: d.tokenMasterPermissionsModel
}
PermissionPanel {
id: joinPermissionPanel
permissionType: PermissionTypes.Type.Member
@ -177,6 +176,11 @@ Rectangle {
permissionType: PermissionTypes.Type.Admin
permissionsModel: d.adminPermissionsModel
}
PermissionPanel {
id: tokenMasterPermissionPanel
permissionType: PermissionTypes.Type.TokenMaster
permissionsModel: d.tokenMasterPermissionsModel
}
Repeater { // channel repeater
id: channelPermissionsPanel
@ -194,6 +198,15 @@ Rectangle {
}
}
CommunityEligibilityTag {
id: eligibilityHintBubble
eligibleToJoinAs: root.eligibleToJoinAs
visible: !root.isEditMode
anchors.bottom: parent.bottom
anchors.bottomMargin: 24
anchors.horizontalCenter: parent.horizontalCenter
}
component PanelBg: Rectangle {
color: Theme.palette.statusListItem.backgroundColor
border.width: 1
@ -207,6 +220,8 @@ Rectangle {
Layout.alignment: Qt.AlignTop
asset.name: {
switch (permissionType) {
case PermissionTypes.Type.TokenMaster:
return "arbitrator"
case PermissionTypes.Type.Admin:
return "admin"
case PermissionTypes.Type.Member:
@ -252,6 +267,7 @@ Rectangle {
width: 16
height: 16
image.source: model.imageSource
visible: !isError
}
StatusBaseText {
anchors.verticalCenter: parent.verticalCenter
@ -352,6 +368,7 @@ Rectangle {
}
Row {
Layout.alignment: Qt.AlignCenter
spacing: 4
StatusIcon {
anchors.verticalCenter: parent.verticalCenter
width: 16
@ -525,6 +542,7 @@ Rectangle {
}
Row {
Layout.alignment: Qt.AlignCenter
spacing: 4
StatusIcon {
anchors.verticalCenter: parent.verticalCenter
width: 16

View File

@ -502,21 +502,22 @@ Item {
}
onClicked: {
Global.openPopup(communityMembershipSetupDialogComponent);
Global.communityIntroPopupRequested(communityData.id, communityData.name, communityData.introMessage,
communityData.image, d.invitationPending)
}
Connections {
enabled: d.joiningCommunityInProgress
target: root.store.communitiesModuleInst
function onCommunityAccessRequested(communityId: string) {
if (communityId === communityData.id) {
d.invitationPending = root.store.isMyCommunityRequestPending(communityData.id)
if (communityId === root.communityData.id) {
d.invitationPending = root.store.isMyCommunityRequestPending(communityId)
d.joiningCommunityInProgress = false
}
}
function onCommunityAccessFailed(communityId: string, error: string) {
if (communityId === communityData.id) {
if (communityId === root.communityData.id) {
d.invitationPending = false
d.joiningCommunityInProgress = false
Global.displayToastMessage(qsTr("Request to join failed"),
@ -528,90 +529,6 @@ Item {
}
}
}
Component {
id: communityMembershipSetupDialogComponent
CommunityMembershipSetupDialog {
id: dialogRoot
isInvitationPending: d.invitationPending
requirementsCheckPending: root.store.requirementsCheckPending
communityName: communityData.name
introMessage: communityData.introMessage
communityIcon: communityData.image
accessType: communityData.access
walletAccountsModel: WalletStore.RootStore.nonWatchAccounts
canProfileProveOwnershipOfProvidedAddressesFn: WalletStore.RootStore.canProfileProveOwnershipOfProvidedAddresses
walletAssetsModel: walletAssetsStore.groupedAccountAssetsModel
permissionsModel: {
root.store.prepareTokenModelForCommunity(communityData.id)
return root.store.permissionsModel
}
assetsModel: root.store.assetsModel
collectiblesModel: root.store.collectiblesModel
getCurrencyAmount: function (balance, symbol){
return currencyStore.getCurrencyAmount(balance, symbol)
}
onPrepareForSigning: {
root.store.prepareKeypairsForSigning(communityData.id, root.store.userProfileInst.name, sharedAddresses, airdropAddress, false)
dialogRoot.keypairSigningModel = root.store.communitiesModuleInst.keypairsSigningModel
}
onSignProfileKeypairAndAllNonKeycardKeypairs: {
root.store.signProfileKeypairAndAllNonKeycardKeypairs()
}
onSignSharedAddressesForKeypair: {
root.store.signSharedAddressesForKeypair(keyUid)
}
onJoinCommunity: {
d.joiningCommunityInProgress = true
root.store.joinCommunityOrEditSharedAddresses()
}
onCancelMembershipRequest: {
root.store.cancelPendingRequest(communityData.id)
d.invitationPending = root.store.isMyCommunityRequestPending(communityData.id)
}
onSharedAddressesUpdated: {
root.store.updatePermissionsModel(communityData.id, sharedAddresses)
}
onClosed: {
root.store.cleanJoinEditCommunityData()
}
Connections {
target: root.store.communitiesModuleInst
function onAllSharedAddressesSigned() {
if (dialogRoot.profileProvesOwnershipOfSelectedAddresses) {
dialogRoot.joinCommunity()
dialogRoot.close()
return
}
if (dialogRoot.allAddressesToRevealBelongToSingleNonProfileKeypair) {
dialogRoot.joinCommunity()
dialogRoot.close()
return
}
if (!!dialogRoot.replaceItem) {
dialogRoot.replaceLoader.item.allSigned()
}
}
}
}
}
}
}

View File

@ -52,7 +52,7 @@ StatusListView {
readonly property bool isOwner: model.memberRole === Constants.memberRole.owner
readonly property bool isAdmin: model.memberRole === Constants.memberRole.admin
readonly property bool isTokenMaster: model.memberRole === Constants.memberRole.tokenMaster
readonly property bool isInvitationPending: root.rootStore.isMyCommunityRequestPending(model.id)
property bool isInvitationPending: root.rootStore.isMyCommunityRequestPending(model.id)
components: [
StatusFlatButton {
@ -153,9 +153,10 @@ StatusListView {
enabled: !listItem.isOwner
onTriggered: {
moreMenu.close()
if (listItem.isInvitationPending)
if (listItem.isInvitationPending) {
root.cancelMembershipRequest(model.id)
else if (listItem.isSpectator)
listItem.isInvitationPending = root.rootStore.isMyCommunityRequestPending(model.id)
} else if (listItem.isSpectator)
root.closeCommunityClicked(model.id)
else
root.leaveCommunityClicked(model.name, model.id, model.outroMessage)

View File

@ -208,100 +208,10 @@ SettingsContentBase {
null)
}
onShowCommunityMembershipSetupDialog: {
Global.openPopup(communityMembershipSetupDialogComponent, {
communityId: communityId,
isInvitationPending: root.rootStore.isMyCommunityRequestPending(communityId),
communityName: name,
introMessage: introMessage,
communityIcon: imageSrc,
accessType: accessType
})
Global.communityIntroPopupRequested(communityId, name, introMessage, imageSrc, root.rootStore.isMyCommunityRequestPending(communityId))
}
onCancelMembershipRequest: {
root.rootStore.cancelPendingRequest(communityId)
}
}
readonly property var communityMembershipSetupDialogComponent: Component {
id: communityMembershipSetupDialogComponent
CommunityMembershipSetupDialog {
id: dialogRoot
property string communityId
readonly property var chatStore: ChatStore.RootStore {
chatCommunitySectionModule: {
root.rootStore.mainModuleInst.prepareCommunitySectionModuleForCommunityId(dialogRoot.communityId)
return root.rootStore.mainModuleInst.getCommunitySectionModule()
}
}
walletAccountsModel: WalletStore.RootStore.nonWatchAccounts
canProfileProveOwnershipOfProvidedAddressesFn: WalletStore.RootStore.canProfileProveOwnershipOfProvidedAddresses
walletAssetsModel: walletAssetsStore.groupedAccountAssetsModel
requirementsCheckPending: root.rootStore.requirementsCheckPending
permissionsModel: {
root.rootStore.prepareTokenModelForCommunity(dialogRoot.communityId)
return root.rootStore.permissionsModel
}
assetsModel: chatStore.assetsModel
collectiblesModel: chatStore.collectiblesModel
getCurrencyAmount: function (balance, symbol){
return currencyStore.getCurrencyAmount(balance, symbol)
}
onPrepareForSigning: {
chatStore.prepareKeypairsForSigning(dialogRoot.communityId, root.rootStore.userProfileInst.name, sharedAddresses, airdropAddress, false)
dialogRoot.keypairSigningModel = chatStore.communitiesModuleInst.keypairsSigningModel
}
onSignProfileKeypairAndAllNonKeycardKeypairs: {
chatStore.signProfileKeypairAndAllNonKeycardKeypairs()
}
onSignSharedAddressesForKeypair: {
chatStore.signSharedAddressesForKeypair(keyUid)
}
onJoinCommunity: {
chatStore.joinCommunityOrEditSharedAddresses()
}
onCancelMembershipRequest: root.rootStore.cancelPendingRequest(dialogRoot.communityId)
onSharedAddressesUpdated: {
root.rootStore.updatePermissionsModel(dialogRoot.communityId, sharedAddresses)
}
onClosed: {
chatStore.cleanJoinEditCommunityData()
}
Connections {
target: chatStore.communitiesModuleInst
function onAllSharedAddressesSigned() {
if (dialogRoot.profileProvesOwnershipOfSelectedAddresses) {
dialogRoot.joinCommunity()
dialogRoot.close()
return
}
if (dialogRoot.allAddressesToRevealBelongToSingleNonProfileKeypair) {
dialogRoot.joinCommunity()
dialogRoot.close()
return
}
if (!!dialogRoot.replaceItem) {
dialogRoot.replaceLoader.item.allSigned()
}
}
}
}
}
}

View File

@ -260,13 +260,12 @@ QtObject {
}
function openCommunityIntroPopup(communityId, name, introMessage,
imageSrc, accessType, isInvitationPending) {
imageSrc, isInvitationPending) {
openPopup(communityJoinDialogPopup,
{communityId: communityId,
communityName: name,
introMessage: introMessage,
communityIcon: imageSrc,
accessType: accessType,
isInvitationPending: isInvitationPending
})
}
@ -278,7 +277,6 @@ QtObject {
communityName: name,
introMessage: qsTr("Share addresses to rejoin %1").arg(name),
communityIcon: imageSrc,
accessType: Constants.communityChatOnRequestAccess,
isInvitationPending: false
})
}
@ -710,7 +708,7 @@ QtObject {
assetsModel: root.rootStore.assetsModel
collectiblesModel: root.rootStore.collectiblesModel
getCurrencyAmount: function (balance, symbol){
getCurrencyAmount: function (balance, symbol) {
return currencyStore.getCurrencyAmount(balance, symbol)
}
@ -732,7 +730,10 @@ QtObject {
root.rootStore.joinCommunityOrEditSharedAddresses()
}
onCancelMembershipRequest: root.rootStore.cancelPendingRequest(dialogRoot.communityId)
onCancelMembershipRequest: {
root.rootStore.cancelPendingRequest(dialogRoot.communityId)
}
Connections {
target: root.communitiesStore.communitiesModuleInst
function onCommunityAccessRequested(communityId: string) {
@ -944,6 +945,8 @@ QtObject {
communityIcon: chatStore.sectionDetails.image
requirementsCheckPending: root.rootStore.requirementsCheckPending
introMessage: chatStore.sectionDetails.introMessage
canProfileProveOwnershipOfProvidedAddressesFn: WalletStore.RootStore.canProfileProveOwnershipOfProvidedAddresses
walletAccountsModel: root.rootStore.walletAccountsModel

View File

@ -4,6 +4,7 @@ import QtQuick.Layouts 1.15
import utils 1.0
import StatusQ 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
@ -12,6 +13,8 @@ import StatusQ.Popups 0.1
import StatusQ.Core.Utils 0.1
import AppLayouts.Communities.panels 1.0
import AppLayouts.Communities.controls 1.0
import AppLayouts.Communities.helpers 1.0
import SortFilterProxyModel 0.2
@ -26,7 +29,6 @@ StatusStackModal {
required property bool requirementsCheckPending
property string introMessage
property int accessType
property bool isInvitationPending: false
@ -71,13 +73,63 @@ StatusStackModal {
width: 640 // by design
padding: 0
stackTitle: root.accessType === Constants.communityChatOnRequestAccess ?
stackTitle: d.accessType === Constants.communityChatOnRequestAccess ?
qsTr("Request to join %1").arg(root.communityName)
: qsTr("Welcome to %1").arg(root.communityName)
rightButtons: [d.shareButton, finishButton]
finishButton: root.isInvitationPending ? d.cancelRequestButton : d.shareAddressesButton
finishButton: StatusButton {
enabled: {
if (root.isInvitationPending || d.accessType !== Constants.communityChatOnRequestAccess)
return true
return d.eligibleToJoinAs !== PermissionTypes.Type.None
}
text: {
if (root.isInvitationPending) {
return qsTr("Cancel Membership Request")
}
if (d.selectedSharedAddressesCount === d.totalNumOfAddressesForSharing) {
return qsTr("Share all addresses to join")
}
return qsTr("Share %n address(s) to join", "", d.selectedSharedAddressesCount)
}
type: root.isInvitationPending ? StatusBaseButton.Type.Danger
: StatusBaseButton.Type.Normal
icon.name: {
if (root.isInvitationPending)
return ""
if (root.profileProvesOwnershipOfSelectedAddresses) {
if (userProfile.usingBiometricLogin) {
return "touch-id"
}
if (userProfile.isKeycardUser) {
return "keycard"
}
return "password"
}
if (root.allAddressesToRevealBelongToSingleNonProfileKeypair) {
return "keycard"
}
return ""
}
onClicked: {
if (root.isInvitationPending) {
root.cancelMembershipRequest()
root.close()
return
}
d.proceedToSigningOrSubmitRequest(d.communityIntroUid)
}
}
backButton: StatusBackButton {
visible: !!root.replaceLoader.item
@ -117,17 +169,29 @@ StatusStackModal {
readonly property int selectedSharedAddressesCount: d.selectedSharedAddressesMap.size
readonly property int accessType: d.eligibleToJoinAs !== - 1 ? Constants.communityChatOnRequestAccess
: Constants.communityChatPublicAccess
property int eligibleToJoinAs: PermissionsHelpers.isEligibleToJoinAs(root.permissionsModel)
readonly property var _con: Connections {
target: root.permissionsModel
function onTokenCriteriaUpdated() {
d.eligibleToJoinAs = PermissionsHelpers.isEligibleToJoinAs(root.permissionsModel)
}
}
property var initialAddressesModel: SortFilterProxyModel {
sourceModel: root.walletAccountsModel
sorters: [
ExpressionSorter {
function isGenerated(modelData) {
return modelData.walletType === Constants.generatedWalletType
FastExpressionSorter {
function sortPredicate(lhs, rhs) {
if (lhs.walletType === rhs.walletType) return 0
return lhs.walletType === Constants.generatedWalletType ? -1 : 1
}
expression: {
return isGenerated(modelLeft)
return sortPredicate(modelLeft, modelRight)
}
expectedRoles: ["walletType"]
},
RoleSorter {
roleName: "position"
@ -138,50 +202,6 @@ StatusStackModal {
]
}
readonly property StatusFlatButton cancelRequestButton: StatusFlatButton {
text: qsTr("Cancel Membership Request")
type: StatusBaseButton.Type.Danger
onClicked: {
if (root.isInvitationPending) {
root.cancelMembershipRequest()
root.close()
return
}
}
}
readonly property StatusButton shareAddressesButton: StatusButton {
text: {
if (d.selectedSharedAddressesCount === d.totalNumOfAddressesForSharing) {
return qsTr("Share all addresses to join")
}
return qsTr("Share %n address(s) to join", "", d.selectedSharedAddressesCount)
}
type: StatusBaseButton.Type.Normal
icon.name: {
if (root.profileProvesOwnershipOfSelectedAddresses) {
if (userProfile.usingBiometricLogin) {
return "touch-id"
}
if (userProfile.isKeycardUser) {
return "keycard"
}
return "password"
}
if (root.allAddressesToRevealBelongToSingleNonProfileKeypair) {
return "keycard"
}
return ""
}
onClicked: d.proceedToSigningOrSubmitRequest(d.communityIntroUid)
}
function proceedToSigningOrSubmitRequest(uidOfComponentThisFunctionIsCalledFrom) {
const selected = d.getSelectedAddresses()
root.prepareForSigning(selected.airdropAddress, selected.addresses)
@ -340,6 +360,7 @@ StatusStackModal {
currentSharedAddressesMap: d.currentSharedAddressesMap
totalNumOfAddressesForSharing: d.totalNumOfAddressesForSharing
eligibleToJoinAs: d.eligibleToJoinAs
profileProvesOwnershipOfSelectedAddresses: root.profileProvesOwnershipOfSelectedAddresses
allAddressesToRevealBelongToSingleNonProfileKeypair: root.allAddressesToRevealBelongToSingleNonProfileKeypair
@ -377,7 +398,6 @@ StatusStackModal {
Component {
id: sharedAddressesSigningPanelComponent
SharedAddressesSigningPanel {
componentUid: d.signingPanelUid
isEditMode: root.isEditMode
totalNumOfAddressesForSharing: d.totalNumOfAddressesForSharing
@ -430,6 +450,12 @@ StatusStackModal {
color: Theme.palette.directColor1
wrapMode: Text.WordWrap
}
CommunityEligibilityTag {
Layout.alignment: Qt.AlignHCenter
eligibleToJoinAs: d.eligibleToJoinAs
visible: !root.isEditMode && !root.isInvitationPending && d.accessType === Constants.communityChatOnRequestAccess
}
}
}
]

View File

@ -85,7 +85,7 @@ QtObject {
signal createCommunityPopupRequested(bool isDiscordImport)
signal importCommunityPopupRequested()
signal communityIntroPopupRequested(string communityId, string name, string introMessage,
string imageSrc, int accessType, bool isInvitationPending)
string imageSrc, bool isInvitationPending)
signal communityShareAddressesPopupRequested(string communityId, string name, string imageSrc)
signal leaveCommunityRequested(string community, string communityId, string outroMessage)
signal openEditSharedAddressesFlow(string communityId)