token-permissions: implement the UI "edit" flow for shared addresses
Fixes #11599
This commit is contained in:
parent
f721636452
commit
22258cc363
|
@ -26,13 +26,29 @@ SplitView {
|
|||
loginType: ctrlLoginType.currentIndex
|
||||
walletAccountsModel: WalletAccountsModel {}
|
||||
permissionsModel: {
|
||||
if (ctrlPermissions.checked && ctrlTokenGatedChannels.checked)
|
||||
if (ctrlPermissions.checked && ctrlTokenGatedChannels.checked) {
|
||||
if (selectedSharedAddresses.length === 1 && ctrlEditMode.checked) {
|
||||
console.warn("Simulation: Losing BOTH the join and VIP read channel permissions !!!")
|
||||
return PermissionsModel.complexCombinedPermissionsModelNotMet
|
||||
}
|
||||
return PermissionsModel.complexCombinedPermissionsModel
|
||||
}
|
||||
if (ctrlPermissions.checked) {
|
||||
if (selectedSharedAddresses.length === 1 && ctrlEditMode.checked) {
|
||||
console.warn("Simulation: Losing the join community permission !!!")
|
||||
return PermissionsModel.complexPermissionsModelNotMet
|
||||
}
|
||||
return PermissionsModel.complexPermissionsModel
|
||||
if (ctrlPermissions.checked)
|
||||
return PermissionsModel.permissionsModel
|
||||
if (ctrlTokenGatedChannels.checked)
|
||||
}
|
||||
if (ctrlTokenGatedChannels.checked) {
|
||||
if (selectedSharedAddresses.length === 1 && ctrlEditMode.checked) {
|
||||
console.warn("Simulation: Losing the VIP read channel permission !!!")
|
||||
return PermissionsModel.channelsOnlyPermissionsModelNotMet
|
||||
}
|
||||
return PermissionsModel.channelsOnlyPermissionsModel
|
||||
}
|
||||
|
||||
console.warn("!!! EMPTY MODEL !!!")
|
||||
return emptyModel
|
||||
}
|
||||
|
||||
|
@ -40,7 +56,18 @@ SplitView {
|
|||
collectiblesModel: CollectiblesModel {}
|
||||
visible: true
|
||||
|
||||
Binding on selectedSharedAddresses {
|
||||
value: ["0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8884"]
|
||||
when: ctrlEditMode.checked
|
||||
}
|
||||
|
||||
Binding on selectedAirdropAddress {
|
||||
value: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881"
|
||||
when: ctrlEditMode.checked
|
||||
}
|
||||
|
||||
onShareSelectedAddressesClicked: logs.logEvent("::shareSelectedAddressesClicked", ["airdropAddress", "sharedAddresses"], arguments)
|
||||
onSaveSelectedAddressesClicked: logs.logEvent("::saveSelectedAddressesClicked", ["airdropAddress", "sharedAddresses"], arguments)
|
||||
onClosed: destroy()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,23 @@ QtObject {
|
|||
}
|
||||
]
|
||||
|
||||
readonly property var permissionsModelDataNotMet: [
|
||||
{
|
||||
holdingsListModel: root.createHoldingsModel1(),
|
||||
channelsListModel: root.createChannelsModel1(),
|
||||
permissionType: PermissionTypes.Type.Admin,
|
||||
isPrivate: true,
|
||||
tokenCriteriaMet: false
|
||||
},
|
||||
{
|
||||
holdingsListModel: root.createHoldingsModel1(),
|
||||
channelsListModel: root.createChannelsModel2(),
|
||||
permissionType: PermissionTypes.Type.Member,
|
||||
isPrivate: false,
|
||||
tokenCriteriaMet: false
|
||||
}
|
||||
]
|
||||
|
||||
readonly property var shortPermissionsModelData: [
|
||||
{
|
||||
holdingsListModel: root.createHoldingsModel4(),
|
||||
|
@ -197,6 +214,41 @@ QtObject {
|
|||
}
|
||||
]
|
||||
|
||||
readonly property var complexPermissionsModelDataNotMet: [
|
||||
{
|
||||
id: "admin1",
|
||||
holdingsListModel: root.createHoldingsModel1(),
|
||||
channelsListModel: root.createChannelsModel2(),
|
||||
permissionType: PermissionTypes.Type.Admin,
|
||||
isPrivate: false,
|
||||
tokenCriteriaMet: false
|
||||
},
|
||||
{
|
||||
id: "admin2",
|
||||
holdingsListModel: root.createHoldingsModel3(),
|
||||
channelsListModel: root.createChannelsModel2(),
|
||||
permissionType: PermissionTypes.Type.Admin,
|
||||
isPrivate: false,
|
||||
tokenCriteriaMet: false
|
||||
},
|
||||
{
|
||||
id: "member1",
|
||||
holdingsListModel: root.createHoldingsModel1(),
|
||||
channelsListModel: root.createChannelsModel2(),
|
||||
permissionType: PermissionTypes.Type.Member,
|
||||
isPrivate: false,
|
||||
tokenCriteriaMet: false
|
||||
},
|
||||
{
|
||||
id: "member2",
|
||||
holdingsListModel: root.createHoldingsModel4(),
|
||||
channelsListModel: root.createChannelsModel2(),
|
||||
permissionType: PermissionTypes.Type.Member,
|
||||
isPrivate: false,
|
||||
tokenCriteriaMet: false
|
||||
}
|
||||
]
|
||||
|
||||
readonly property var channelsOnlyPermissionsModelData: [
|
||||
{
|
||||
id: "read1a",
|
||||
|
@ -290,6 +342,89 @@ QtObject {
|
|||
}
|
||||
]
|
||||
|
||||
readonly property var channelsOnlyPermissionsModelDataNotMet: [
|
||||
{
|
||||
id: "read1a",
|
||||
holdingsListModel: root.createHoldingsModel1b(),
|
||||
channelsListModel: root.createChannelsModel1(),
|
||||
permissionType: PermissionTypes.Type.Read,
|
||||
isPrivate: false,
|
||||
tokenCriteriaMet: true
|
||||
},
|
||||
{
|
||||
id: "read1b",
|
||||
holdingsListModel: root.createHoldingsModel1(),
|
||||
channelsListModel: root.createChannelsModel1(),
|
||||
permissionType: PermissionTypes.Type.Read,
|
||||
isPrivate: false,
|
||||
tokenCriteriaMet: false
|
||||
},
|
||||
{
|
||||
id: "read1c",
|
||||
holdingsListModel: root.createHoldingsModel3(),
|
||||
channelsListModel: root.createChannelsModel1(),
|
||||
permissionType: PermissionTypes.Type.Read,
|
||||
isPrivate: false,
|
||||
tokenCriteriaMet: false
|
||||
},
|
||||
{
|
||||
id: "read2a",
|
||||
holdingsListModel: root.createHoldingsModel1(),
|
||||
channelsListModel: root.createChannelsModel3(),
|
||||
permissionType: PermissionTypes.Type.Read,
|
||||
isPrivate: false,
|
||||
tokenCriteriaMet: false
|
||||
},
|
||||
{
|
||||
id: "read2b",
|
||||
holdingsListModel: root.createHoldingsModel3(),
|
||||
channelsListModel: root.createChannelsModel3(),
|
||||
permissionType: PermissionTypes.Type.Read,
|
||||
isPrivate: false,
|
||||
tokenCriteriaMet: false
|
||||
},
|
||||
{
|
||||
id: "viewAndPost1a",
|
||||
holdingsListModel: root.createHoldingsModel3(),
|
||||
channelsListModel: root.createChannelsModel1(),
|
||||
permissionType: PermissionTypes.Type.ViewAndPost,
|
||||
isPrivate: false,
|
||||
tokenCriteriaMet: false
|
||||
},
|
||||
{
|
||||
id: "viewAndPost1b",
|
||||
holdingsListModel: root.createHoldingsModel2b(),
|
||||
channelsListModel: root.createChannelsModel1(),
|
||||
permissionType: PermissionTypes.Type.ViewAndPost,
|
||||
isPrivate: false,
|
||||
tokenCriteriaMet: true
|
||||
},
|
||||
{
|
||||
id: "viewAndPost2a",
|
||||
holdingsListModel: root.createHoldingsModel3(),
|
||||
channelsListModel: root.createChannelsModel3(),
|
||||
permissionType: PermissionTypes.Type.ViewAndPost,
|
||||
isPrivate: false,
|
||||
tokenCriteriaMet: false
|
||||
},
|
||||
{
|
||||
id: "viewAndPost2b",
|
||||
holdingsListModel: root.createHoldingsModel5(),
|
||||
channelsListModel: root.createChannelsModel3(),
|
||||
permissionType: PermissionTypes.Type.ViewAndPost,
|
||||
isPrivate: false,
|
||||
tokenCriteriaMet: false
|
||||
},
|
||||
{
|
||||
id: "viewAndPost2c",
|
||||
holdingsListModel: root.createHoldingsModel1(),
|
||||
channelsListModel: root.createChannelsModel3(),
|
||||
permissionType: PermissionTypes.Type.ViewAndPost,
|
||||
isPrivate: false,
|
||||
tokenCriteriaMet: false
|
||||
}
|
||||
]
|
||||
|
||||
readonly property ListModel permissionsModel: ListModel {
|
||||
readonly property ModelChangeGuard guard: ModelChangeGuard {
|
||||
model: root.permissionsModel
|
||||
|
@ -301,6 +436,17 @@ QtObject {
|
|||
}
|
||||
}
|
||||
|
||||
readonly property ListModel permissionsModelNotMet: ListModel {
|
||||
readonly property ModelChangeGuard guard: ModelChangeGuard {
|
||||
model: root.permissionsModelNotMet
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
append(permissionsModelDataNotMet)
|
||||
guard.enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
readonly property var shortPermissionsModel: ListModel {
|
||||
readonly property ModelChangeGuard guard: ModelChangeGuard {
|
||||
model: root.shortPermissionsModel
|
||||
|
@ -367,6 +513,30 @@ QtObject {
|
|||
}
|
||||
}
|
||||
|
||||
readonly property var complexCombinedPermissionsModel: ListModel {
|
||||
readonly property ModelChangeGuard guard: ModelChangeGuard {
|
||||
model: root.complexCombinedPermissionsModel
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
append(complexPermissionsModelData)
|
||||
append(channelsOnlyPermissionsModelData)
|
||||
guard.enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
readonly property var complexCombinedPermissionsModelNotMet: ListModel {
|
||||
readonly property ModelChangeGuard guard: ModelChangeGuard {
|
||||
model: root.complexCombinedPermissionsModelNotMet
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
append(complexPermissionsModelDataNotMet)
|
||||
append(channelsOnlyPermissionsModelDataNotMet)
|
||||
guard.enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
readonly property var complexPermissionsModel: ListModel {
|
||||
readonly property ModelChangeGuard guard: ModelChangeGuard {
|
||||
model: root.complexPermissionsModel
|
||||
|
@ -374,7 +544,17 @@ QtObject {
|
|||
|
||||
Component.onCompleted: {
|
||||
append(complexPermissionsModelData)
|
||||
append(channelsOnlyPermissionsModelData)
|
||||
guard.enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
readonly property var complexPermissionsModelNotMet: ListModel {
|
||||
readonly property ModelChangeGuard guard: ModelChangeGuard {
|
||||
model: root.complexPermissionsModelNotMet
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
append(complexPermissionsModelDataNotMet)
|
||||
guard.enabled = true
|
||||
}
|
||||
}
|
||||
|
@ -390,6 +570,17 @@ QtObject {
|
|||
}
|
||||
}
|
||||
|
||||
readonly property var channelsOnlyPermissionsModelNotMet: ListModel {
|
||||
readonly property ModelChangeGuard guard: ModelChangeGuard {
|
||||
model: root.channelsOnlyPermissionsModelNotMet
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
append(channelsOnlyPermissionsModelDataNotMet)
|
||||
guard.enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
function createHoldingsModel1() {
|
||||
return [
|
||||
{
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <QJSValue>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
|
@ -28,6 +29,10 @@ public:
|
|||
|
||||
Q_INVOKABLE bool contains(QAbstractItemModel *model, const QString &roleName, const QVariant &value, int mode = Qt::CaseSensitive) const;
|
||||
|
||||
///< performs a strict check whether @lhs and @rhs arrays (QList<T>) contain the same elements;
|
||||
/// eg. `["a", "c", "b"]` and `["b", "c", "a"]` are considered equal
|
||||
Q_INVOKABLE bool isSameArray(const QJSValue& lhs, const QJSValue& rhs) const;
|
||||
|
||||
static QObject* qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine)
|
||||
{
|
||||
Q_UNUSED(engine);
|
||||
|
|
|
@ -15,7 +15,7 @@ public:
|
|||
//!< traverse the permissions @p model, and look for unique token keys recursively under holdingsListModel->key
|
||||
Q_INVOKABLE QStringList getUniquePermissionTokenKeys(QAbstractItemModel *model) const;
|
||||
|
||||
//!< traverse the permissions @p model, and look for unique channels recursively under channelsListModel->key; filtering out @permissionTypes ([PermissionTypes.Type.FOO])
|
||||
//! @return an array of array<key,channelName>
|
||||
//!< 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;
|
||||
};
|
||||
|
|
|
@ -72,3 +72,20 @@ bool ModelUtilsInternal::contains(QAbstractItemModel* model,
|
|||
const auto indexes = model->match(model->index(0, 0), roleByName(model, roleName), value, 1, flags);
|
||||
return !indexes.isEmpty();
|
||||
}
|
||||
|
||||
bool ModelUtilsInternal::isSameArray(const QJSValue& lhs, const QJSValue& rhs) const
|
||||
{
|
||||
if (!lhs.isArray() || !rhs.isArray())
|
||||
return false;
|
||||
|
||||
auto left = lhs.toVariant().toStringList();
|
||||
auto right = rhs.toVariant().toStringList();
|
||||
|
||||
if (left.size() != right.size())
|
||||
return false;
|
||||
|
||||
left.sort();
|
||||
right.sort();
|
||||
|
||||
return left == right;
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ StackLayout {
|
|||
viewAndPostHoldingsModel: root.permissionsStore.viewAndPostPermissionsModel
|
||||
assetsModel: root.rootStore.assetsModel
|
||||
collectiblesModel: root.rootStore.collectiblesModel
|
||||
isInvitationPending: root.rootStore.isCommunityRequestPending(communityData.id)
|
||||
isInvitationPending: root.rootStore.isCommunityRequestPending(communityId)
|
||||
notificationCount: activityCenterStore.unreadNotificationsCount
|
||||
hasUnseenNotifications: activityCenterStore.hasUnseenNotifications
|
||||
openCreateChat: rootStore.openCreateChat
|
||||
|
@ -74,7 +74,7 @@ StackLayout {
|
|||
onAdHocChatButtonClicked: rootStore.openCloseCreateChatView()
|
||||
onRevealAddressClicked: {
|
||||
Global.openPopup(communityIntroDialogPopup, {
|
||||
communityId: communityData.id,
|
||||
communityId: joinCommunityView.communityId,
|
||||
isInvitationPending: joinCommunityView.isInvitationPending,
|
||||
name: communityData.name,
|
||||
introMessage: communityData.introMessage,
|
||||
|
@ -83,15 +83,15 @@ StackLayout {
|
|||
})
|
||||
}
|
||||
onInvitationPendingClicked: {
|
||||
root.rootStore.cancelPendingRequest(communityData.id)
|
||||
joinCommunityView.isInvitationPending = root.rootStore.isCommunityRequestPending(communityData.id)
|
||||
root.rootStore.cancelPendingRequest(communityId)
|
||||
joinCommunityView.isInvitationPending = root.rootStore.isCommunityRequestPending(communityId)
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.rootStore.communitiesModuleInst
|
||||
function onCommunityAccessRequested(communityId: string) {
|
||||
if (communityId === joinCommunityView.communityData.id) {
|
||||
joinCommunityView.isInvitationPending = root.rootStore.isCommunityRequestPending(communityData.id)
|
||||
if (communityId === joinCommunityView.communityId) {
|
||||
joinCommunityView.isInvitationPending = root.rootStore.isCommunityRequestPending(communityId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -670,7 +670,7 @@ QtObject {
|
|||
target: mainModuleInst
|
||||
enabled: !!chatCommunitySectionModule
|
||||
function onOpenCommunityMembershipRequestsView(sectionId: string) {
|
||||
if(root.getMySectionId() != sectionId)
|
||||
if(root.getMySectionId() !== sectionId)
|
||||
return
|
||||
|
||||
root.goToMembershipRequestsPage()
|
||||
|
@ -703,6 +703,7 @@ QtObject {
|
|||
readonly property string id: model.id
|
||||
readonly property int sectionType: model.sectionType
|
||||
readonly property string name: model.name
|
||||
readonly property string image: model.image
|
||||
readonly property bool joined: model.joined
|
||||
readonly property bool amIBanned: model.amIBanned
|
||||
// add others when needed..
|
||||
|
|
|
@ -69,7 +69,6 @@ QtObject {
|
|||
}
|
||||
|
||||
function getUniquePermissionChannels(model, permissionsTypesArray = []) {
|
||||
// TODO return a QVariantMap (https://github.com/status-im/status-desktop/issues/11481)
|
||||
return Internal.PermissionUtils.getUniquePermissionChannels(model, permissionsTypesArray)
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,10 @@ StatusListView {
|
|||
property var uniquePermissionTokenKeys
|
||||
|
||||
// read/write properties
|
||||
property string selectedAirdropAddress: selectedSharedAddresses.length ? selectedSharedAddresses[0] : ""
|
||||
property var selectedSharedAddresses: count ? ModelUtils.modelToFlatArray(model, "address") : []
|
||||
property string selectedAirdropAddress
|
||||
property var selectedSharedAddresses: []
|
||||
|
||||
signal addressesChanged()
|
||||
|
||||
leftMargin: d.absLeftMargin
|
||||
topMargin: Style.current.padding
|
||||
|
@ -105,6 +107,7 @@ StatusListView {
|
|||
visible: shareAddressCheckbox.checked
|
||||
opacity: enabled ? 1.0 : 0.3
|
||||
onCheckedChanged: if (checked) root.selectedAirdropAddress = model.address
|
||||
onToggled: root.addressesChanged()
|
||||
|
||||
StatusToolTip {
|
||||
text: qsTr("Use this address for any Community airdrops")
|
||||
|
@ -134,6 +137,8 @@ StatusListView {
|
|||
if (!checked && model.address === root.selectedAirdropAddress) {
|
||||
d.selectFirstAvailableAirdropAddress()
|
||||
}
|
||||
|
||||
root.addressesChanged()
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -9,6 +9,7 @@ import StatusQ.Components 0.1
|
|||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Core.Utils 0.1
|
||||
import StatusQ.Internal 0.1 as SQInternal
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
|
@ -18,6 +19,7 @@ import AppLayouts.Communities.views 1.0
|
|||
import AppLayouts.Communities.helpers 1.0
|
||||
|
||||
import utils 1.0
|
||||
import shared.panels 1.0
|
||||
|
||||
Control {
|
||||
id: root
|
||||
|
@ -44,18 +46,20 @@ Control {
|
|||
onClicked: root.close()
|
||||
}
|
||||
StatusButton {
|
||||
enabled: root.selectedSharedAddresses.length && root.selectedAirdropAddress
|
||||
enabled: d.dirty
|
||||
type: d.lostCommunityPermission || d.lostChannelPermissions ? StatusBaseButton.Type.Danger : StatusBaseButton.Type.Normal
|
||||
visible: root.isEditMode
|
||||
icon.name: Constants.authenticationIconByType[root.loginType]
|
||||
text: qsTr("Save changes")
|
||||
icon.name: type === StatusBaseButton.Type.Normal && d.selectedAddressesDirty ? Constants.authenticationIconByType[root.loginType] : ""
|
||||
text: d.lostCommunityPermission ? qsTr("Save changes & leave %1").arg(root.communityName) :
|
||||
d.lostChannelPermissions ? qsTr("Save changes & update my permissions")
|
||||
: qsTr("Save changes")
|
||||
onClicked: {
|
||||
// TODO connect to backend
|
||||
root.saveSelectedAddressesClicked(root.selectedAirdropAddress, root.selectedSharedAddresses)
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
StatusButton {
|
||||
visible: !root.isEditMode
|
||||
enabled: root.selectedAirdropAddress && root.selectedSharedAddresses.length
|
||||
text: qsTr("Share selected addresses to join")
|
||||
onClicked: {
|
||||
root.shareSelectedAddressesClicked(root.selectedAirdropAddress, root.selectedSharedAddresses)
|
||||
|
@ -67,12 +71,15 @@ Control {
|
|||
|
||||
readonly property var rightButtons: [buttons.get(buttons.count-1)] // "magically" used by CommunityIntroDialog StatusStackModal impl
|
||||
|
||||
readonly property string selectedAirdropAddress: accountSelector.selectedAirdropAddress
|
||||
readonly property var selectedSharedAddresses: accountSelector.selectedSharedAddresses
|
||||
property var selectedSharedAddresses: []
|
||||
property string selectedAirdropAddress
|
||||
|
||||
signal shareSelectedAddressesClicked(string airdropAddress, var sharedAddresses)
|
||||
signal saveSelectedAddressesClicked(string airdropAddress, var sharedAddresses)
|
||||
|
||||
signal close()
|
||||
signal shareSelectedAddressesClicked(string airdropAddress, var sharedAddresses)
|
||||
|
||||
padding: 0
|
||||
spacing: Style.current.padding
|
||||
|
||||
QtObject {
|
||||
|
@ -80,12 +87,73 @@ Control {
|
|||
|
||||
// internal logic
|
||||
readonly property bool hasPermissions: root.permissionsModel && root.permissionsModel.count
|
||||
|
||||
// initial state (not bindings, we want a static snapshot of the initial state)
|
||||
property var initialSelectedSharedAddresses: []
|
||||
property string initialSelectedAirdropAddress
|
||||
|
||||
// dirty state handling
|
||||
readonly property bool selectedAddressesDirty: !SQInternal.ModelUtils.isSameArray(d.initialSelectedSharedAddresses, root.selectedSharedAddresses)
|
||||
readonly property bool selectedAirdropAddressDirty: root.selectedAirdropAddress !== d.initialSelectedAirdropAddress
|
||||
readonly property bool dirty: selectedAddressesDirty || selectedAirdropAddressDirty
|
||||
|
||||
// warning states
|
||||
readonly property bool lostCommunityPermission: root.isEditMode && permissionsView.lostPermissionToJoin
|
||||
readonly property bool lostChannelPermissions: root.isEditMode && permissionsView.lostChannelPermissions
|
||||
}
|
||||
|
||||
padding: 0
|
||||
Component.onCompleted: {
|
||||
// initialize the state
|
||||
d.initialSelectedSharedAddresses = root.selectedSharedAddresses.length ? root.selectedSharedAddresses
|
||||
: filteredAccountsModel.count ? ModelUtils.modelToFlatArray(filteredAccountsModel, "address")
|
||||
: []
|
||||
d.initialSelectedAirdropAddress = !!root.selectedAirdropAddress ? root.selectedAirdropAddress
|
||||
: d.initialSelectedSharedAddresses.length ? d.initialSelectedSharedAddresses[0] : ""
|
||||
root.selectedSharedAddresses = accountSelector.selectedSharedAddresses
|
||||
root.selectedAirdropAddress = accountSelector.selectedAirdropAddress
|
||||
}
|
||||
|
||||
SortFilterProxyModel {
|
||||
id: filteredAccountsModel
|
||||
sourceModel: root.walletAccountsModel
|
||||
filters: ValueFilter {
|
||||
roleName: "walletType"
|
||||
value: Constants.watchWalletType
|
||||
inverted: true
|
||||
}
|
||||
sorters: [
|
||||
ExpressionSorter {
|
||||
function isGenerated(modelData) {
|
||||
return modelData.walletType === Constants.generatedWalletType
|
||||
}
|
||||
|
||||
expression: {
|
||||
return isGenerated(modelLeft)
|
||||
}
|
||||
},
|
||||
RoleSorter {
|
||||
roleName: "position"
|
||||
},
|
||||
RoleSorter {
|
||||
roleName: "name"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
// warning panel
|
||||
ModuleWarning {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 32
|
||||
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
|
||||
closeBtnVisible: false
|
||||
}
|
||||
|
||||
// addresses
|
||||
SharedAddressesAccountSelector {
|
||||
id: accountSelector
|
||||
|
@ -96,6 +164,12 @@ Control {
|
|||
Layout.maximumHeight: hasPermissions ? permissionsView.implicitHeight > root.availableHeight / 2 ? root.availableHeight / 2 : root.availableHeight : -1
|
||||
Layout.fillHeight: !hasPermissions
|
||||
model: root.walletAccountsModel
|
||||
selectedSharedAddresses: d.initialSelectedSharedAddresses
|
||||
selectedAirdropAddress: d.initialSelectedAirdropAddress
|
||||
onAddressesChanged: {
|
||||
root.selectedSharedAddresses = selectedSharedAddresses
|
||||
root.selectedAirdropAddress = selectedAirdropAddress
|
||||
}
|
||||
}
|
||||
|
||||
// divider with top rounded corners + drop shadow
|
||||
|
@ -121,6 +195,7 @@ Control {
|
|||
// permissions
|
||||
SharedAddressesPermissionsPanel {
|
||||
id: permissionsView
|
||||
isEditMode: root.isEditMode
|
||||
permissionsModel: root.permissionsModel
|
||||
assetsModel: root.assetsModel
|
||||
collectiblesModel: root.collectiblesModel
|
||||
|
|
|
@ -18,11 +18,17 @@ import utils 1.0
|
|||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool isEditMode
|
||||
|
||||
property string communityName
|
||||
property string communityIcon
|
||||
|
||||
property var permissionsModel
|
||||
property var assetsModel
|
||||
property var collectiblesModel
|
||||
property string communityName
|
||||
property string communityIcon
|
||||
|
||||
readonly property bool lostPermissionToJoin: d.lostPermissionToJoin
|
||||
readonly property bool lostChannelPermissions: d.lostChannelPermissions
|
||||
|
||||
implicitHeight: permissionsScrollView.contentHeight - permissionsScrollView.anchors.topMargin
|
||||
color: Theme.palette.baseColor2
|
||||
|
@ -35,11 +41,26 @@ Rectangle {
|
|||
readonly property color tableBorderColor: Theme.palette.directColor7
|
||||
|
||||
// internal logic
|
||||
readonly property bool lostPermissionToJoin: root.isEditMode && joinPermissionsModel.count && !joinPermissionPanel.tokenCriteriaMet
|
||||
|
||||
readonly property var uniquePermissionChannels:
|
||||
root.permissionsModel && root.permissionsModel.count ?
|
||||
PermissionsHelpers.getUniquePermissionChannels(root.permissionsModel, [PermissionTypes.Type.Read, PermissionTypes.Type.ViewAndPost])
|
||||
: []
|
||||
|
||||
property var initialChannelPermissions
|
||||
|
||||
function getChannelPermissions() {
|
||||
var result = {}
|
||||
for (let i = 0; i < channelPermissionsPanel.count; i++) {
|
||||
const channel = channelPermissionsPanel.itemAt(i)
|
||||
const ckey = channel.channelKey
|
||||
result[ckey] = [channel.readPermissionMet, channel.viewAndPostPermissionMet]
|
||||
}
|
||||
return result
|
||||
}
|
||||
readonly property bool lostChannelPermissions: root.isEditMode && d.uniquePermissionChannels.length > 0 && channelPermissionsPanel.anyPermissionLost
|
||||
|
||||
// models
|
||||
readonly property var adminPermissionsModel: SortFilterProxyModel {
|
||||
id: adminPermissionsModel
|
||||
|
@ -65,6 +86,10 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
d.initialChannelPermissions = d.getChannelPermissions()
|
||||
}
|
||||
|
||||
StatusScrollView {
|
||||
id: permissionsScrollView
|
||||
anchors.fill: parent
|
||||
|
@ -94,6 +119,7 @@ Rectangle {
|
|||
|
||||
// permission types
|
||||
PermissionPanel {
|
||||
id: joinPermissionPanel
|
||||
permissionType: PermissionTypes.Type.Member
|
||||
permissionsModel: d.joinPermissionsModel
|
||||
}
|
||||
|
@ -103,8 +129,17 @@ Rectangle {
|
|||
}
|
||||
|
||||
Repeater { // channel repeater
|
||||
id: channelPermissionsPanel
|
||||
model: d.uniquePermissionChannels
|
||||
delegate: ChannelPermissionPanel {}
|
||||
readonly property bool anyPermissionLost: {
|
||||
for (let i = 0; i < channelPermissionsPanel.count; i++) {
|
||||
const channel = channelPermissionsPanel.itemAt(i)
|
||||
if (channel && channel.anyPermissionLost)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +185,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
component SinglePermissionFlow: Flow {
|
||||
width: parent.width
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.current.halfPadding
|
||||
Repeater {
|
||||
model: HoldingsSelectionModel {
|
||||
|
@ -214,9 +249,11 @@ Rectangle {
|
|||
id: permissionsRepeater
|
||||
|
||||
property int revision
|
||||
onItemAdded: revision++
|
||||
onItemRemoved: revision++
|
||||
|
||||
model: permissionPanel.permissionsModel
|
||||
delegate: Column {
|
||||
delegate: ColumnLayout {
|
||||
Layout.column: 0
|
||||
Layout.row: index
|
||||
Layout.fillWidth: true
|
||||
|
@ -228,9 +265,10 @@ Rectangle {
|
|||
SinglePermissionFlow {}
|
||||
|
||||
Rectangle {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width + grid.anchors.margins*2
|
||||
height: 1
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: -grid.anchors.leftMargin
|
||||
Layout.rightMargin: -grid.anchors.rightMargin
|
||||
Layout.preferredHeight: 1
|
||||
color: d.tableBorderColor
|
||||
visible: index < permissionsRepeater.count - 1
|
||||
}
|
||||
|
@ -244,10 +282,10 @@ Rectangle {
|
|||
Layout.fillHeight: true
|
||||
|
||||
readonly property bool tokenCriteriaMet: {
|
||||
permissionsRepeater.revision // NB no let/const here b/c of https://bugreports.qt.io/browse/QTBUG-91917
|
||||
for (var i = 0; i < permissionsRepeater.count; i++) {
|
||||
permissionsRepeater.revision
|
||||
for (var i = 0; i < permissionsRepeater.count; i++) { // NB no let/const here b/c of https://bugreports.qt.io/browse/QTBUG-91917
|
||||
var permissionItem = permissionsRepeater.itemAt(i);
|
||||
if (permissionItem.tokenCriteriaMet)
|
||||
if (permissionItem && permissionItem.tokenCriteriaMet)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -267,7 +305,13 @@ Rectangle {
|
|||
width: 16
|
||||
height: 16
|
||||
icon: overallPermissionRow.tokenCriteriaMet ? "tiny/checkmark" : "tiny/secure"
|
||||
color: overallPermissionRow.tokenCriteriaMet ? Theme.palette.successColor1 : Theme.palette.baseColor1
|
||||
color: {
|
||||
if (d.lostPermissionToJoin)
|
||||
return Theme.palette.dangerColor1
|
||||
if (overallPermissionRow.tokenCriteriaMet)
|
||||
return Theme.palette.successColor1
|
||||
return Theme.palette.baseColor1
|
||||
}
|
||||
}
|
||||
StatusBaseText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
@ -287,7 +331,13 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
color: overallPermissionRow.tokenCriteriaMet ? Theme.palette.directColor1 : Theme.palette.baseColor1
|
||||
color: {
|
||||
if (d.lostPermissionToJoin)
|
||||
return Theme.palette.dangerColor1
|
||||
if (overallPermissionRow.tokenCriteriaMet)
|
||||
return Theme.palette.directColor1
|
||||
return Theme.palette.baseColor1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -304,7 +354,10 @@ Rectangle {
|
|||
padding: d.absLeftMargin
|
||||
background: PanelBg {}
|
||||
|
||||
readonly property string channelKey: d.uniquePermissionChannels[index][0]
|
||||
readonly property string channelKey: modelData[0]
|
||||
readonly property bool readPermissionMet: channelPermsRepeater.count > 0 ? channelPermsRepeater.itemAt(0).tokenCriteriaMet : false
|
||||
readonly property bool viewAndPostPermissionMet: channelPermsRepeater.count > 1 ? channelPermsRepeater.itemAt(1).tokenCriteriaMet : false
|
||||
readonly property bool anyPermissionLost: channelPermsRepeater.count > 0 ? channelPermsRepeater.itemAt(0).permissionLost || channelPermsRepeater.itemAt(1).permissionLost : false
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: Style.current.padding
|
||||
|
@ -315,12 +368,15 @@ Rectangle {
|
|||
spacing: Style.current.smallPadding
|
||||
PanelHeading {}
|
||||
Repeater { // permissions repeater
|
||||
id: channelPermsRepeater
|
||||
model: [PermissionTypes.Type.Read, PermissionTypes.Type.ViewAndPost]
|
||||
|
||||
delegate: Rectangle {
|
||||
id: channelPermsSubPanel
|
||||
|
||||
readonly property int permissionType: modelData
|
||||
readonly property alias tokenCriteriaMet: overallPermissionRow2.tokenCriteriaMet
|
||||
readonly property bool permissionLost: d.initialChannelPermissions[channelPermsPanel.channelKey][index] && !tokenCriteriaMet
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: grid2.implicitHeight + grid2.anchors.margins*2
|
||||
|
@ -341,6 +397,8 @@ Rectangle {
|
|||
id: permissionsRepeater2
|
||||
|
||||
property int revision
|
||||
onItemAdded: revision++
|
||||
onItemRemoved: revision++
|
||||
|
||||
model: SortFilterProxyModel {
|
||||
id: channelPermissionsModel
|
||||
|
@ -354,7 +412,7 @@ Rectangle {
|
|||
expression: channelPermissionsModel.filterPredicate(model)
|
||||
}
|
||||
}
|
||||
delegate: Column {
|
||||
delegate: ColumnLayout {
|
||||
Layout.column: 0
|
||||
Layout.row: index
|
||||
Layout.fillWidth: true
|
||||
|
@ -366,9 +424,10 @@ Rectangle {
|
|||
SinglePermissionFlow {}
|
||||
|
||||
Rectangle {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width + grid2.anchors.margins*2
|
||||
height: 1
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: -grid2.anchors.leftMargin
|
||||
Layout.rightMargin: -grid2.anchors.rightMargin
|
||||
Layout.preferredHeight: 1
|
||||
color: d.tableBorderColor
|
||||
visible: index < permissionsRepeater2.count - 1
|
||||
}
|
||||
|
@ -384,9 +443,9 @@ Rectangle {
|
|||
|
||||
readonly property bool tokenCriteriaMet: {
|
||||
permissionsRepeater2.revision
|
||||
for (let i = 0; i < permissionsRepeater2.count; i++) {
|
||||
for (var i = 0; i < permissionsRepeater2.count; i++) { // NB no let/const here b/c of https://bugreports.qt.io/browse/QTBUG-91917
|
||||
const permissionItem = permissionsRepeater2.itemAt(i);
|
||||
if (permissionItem.tokenCriteriaMet)
|
||||
if (permissionItem && permissionItem.tokenCriteriaMet)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -406,7 +465,13 @@ Rectangle {
|
|||
width: 16
|
||||
height: 16
|
||||
icon: overallPermissionRow2.tokenCriteriaMet ? "tiny/checkmark" : "tiny/secure"
|
||||
color: overallPermissionRow2.tokenCriteriaMet ? Theme.palette.successColor1 : Theme.palette.baseColor1
|
||||
color: {
|
||||
if (channelPermsSubPanel.permissionLost)
|
||||
return Theme.palette.dangerColor1
|
||||
if (overallPermissionRow2.tokenCriteriaMet)
|
||||
return Theme.palette.successColor1
|
||||
return Theme.palette.baseColor1
|
||||
}
|
||||
}
|
||||
StatusBaseText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
@ -424,7 +489,13 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
color: overallPermissionRow2.tokenCriteriaMet ? Theme.palette.directColor1 : Theme.palette.baseColor1
|
||||
color: {
|
||||
if (channelPermsSubPanel.permissionLost)
|
||||
return Theme.palette.dangerColor1
|
||||
if (overallPermissionRow2.tokenCriteriaMet)
|
||||
return Theme.palette.directColor1
|
||||
return Theme.palette.baseColor1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,10 +20,11 @@ StatusDialog {
|
|||
required property var assetsModel
|
||||
required property var collectiblesModel
|
||||
|
||||
readonly property string selectedAirdropAddress: panel.selectedAirdropAddress
|
||||
readonly property var selectedSharedAddresses: panel.selectedSharedAddresses
|
||||
property alias selectedSharedAddresses: panel.selectedSharedAddresses
|
||||
property alias selectedAirdropAddress: panel.selectedAirdropAddress
|
||||
|
||||
signal shareSelectedAddressesClicked(string airdropAddress, var sharedAddresses)
|
||||
signal saveSelectedAddressesClicked(string airdropAddress, var sharedAddresses)
|
||||
|
||||
title: panel.title
|
||||
implicitWidth: 640 // by design
|
||||
|
@ -40,6 +41,7 @@ StatusDialog {
|
|||
assetsModel: root.assetsModel
|
||||
collectiblesModel: root.collectiblesModel
|
||||
onShareSelectedAddressesClicked: root.shareSelectedAddressesClicked(airdropAddress, sharedAddresses)
|
||||
onSaveSelectedAddressesClicked: root.saveSelectedAddressesClicked(airdropAddress, sharedAddresses)
|
||||
onClose: root.close()
|
||||
}
|
||||
|
||||
|
|
|
@ -128,7 +128,6 @@ StatusListView {
|
|||
onTriggered: {
|
||||
moreMenu.close()
|
||||
Global.openEditSharedAddressesFlow(model.id)
|
||||
// TODO shared addresses flow, cf https://github.com/status-im/status-desktop/issues/11138
|
||||
}
|
||||
}
|
||||
StatusMenuSeparator {
|
||||
|
|
|
@ -449,9 +449,10 @@ Item {
|
|||
popupMenu: Component {
|
||||
StatusMenu {
|
||||
id: communityContextMenu
|
||||
width: 180
|
||||
property var chatCommunitySectionModule
|
||||
|
||||
readonly property bool isSpectator: model.spectated && !model.joined
|
||||
|
||||
openHandler: function () {
|
||||
// we cannot return QVariant if we pass another parameter in a function call
|
||||
// that's why we're using it this way
|
||||
|
@ -496,19 +497,35 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
StatusAction {
|
||||
text: qsTr("Edit Shared Addresses")
|
||||
icon.name: "wallet"
|
||||
enabled: {
|
||||
if (model.memberRole === Constants.memberRole.owner)
|
||||
return false
|
||||
if (communityContextMenu.isSpectator && !appMain.rootStore.isCommunityRequestPending(model.id))
|
||||
return false
|
||||
return true
|
||||
}
|
||||
onTriggered: {
|
||||
communityContextMenu.close()
|
||||
Global.openEditSharedAddressesFlow(model.id)
|
||||
}
|
||||
}
|
||||
|
||||
StatusMenuSeparator { visible: leaveCommunityMenuItem.enabled }
|
||||
|
||||
StatusAction {
|
||||
id: leaveCommunityMenuItem
|
||||
enabled: model.memberRole !== Constants.memberRole.owner
|
||||
text: {
|
||||
if (model.spectated)
|
||||
if (communityContextMenu.isSpectator)
|
||||
return qsTr("Close Community")
|
||||
return qsTr("Leave Community")
|
||||
}
|
||||
icon.name: model.spectated ? "close-circle" : "arrow-left"
|
||||
icon.name: communityContextMenu.isSpectator ? "close-circle" : "arrow-left"
|
||||
type: StatusAction.Type.Danger
|
||||
onTriggered: model.spectated ? communityContextMenu.chatCommunitySectionModule.leaveCommunity()
|
||||
onTriggered: communityContextMenu.isSpectator ? communityContextMenu.chatCommunitySectionModule.leaveCommunity()
|
||||
: popups.openLeaveCommunityPopup(model.name, model.id, model.outroMessage)
|
||||
}
|
||||
}
|
||||
|
@ -1198,6 +1215,7 @@ Item {
|
|||
emojiPopup: statusEmojiPopup.item
|
||||
stickersPopup: statusStickersPopupLoader.item
|
||||
sectionItemModel: model
|
||||
createChatPropertiesStore: appMain.createChatPropertiesStore
|
||||
communitySettingsDisabled: production && appMain.rootStore.profileSectionStore.walletStore.areTestNetworksEnabled
|
||||
|
||||
rootStore: ChatStores.RootStore {
|
||||
|
|
|
@ -14,11 +14,13 @@ import AppLayouts.Chat.popups 1.0
|
|||
import AppLayouts.Profile.popups 1.0
|
||||
import AppLayouts.Communities.popups 1.0
|
||||
|
||||
import AppLayouts.Wallet.stores 1.0 as WalletStore
|
||||
import AppLayouts.Chat.stores 1.0 as ChatStore
|
||||
|
||||
import shared.popups 1.0
|
||||
import shared.status 1.0
|
||||
|
||||
import utils 1.0
|
||||
import AppLayouts.Wallet.stores 1.0 as WalletStore
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
@ -61,6 +63,7 @@ QtObject {
|
|||
Global.openTestnetPopup.connect(openTestnetPopup)
|
||||
Global.openExportControlNodePopup.connect(openExportControlNodePopup)
|
||||
Global.openImportControlNodePopup.connect(openImportControlNodePopup)
|
||||
Global.openEditSharedAddressesFlow.connect(openEditSharedAddressesPopup)
|
||||
}
|
||||
|
||||
property var currentPopup
|
||||
|
@ -234,7 +237,11 @@ QtObject {
|
|||
imageSrc: imageSrc,
|
||||
accessType: accessType,
|
||||
isInvitationPending: isInvitationPending
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function openEditSharedAddressesPopup(communityId) {
|
||||
openPopup(editSharedAddressesPopupComponent, {communityId: communityId, isEditMode: true})
|
||||
}
|
||||
|
||||
function openDiscordImportProgressPopup() {
|
||||
|
@ -498,18 +505,15 @@ QtObject {
|
|||
ImportCommunityPopup {
|
||||
store: root.communitiesStore
|
||||
onJoinCommunity: {
|
||||
Global.communityIntroPopupRequested(
|
||||
communityId,
|
||||
close()
|
||||
openCommunityIntroPopup(communityId,
|
||||
communityDetails.name,
|
||||
communityDetails.introMessage,
|
||||
communityDetails.image,
|
||||
Constants.communityChatOnRequestAccess,
|
||||
root.rootStore.isCommunityRequestPending(communityId));
|
||||
close();
|
||||
}
|
||||
onClosed: {
|
||||
destroy();
|
||||
communityDetails.access,
|
||||
root.rootStore.isCommunityRequestPending(communityId))
|
||||
}
|
||||
onClosed: destroy()
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -528,10 +532,14 @@ QtObject {
|
|||
Connections {
|
||||
target: root.communitiesStore.communitiesModuleInst
|
||||
function onCommunityAccessRequested(communityId: string) {
|
||||
if (communityId !== communityIntroDialog.communityId)
|
||||
return
|
||||
root.communitiesStore.spectateCommunity(communityId);
|
||||
communityIntroDialog.close();
|
||||
}
|
||||
function onCommunityAccessFailed(communityId: string) {
|
||||
if (communityId !== communityIntroDialog.communityId)
|
||||
return
|
||||
communityIntroDialog.close();
|
||||
}
|
||||
function onUserAuthenticationCanceled() {
|
||||
|
@ -675,7 +683,38 @@ QtObject {
|
|||
ImportControlNodePopup {
|
||||
onClosed: destroy()
|
||||
}
|
||||
},
|
||||
|
||||
Component {
|
||||
id: editSharedAddressesPopupComponent
|
||||
SharedAddressesPopup {
|
||||
id: editSharedAddressesPopup
|
||||
|
||||
property string communityId
|
||||
|
||||
readonly property var chatStore: ChatStore.RootStore {
|
||||
contactsStore: root.rootStore.contactStore
|
||||
chatCommunitySectionModule: {
|
||||
root.rootStore.mainModuleInst.prepareCommunitySectionModuleForCommunityId(editSharedAddressesPopup.communityId)
|
||||
return root.rootStore.mainModuleInst.getCommunitySectionModule()
|
||||
}
|
||||
}
|
||||
|
||||
communityName: chatStore.sectionDetails.name
|
||||
communityIcon: chatStore.sectionDetails.image
|
||||
// FIXME get these from the community settings (from the initial "join" call)
|
||||
//selectedSharedAddresses: [???]
|
||||
//selectedAirdropAddress: "???"
|
||||
loginType: chatStore.loginType
|
||||
walletAccountsModel: WalletStore.RootStore.receiveAccounts
|
||||
permissionsModel: chatStore.permissionsStore.permissionsModel
|
||||
assetsModel: chatStore.assetsModel
|
||||
collectiblesModel: chatStore.collectiblesModel
|
||||
|
||||
onSaveSelectedAddressesClicked: console.warn("!!! FIXME implement saving the shared & airdrop addresses for the community",
|
||||
JSON.stringify(sharedAddresses), airdropAddress)
|
||||
onClosed: destroy()
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue