feat(CommunityNewAirdropView): design-compliant token airdrop flow for collectibles

This commit is contained in:
Michał Cieślak 2023-04-25 23:24:04 +02:00 committed by Michał
parent f9e2ce97a4
commit 27aac8d83a
16 changed files with 482 additions and 126 deletions

View File

@ -17,6 +17,10 @@ ListModel {
title: "CommunityNewPermissionView"
section: "Views"
}
ListElement {
title: "CommunityNewAirdropView"
section: "Views"
}
ListElement {
title: "ProfileFetchingView"
section: "Views"

View File

@ -45,6 +45,14 @@
"CommunityMintedTokensView": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=2934%3A479136&t=zs22ORYUVDYpqubQ-1"
],
"CommunityNewAirdropView": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22602-495563&t=9dIP8Sji2UlfhsEs-0",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22628-495258&t=9dIP8Sji2UlfhsEs-0",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22628-496145&t=9dIP8Sji2UlfhsEs-0",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22647-497754&t=9dIP8Sji2UlfhsEs-0",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22647-501014&t=9dIP8Sji2UlfhsEs-0",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22647-499051&t=kHAcE8WSCyGqhWSH-0"
],
"CommunityNewCollectibleView": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=2934%3A480877&t=Qo2FwPRxvSxbluqB-1",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=26601%3A518245&t=Qo2FwPRxvSxbluqB-1",

View File

@ -110,11 +110,10 @@ SplitView {
showAddressesInputWhenEmpty:
showAddressesInputWhenEmptyCheckBox.checked
infiniteExpectedNumberOfRecipients:
infiniteExpectedNumberOfRecipientsCheckBox.checked
infiniteMaxNumberOfRecipients:
infiniteMaxNumberOfRecipientsCheckBox.checked
expectedNumberOfRecipients:
expectedNumberOfRecipientsSpinBox.value
maxNumberOfRecipients: maxNumberOfRecipientsSpinBox.value
onAddAddressesRequested: timer.start()
onRemoveAddressRequested: addresses.remove(index)
@ -163,7 +162,7 @@ SplitView {
}
CheckBox {
id: infiniteExpectedNumberOfRecipientsCheckBox
id: infiniteMaxNumberOfRecipientsCheckBox
text: "Infinite number of expected recipients"
}
@ -175,7 +174,7 @@ SplitView {
}
SpinBox {
id: expectedNumberOfRecipientsSpinBox
id: maxNumberOfRecipientsSpinBox
value: 2
from: 1

View File

@ -25,6 +25,7 @@ SplitView {
anchors.topMargin: 50
assetsModel: AssetsModel {}
collectiblesModel: CollectiblesModel {}
membersModel: ListModel {}
onAirdropClicked: logs.logEvent("CommunityAirdropsSettingsPanel::onAirdropClicked")
}

View File

@ -0,0 +1,180 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import AppLayouts.Chat.views.communities 1.0
import AppLayouts.Chat.controls.community 1.0
import Storybook 1.0
import Models 1.0
import SortFilterProxyModel 0.2
import utils 1.0
SplitView {
orientation: Qt.Vertical
SplitView.fillWidth: true
property bool globalUtilsReady: false
property bool mainModuleReady: false
Logs { id: logs }
QtObject {
function isCompressedPubKey(publicKey) {
return true
}
function getCompressedPk(publicKey) {
return "compressed_" + publicKey
}
function getColorId(publicKey) {
return Math.floor(Math.random() * 10)
}
Component.onCompleted: {
Utils.globalUtilsInst = this
globalUtilsReady = true
}
Component.onDestruction: {
globalUtilsReady = false
Utils.globalUtilsInst = {}
}
}
QtObject {
function getContactDetailsAsJson() {
return JSON.stringify({ ensVerified: true })
}
Component.onCompleted: {
mainModuleReady = true
Utils.mainModuleInst = this
}
Component.onDestruction: {
mainModuleReady = false
Utils.mainModuleInst = {}
}
}
ListModel {
id: members
property int counter: 0
function addMember() {
const i = counter++
const key = `pub_key_${i}`
const firstLetters = ["a", "b", "c", "d"]
const firstLetterIdx = Math.min(Math.floor(i / firstLetters.length),
firstLetters.length - 1)
const firstLetter = firstLetters[firstLetterIdx]
append({
alias: "",
colorId: "1",
displayName: `${firstLetter}contact ${i}`,
ensName: "",
icon: "",
isContact: true,
localNickname: "",
onlineStatus: 1,
pubKey: key,
isVerified: true,
isUntrustworthy: false
})
}
Component.onCompleted: {
for (let i = 0; i < 33; i++)
addMember()
}
}
Pane {
SplitView.fillWidth: true
SplitView.fillHeight: true
Loader {
anchors.fill: parent
active: globalUtilsReady && mainModuleReady
sourceComponent: CommunityNewAirdropView {
id: communityNewPermissionView
CollectiblesModel {
id: collectiblesModel
}
SortFilterProxyModel {
id: collectiblesModelWithSupply
sourceModel: collectiblesModel
proxyRoles: [
ExpressionRole {
name: "supply"
expression: ((model.index + 1) * 115).toString()
},
ExpressionRole {
name: "infiniteSupply"
expression: !(model.index % 4)
},
ExpressionRole {
name: "chainName"
expression: model.index ? "Optimism" : "Arbitrum"
},
ExpressionRole {
readonly property string icon1: "network/Network=Optimism"
readonly property string icon2: "network/Network=Arbitrum"
name: "chainIcon"
expression: model.index ? icon1 : icon2
}
]
filters: ValueFilter {
roleName: "category"
value: TokenCategories.Category.Community
}
Component.onCompleted: {
Qt.callLater(() => communityNewPermissionView.collectiblesModel = this)
}
}
assetsModel: ListModel {}
collectiblesModel: ListModel {}
membersModel: members
onAirdropClicked: {
logs.logEvent("CommunityNewAirdropView::airdropClicked", ["airdropTokens", "addresses", "membersPubKeys"], arguments)
}
}
}
}
LogsAndControlsPanel {
id: logsAndControlsPanel
SplitView.minimumHeight: 100
SplitView.preferredHeight: 160
logsView.logText: logs.logText
ColumnLayout {
MenuSeparator {}
TextEdit {
readOnly: true
selectByMouse: true
text: "valid address: 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc4"
}
}
}
}

View File

@ -25,6 +25,10 @@ SplitView {
return true
}
function getCompressedPk(publicKey) {
return "compressed_" + publicKey
}
function getColorId(publicKey) {
return Math.floor(Math.random() * 10)
}

View File

@ -68,6 +68,7 @@ AbstractButton {
}
StatusBaseText {
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
text: root.text
color: root.textColor
font.pixelSize: root.font.pixelSize

View File

@ -180,10 +180,11 @@ Control {
multiline: true
topPadding: bottomPadding + (listView.count ? d.spacing : 0)
bottomPadding: 5
height: edit.implicitHeight + topPadding + bottomPadding
placeholderText: qsTr("Example: 0x39cf...fbd2")
placeholderText: root.count ? "" : qsTr("Example: 0x39cf...fbd2")
Keys.onPressed: {
if ((event.key !== Qt.Key_Return && event.key !== Qt.Key_Enter)

View File

@ -17,14 +17,15 @@ StatusFlowSelector {
property alias addressesInputText: addressesSelectorPanel.text
property bool showAddressesInputWhenEmpty: false
property int expectedNumberOfRecipients: 0
property bool infiniteExpectedNumberOfRecipients: false
property int maxNumberOfRecipients: 0
property bool infiniteMaxNumberOfRecipients: false
readonly property int count: addressesSelectorPanel.count +
membersSelectorPanel.count
readonly property bool valid:
addressesSelectorPanel.invalidAddressesCount === 0
addressesSelectorPanel.invalidAddressesCount === 0 &&
(infiniteMaxNumberOfRecipients || count <= maxNumberOfRecipients)
signal addAddressesRequested(string addresses)
signal removeAddressRequested(int index)
@ -35,7 +36,8 @@ StatusFlowSelector {
title: qsTr("To")
icon: Style.svg("member")
flowSpacing: 12
flowSpacing: addressesSelectorPanel.visible || membersSelectorPanel.visible
? 12 : 6
placeholderText: qsTr("Example: 12 addresses and 3 members")
@ -61,12 +63,12 @@ StatusFlowSelector {
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
readonly property bool valid: root.infiniteExpectedNumberOfRecipients ||
root.count <= root.expectedNumberOfRecipients
readonly property bool valid: root.infiniteMaxNumberOfRecipients ||
root.count <= root.maxNumberOfRecipients
text: root.count + " / " + (root.infiniteExpectedNumberOfRecipients
text: root.count + " / " + (root.infiniteMaxNumberOfRecipients
? qsTr("∞ recipients", "infinite number of recipients")
: qsTr("%n recipient(s)", "", root.expectedNumberOfRecipients))
: qsTr("%n recipient(s)", "", root.maxNumberOfRecipients))
font.pixelSize: Theme.tertiaryTextFontSize + 1
color: valid ? Theme.palette.baseColor1 : Theme.palette.dangerColor1

View File

@ -12,8 +12,9 @@ StatusFlowSelector {
id: root
property alias model: repeater.model
readonly property alias count: repeater.count
signal itemClicked(int index, var mouse)
signal itemClicked(int index, var mouse, var item)
placeholderText: qsTr("Example: 1 SOCK")
placeholderItem.visible: repeater.count === 0
@ -32,9 +33,13 @@ StatusFlowSelector {
id: repeater
Control {
id: delegateRoot
component Icon: StatusRoundedImage {
implicitWidth: d.iconSize
implicitHeight: d.iconSize
image.mipmap: true
}
component Text: StatusBaseText {
@ -55,7 +60,7 @@ StatusFlowSelector {
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: root.itemClicked(model.index, mouse)
onClicked: root.itemClicked(model.index, mouse, delegateRoot)
}
}

View File

@ -36,6 +36,8 @@ StatusDropdown {
bottomInset: 10
bottomPadding: padding + bottomInset
onOpened: filterInput.text = ""
QtObject {
id: d

View File

@ -68,7 +68,7 @@ Control {
}
StatusBaseText {
visible: !!root.amount
visible: !!root.amount && !root.selected
text: root.amount
color: Theme.palette.baseColor1
font.pixelSize: 12

View File

@ -17,9 +17,11 @@ SettingsPageLayout {
required property var assetsModel
required property var collectiblesModel
required property var membersModel
property int viewWidth: 560 // by design
signal airdropClicked(var airdropTokens, var addresses)
signal airdropClicked(var airdropTokens, var addresses, var membersPubKeys)
signal navigateToMintTokenSettings
@ -102,9 +104,10 @@ SettingsPageLayout {
assetsModel: root.assetsModel
collectiblesModel: root.collectiblesModel
membersModel: root.membersModel
onAirdropClicked: {
root.airdropClicked(airdropTokens, addresses)
root.airdropClicked(airdropTokens, addresses, membersPubKeys)
stackManager.clear(d.welcomeViewState, StackView.Immediate)
}
onNavigateToMintTokenSettings: root.navigateToMintTokenSettings()

View File

@ -19,6 +19,7 @@ import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1
import AppLayouts.Chat.stores 1.0
import AppLayouts.Chat.controls.community 1.0
import shared.stores 1.0
import shared.views.chat 1.0
@ -370,8 +371,48 @@ StatusSectionLayout {
readonly property CommunityTokensStore communityTokensStore:
rootStore.communityTokensStore
assetsModel: rootStore.assetsModel
collectiblesModel: rootStore.collectiblesModel
assetsModel: ListModel {}
readonly property var communityTokens: root.community.communityTokens
Loader {
id: modelLoader
active: airdropPanel.communityTokens
sourceComponent: SortFilterProxyModel {
sourceModel: airdropPanel.communityTokens
proxyRoles: [
ExpressionRole {
name: "category"
// Singleton cannot be used directly in the epression
readonly property int category: TokenCategories.Category.Own
expression: category
},
ExpressionRole {
name: "iconSource"
expression: model.image
},
ExpressionRole {
name: "key"
expression: model.symbol
}
]
}
}
collectiblesModel: modelLoader.item
membersModel: {
const chatContentModule = root.rootStore.currentChatContentModule()
if (!chatContentModule || !chatContentModule.usersModule) {
// New communities have no chats, so no chatContentModule
return null
}
return chatContentModule.usersModule.model
}
onPreviousPageNameChanged: root.backButtonName = previousPageName
onAirdropClicked: communityTokensStore.airdrop(root.community.id, airdropTokens, addresses)
@ -413,7 +454,7 @@ StatusSectionLayout {
if(d.currentItem && d.currentItem.goTo) {
d.currentItem.goTo(subSection)
}
}
}
}
}

View File

@ -1,10 +1,8 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import QtQuick 2.15
import QtQuick.Layouts 1.15
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Core.Utils 0.1
@ -15,7 +13,9 @@ import AppLayouts.Chat.helpers 1.0
import AppLayouts.Chat.panels.communities 1.0
import AppLayouts.Chat.controls.community 1.0
// TEMPORAL - BASIC IMPLEMENTATION
import SortFilterProxyModel 0.2
StatusScrollView {
id: root
@ -23,21 +23,27 @@ StatusScrollView {
required property var assetsModel
required property var collectiblesModel
// Community members model:
required property var membersModel
property int viewWidth: 560 // by design
// roles: type, key, name, amount, imageSource
property var selectedHoldingsModel: ListModel {}
readonly property var selectedHoldingsModel: ListModel {}
readonly property bool isFullyFilled: selectedHoldingsModel.count > 0 &&
addressess.model.count > 0
readonly property bool isFullyFilled: tokensSelector.count > 0 &&
airdropRecipientsSelector.count > 0 &&
airdropRecipientsSelector.valid
signal airdropClicked(var airdropTokens, var addresses)
signal airdropClicked(var airdropTokens, var addresses, var membersPubKeys)
signal navigateToMintTokenSettings
function selectCollectible(key, amount) {
const modelItem = CommunityPermissionsHelpers.getTokenByKey(
root.collectiblesModel, key)
d.addItem(HoldingTypes.Type.Collectible, modelItem, amount)
const entry = d.prepareEntry(key, amount)
entry.valid = true
selectedHoldingsModel.append(entry)
}
QtObject {
@ -47,22 +53,68 @@ StatusScrollView {
readonly property int dropdownHorizontalOffset: 4
readonly property int dropdownVerticalOffset: 1
function addItem(type, item, amount) {
const key = item.key
function prepareEntry(key, amount) {
const modelItem = CommunityPermissionsHelpers.getTokenByKey(
root.collectiblesModel, key)
root.selectedHoldingsModel.append({ type, key, amount })
return {
key, amount,
tokenText: amount + " " + modelItem.name,
tokenImage: modelItem.iconSource,
networkText: modelItem.chainName,
networkImage: Style.svg(modelItem.chainIcon),
supply: modelItem.supply,
infiniteSupply: modelItem.infiniteSupply
}
}
}
Instantiator {
id: recipientsCountInstantiator
model: selectedHoldingsModel
property bool infinity: true
property int maximumRecipientsCount
function findRecipientsCount() {
let min = Number.MAX_SAFE_INTEGER
for (let i = 0; i < count; i++) {
const item = objectAt(i)
if (!item || item.infiniteSupply)
continue
min = Math.min(item.supply / item.amount, min)
}
infinity = min === Number.MAX_SAFE_INTEGER
maximumRecipientsCount = infinity ? 0 : min
}
delegate: QtObject {
readonly property int supply: model.supply
readonly property real amount: model.amount
readonly property bool infiniteSupply: model.infiniteSupply
onSupplyChanged: recipientsCountInstantiator.findRecipientsCount()
onAmountChanged: recipientsCountInstantiator.findRecipientsCount()
onInfiniteSupplyChanged: recipientsCountInstantiator.findRecipientsCount()
}
onCountChanged: findRecipientsCount()
}
contentWidth: mainLayout.width
contentHeight: mainLayout.height
ColumnLayout {
SequenceColumnLayout {
id: mainLayout
width: root.viewWidth
spacing: 0
StatusItemSelector {
AirdropTokensSelector {
id: tokensSelector
property int editedIndex: -1
@ -71,19 +123,10 @@ StatusScrollView {
icon: Style.svg("token")
title: qsTr("What")
placeholderText: qsTr("Example: 1 SOCK")
tagLeftPadding: 2
asset.height: 28
asset.width: asset.height
addButton.visible: model.count < d.maxAirdropTokens
model: HoldingsSelectionModel {
sourceModel: root.selectedHoldingsModel
model: root.selectedHoldingsModel
assetsModel: root.assetsModel
collectiblesModel: root.collectiblesModel
}
// TODO: All this code is repeated inside `CommunityNewPermissionView`. Check how to reuse it.
HoldingsDropdown {
id: dropdown
@ -114,37 +157,26 @@ StatusScrollView {
return itemIndex
}
onAddAsset: {
const modelItem = CommunityPermissionsHelpers.getTokenByKey(
root.assetsModel, key)
d.addItem(HoldingTypes.Type.Asset, modelItem, amount)
dropdown.close()
onOpened: {
usedTokens = ModelUtils.modelToArray(
root.selectedHoldingsModel, ["key", "amount"])
}
onAddCollectible: {
const modelItem = CommunityPermissionsHelpers.getTokenByKey(
root.collectiblesModel, key)
d.addItem(HoldingTypes.Type.Collectible, modelItem, amount)
dropdown.close()
}
const entry = d.prepareEntry(key, amount)
onUpdateAsset: {
const itemIndex = prepareUpdateIndex(key)
const modelItem = CommunityPermissionsHelpers.getTokenByKey(root.assetsModel, key)
root.selectedHoldingsModel.set(
itemIndex, { type: HoldingTypes.Type.Asset, key, amount })
selectedHoldingsModel.append(entry)
dropdown.close()
}
onUpdateCollectible: {
const itemIndex = prepareUpdateIndex(key)
const entry = d.prepareEntry(key, amount)
const modelItem = CommunityPermissionsHelpers.getTokenByKey(
root.collectiblesModel, key)
root.selectedHoldingsModel.set(
itemIndex,
{ type: HoldingTypes.Type.Collectible, key, amount })
root.selectedHoldingsModel.set(itemIndex, entry)
dropdown.close()
}
@ -176,70 +208,146 @@ StatusScrollView {
dropdown.x = mouse.x + d.dropdownHorizontalOffset
dropdown.y = d.dropdownVerticalOffset
const modelItem = tokensSelector.model.get(index)
switch(modelItem.type) {
case HoldingTypes.Type.Asset:
dropdown.assetKey = modelItem.key
dropdown.assetAmount = modelItem.amount
break
case HoldingTypes.Type.Collectible:
dropdown.collectibleKey = modelItem.key
dropdown.collectibleAmount = modelItem.amount
break
default:
console.warn("Unsupported holdings type.")
}
dropdown.setActiveTab(modelItem.type)
const modelItem = selectedHoldingsModel.get(index)
dropdown.collectibleKey = modelItem.key
dropdown.collectibleAmount = modelItem.amount
dropdown.setActiveTab(HoldingTypes.Type.Collectible)
dropdown.openUpdateFlow()
editedIndex = index
}
}
Rectangle {
Layout.leftMargin: 16
Layout.preferredWidth: 2
Layout.preferredHeight: 24
color: Style.current.separator
}
SequenceColumnLayout.Separator {}
// TEMPORAL
StatusInput {
id: addressInput
AirdropRecipientsSelector {
id: airdropRecipientsSelector
Layout.fillWidth: true
addressesModel: addresses
placeholderText: qsTr("Example: 0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7999")
}
infiniteMaxNumberOfRecipients:
recipientsCountInstantiator.infinity
Rectangle {
Layout.leftMargin: 16
Layout.preferredWidth: 2
Layout.preferredHeight: 24
color: Style.current.separator
}
maxNumberOfRecipients:
recipientsCountInstantiator.maximumRecipientsCount
StatusItemSelector {
id: addressess
membersModel: SortFilterProxyModel {
sourceModel: membersModel
Layout.fillWidth: true
icon: Style.svg("member")
title: qsTr("To")
placeholderText: qsTr("Example: 12 addresses and 3 members")
tagLeftPadding: 2
asset.height: 28
asset.width: asset.height
filters: ExpressionFilter {
id: selectedKeysFilter
model: ListModel {}
property var keys: []
addButton.onClicked: {
if(addressInput.text.length > 0)
model.append({text: addressInput.text})
expression: keys.indexOf(model.pubKey) !== -1
}
}
onItemClicked: addressess.model.remove(index)
onRemoveMemberRequested: {
const pubKey = ModelUtils.get(membersModel, index, "pubKey")
const keyIndex = selectedKeysFilter.keys.indexOf(pubKey)
selectedKeysFilter.keys.splice(keyIndex, 1)
selectedKeysFilter.keys = selectedKeysFilter.keys
}
onAddAddressesRequested: (addresses_) => {
addresses.addAddressesFromString(addresses_)
airdropRecipientsSelector.clearAddressesInput()
airdropRecipientsSelector.positionAddressesListAtEnd()
}
onRemoveAddressRequested: addresses.remove(index)
ListModel {
id: addresses
function addAddressesFromString(addresses) {
const words = addresses.trim().split(/[\s+,]/)
const existing = new Set()
for (let i = 0; i < count; i++)
existing.add(get(i).address)
words.forEach(word => {
if (word === "" || existing.has(word))
return
const valid = Utils.isValidAddress(word)
append({ valid, address: word })
})
}
}
function openPopup(popup) {
popup.parent = addButton
popup.x = addButton.width + d.dropdownHorizontalOffset
popup.y = 0
popup.open()
}
addButton.onClicked: openPopup(recipientTypeSelectionDropdown)
RecipientTypeSelectionDropdown {
id: recipientTypeSelectionDropdown
onEthAddressesSelected: {
airdropRecipientsSelector.showAddressesInputWhenEmpty = true
airdropRecipientsSelector.forceInputFocus()
recipientTypeSelectionDropdown.close()
}
onCommunityMembersSelected: {
recipientTypeSelectionDropdown.close()
membersDropdown.selectedKeys = selectedKeysFilter.keys
airdropRecipientsSelector.openPopup(membersDropdown)
}
}
MembersDropdown {
id: membersDropdown
model: SortFilterProxyModel {
sourceModel: membersModel
filters: [
ExpressionFilter {
enabled: membersDropdown.searchText !== ""
function matchesAlias(name, filter) {
return name.split(" ").some(p => p.startsWith(filter))
}
expression: {
membersDropdown.selectedKeys
membersDropdown.searchText
if (membersDropdown.selectedKeys.indexOf(model.pubKey) > -1)
return true
const filter = membersDropdown.searchText.toLowerCase()
return matchesAlias(model.alias.toLowerCase(), filter)
|| model.displayName.toLowerCase().includes(filter)
|| model.ensName.toLowerCase().includes(filter)
|| model.localNickname.toLowerCase().includes(filter)
|| model.pubKey.toLowerCase().includes(filter)
}
}
]
}
onBackButtonClicked: {
close()
airdropRecipientsSelector.openPopup(
recipientTypeSelectionDropdown)
}
onAddButtonClicked: {
selectedKeysFilter.keys = selectedKeys
close()
}
}
}
StatusButton {
@ -253,13 +361,14 @@ StatusScrollView {
onClicked: {
const airdropTokens = ModelUtils.modelToArray(
root.selectedHoldingsModel,
["key", "type", "amount"])
["key", "amount"])
const addresses = ModelUtils.modelToArray(
addressess.model,
["text"])
const addresses_ = ModelUtils.modelToArray(
addresses, ["address"]).map(e => e.address)
root.airdropClicked(airdropTokens, addresses)
const pubKeys = selectedKeysFilter.keys
root.airdropClicked(airdropTokens, addresses_, pubKeys)
}
}
}

View File

@ -54,10 +54,6 @@ QtObject {
// Airdrop tokens:
function airdrop(communityId, airdropTokens, addresses) {
const addrArray = []
for(var i = 0; i < addresses.length; i++) {
addrArray.push(addresses[i]["text"])
}
communityTokensModuleInst.airdropCollectibles(communityId, JSON.stringify(airdropTokens), JSON.stringify(addrArray))
communityTokensModuleInst.airdropCollectibles(communityId, JSON.stringify(airdropTokens), JSON.stringify(addresses))
}
}