feat(Airdrop): Added welcome page and basic view to create airdrop

- Added basic navigation.
- Added welcome page.
- Added temporal basic create airdrop page.

Closes #9796
This commit is contained in:
Noelia 2023-03-13 17:32:14 +01:00 committed by Noelia
parent 3feddd892b
commit b8b6f36bd9
10 changed files with 389 additions and 9 deletions

View File

@ -14,6 +14,7 @@ StatusDropdown {
property var assetsModel
property var collectiblesModel
property bool isENSTab: true
property var usedTokens: []
property var usedEnsNames: []
@ -74,6 +75,8 @@ StatusDropdown {
readonly property var holdingTypes: [
HoldingTypes.Type.Asset, HoldingTypes.Type.Collectible, HoldingTypes.Type.Ens
]
readonly property var tabsModel: [qsTr("Assets"), qsTr("Collectibles"), qsTr("ENS")]
readonly property var tabsModelNoEns: [qsTr("Assets"), qsTr("Collectibles")]
readonly property bool assetsReady: root.assetAmount > 0 && root.assetKey
readonly property bool collectiblesReady: root.collectibleAmount > 0 && root.collectibleKey
readonly property bool ensReady: d.ensDomainNameValid
@ -179,13 +182,15 @@ StatusDropdown {
]
onCurrentIndexChanged: {
d.currentHoldingType = d.holdingTypes[currentIndex]
d.setInitialFlow()
if(currentIndex >= 0) {
d.currentHoldingType = d.holdingTypes[currentIndex]
d.setInitialFlow()
}
}
Repeater {
id: tabLabelsRepeater
model: [qsTr("Assets"), qsTr("Collectibles"), qsTr("ENS")]
model: root.isENSTab ? d.tabsModel : d.tabsModelNoEns
StatusSwitchTabButton {
text: modelData
@ -204,14 +209,14 @@ StatusDropdown {
State {
name: HoldingsDropdown.FlowType.Selected
PropertyChanges {target: loader; sourceComponent: (d.currentHoldingType === HoldingTypes.Type.Asset) ? assetLayout :
((d.currentHoldingType === HoldingTypes.Type.Collectible) ? collectibleLayout : ensLayout) }
((d.currentHoldingType === HoldingTypes.Type.Collectible) ? collectibleLayout : ensLayout) }
PropertyChanges {target: root; height: undefined} // use implicit height
},
State {
name: HoldingsDropdown.FlowType.List_Deep1
PropertyChanges {target: loader; sourceComponent: listLayout}
PropertyChanges {target: root; height: d.extendedContentHeight}
PropertyChanges {target: d; extendedDeepNavigation: false}
PropertyChanges {target: d; extendedDeepNavigation: false}
},
State {
name: HoldingsDropdown.FlowType.List_Deep2
@ -352,8 +357,8 @@ StatusDropdown {
onRemoveClicked: root.removeClicked()
Component.onCompleted: {
if (d.collectibleAmountText.length === 0 && root.collectibleAmount)
collectiblePanel.setAmount(root.collectibleAmount)
if (d.collectibleAmountText.length === 0 && root.collectibleAmount)
collectiblePanel.setAmount(root.collectibleAmount)
}
Connections {

View File

@ -0,0 +1,103 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import StatusQ.Controls 0.1
import StatusQ.Core.Utils 0.1
import AppLayouts.Chat.layouts 1.0
import AppLayouts.Chat.views.communities 1.0
import utils 1.0
SettingsPageLayout {
id: root
// Token models:
required property var assetsModel
required property var collectiblesModel
property int viewWidth: 560 // by design
signal airdropClicked(var airdropTokens, string address)
// TODO: Update with stackmanager when #8736 is integrated
function navigateBack() {
stackManager.pop(StackView.Immediate)
}
QtObject {
id: d
readonly property string welcomeViewState: "WELCOME"
readonly property string newAirdropViewState: "NEW_AIRDROP"
readonly property string welcomePageTitle: qsTr("Airdrops")
readonly property string newAirdropViewPageTitle: qsTr("New airdrop")
}
content: StackView {
anchors.fill: parent
initialItem: welcomeView
Component.onCompleted: stackManager.pushInitialState(d.welcomeViewState)
}
state: stackManager.currentState
states: [
State {
name: d.welcomeViewState
PropertyChanges {target: root; title: d.welcomePageTitle}
PropertyChanges {target: root; previousPageName: ""}
PropertyChanges {target: root; headerButtonVisible: true}
PropertyChanges {target: root; headerButtonText: qsTr("New Airdrop")}
PropertyChanges {target: root; headerWidth: root.viewWidth}
},
State {
name: d.newAirdropViewState
PropertyChanges {target: root; title: d.newAirdropViewPageTitle}
PropertyChanges {target: root; previousPageName: d.welcomePageTitle}
PropertyChanges {target: root; headerButtonVisible: false}
PropertyChanges {target: root; headerWidth: 0}
}
]
onHeaderButtonClicked: stackManager.push(d.newAirdropViewState, newAirdropView, null, StackView.Immediate)
StackViewStates {
id: stackManager
stackView: root.contentItem
}
// Mint tokens possible view contents:
Component {
id: welcomeView
CommunityWelcomeSettingsView {
viewWidth: root.viewWidth
image: Style.png("community/airdrops8_1")
title: qsTr("Airdrop community tokens")
subtitle: qsTr("You can mint custom tokens and collectibles for your community")
checkersModel: [
qsTr("Reward individual members with custom tokens for their contribution"),
qsTr("Incentivise joining, retention, moderation and desired behaviour"),
qsTr("Require holding a token or NFT to obtain exclusive membership rights")
]
}
}
Component {
id: newAirdropView
CommunityNewAirdropView {
assetsModel: root.assetsModel
collectiblesModel: root.collectiblesModel
onAirdropClicked: {
root.airdropClicked(airdropTokens, address)
stackManager.clear(d.welcomeViewState, StackView.Immediate)
}
}
}
}

View File

@ -1,3 +1,4 @@
CommunityAirdropsSettingsPanel 1.0 CommunityAirdropsSettingsPanel.qml
CommunityColumnHeaderPanel 1.0 CommunityColumnHeaderPanel.qml
CommunityMintTokensSettingsPanel 1.0 CommunityMintTokensSettingsPanel.qml
CommunityPermissionsSettingsPanel 1.0 CommunityPermissionsSettingsPanel.qml

View File

@ -55,4 +55,9 @@ QtObject {
}
])
}
// Airdrop tokens:
function airdrop(airdropTokens, address) {
console.warn("TODO: Airdrop backend call!")
}
}

View File

@ -36,9 +36,9 @@ StatusSectionLayout {
property var settingsMenuModel: [{name: qsTr("Overview"), icon: "show", enabled: true},
{name: qsTr("Members"), icon: "group-chat", enabled: true},
{name: qsTr("Permissions"), icon: "objects", enabled: root.rootStore.communityPermissionsEnabled},
{name: qsTr("Mint Tokens"), icon: "token", enabled: root.rootStore.communityTokensEnabled}]
{name: qsTr("Mint Tokens"), icon: "token", enabled: root.rootStore.communityTokensEnabled},
{name: qsTr("Airdrops"), icon: "airdrop", enabled: root.rootStore.communityTokensEnabled}]
// TODO: Next community settings options:
// {name: qsTr("Airdrops"), icon: "airdrop"},
// {name: qsTr("Token sales"), icon: "token-sale"},
// {name: qsTr("Subscriptions"), icon: "subscription"},
property var rootStore
@ -310,6 +310,17 @@ StatusSectionLayout {
}
}
CommunityAirdropsSettingsPanel {
readonly property CommunityTokensStore communityTokensStore:
rootStore.communityTokensStore
assetsModel: rootStore.assetsModel
collectiblesModel: rootStore.collectiblesModel
onPreviousPageNameChanged: root.backButtonName = previousPageName
onAirdropClicked: communityTokensStore.airdrop(airdropTokens, chainId, address)
}
onCurrentIndexChanged: root.backButtonName = centerPanelContentLoader.item.children[d.currentIndex].previousPageName
}
}

View File

@ -0,0 +1,246 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
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
import utils 1.0
import shared.panels 1.0
import AppLayouts.Chat.helpers 1.0
import AppLayouts.Chat.panels.communities 1.0
import AppLayouts.Chat.controls.community 1.0
// TEMPORAL - BASIC IMPLEMENTATION
StatusScrollView {
id: root
// Token models:
required property var assetsModel
required property var collectiblesModel
property int viewWidth: 560 // by design
// roles: type, key, name, amount, imageSource
property var selectedHoldingsModel: ListModel {}
readonly property bool isFullyFilled: selectedHoldingsModel.count > 0 &&
addressess.itemsModel.count > 0
signal airdropClicked(var airdropTokens, string address)
QtObject {
id: d
readonly property int maxAirdropTokens: 5
readonly property int dropdownHorizontalOffset: 4
readonly property int dropdownVerticalOffset: 1
}
contentWidth: mainLayout.width
contentHeight: mainLayout.height
ColumnLayout {
id: mainLayout
width: root.viewWidth
spacing: 0
StatusItemSelector {
id: tokensSelector
property int editedIndex: -1
Layout.fillWidth: true
icon: Style.svg("token")
title: qsTr("What")
defaultItemText: qsTr("Example: 1 SOCK")
tagLeftPadding: 2
asset.height: 28
asset.width: asset.height
addButton.visible: itemsModel.count < d.maxAirdropTokens
itemsModel: HoldingsSelectionModel {
sourceModel: root.selectedHoldingsModel
assetsModel: root.assetsModel
collectiblesModel: root.collectiblesModel
}
// TODO: All this code is repeated inside `CommunityNewPermissionView`. Check how to reuse it.
HoldingsDropdown {
id: dropdown
assetsModel: root.assetsModel
collectiblesModel: root.collectiblesModel
isENSTab: false
function addItem(type, item, amount) {
const key = item.key
root.selectedHoldingsModel.append(
{ type, key, amount })
}
function getHoldingIndex(key) {
return ModelUtils.indexOf(root.selectedHoldingsModel, "key", key)
}
function prepareUpdateIndex(key) {
const itemIndex = tokensSelector.editedIndex
const existingIndex = getHoldingIndex(key)
if (itemIndex !== -1 && existingIndex !== -1 && itemIndex !== existingIndex) {
const previousKey = root.selectedHoldingsModel.get(itemIndex).key
root.selectedHoldingsModel.remove(existingIndex)
return getHoldingIndex(previousKey)
}
if (itemIndex === -1) {
return existingIndex
}
return itemIndex
}
onAddAsset: {
const modelItem = CommunityPermissionsHelpers.getTokenByKey(
root.assetsModel, key)
addItem(HoldingTypes.Type.Asset, modelItem, amount)
dropdown.close()
}
onAddCollectible: {
const modelItem = CommunityPermissionsHelpers.getTokenByKey(
root.collectiblesModel, key)
addItem(HoldingTypes.Type.Collectible, modelItem, amount)
dropdown.close()
}
onUpdateAsset: {
const itemIndex = prepareUpdateIndex(key)
const modelItem = CommunityPermissionsHelpers.getTokenByKey(root.assetsModel, key)
root.selectedHoldingsModel.set(
itemIndex, { type: HoldingTypes.Type.Asset, key, amount })
dropdown.close()
}
onUpdateCollectible: {
const itemIndex = prepareUpdateIndex(key)
const modelItem = CommunityPermissionsHelpers.getTokenByKey(
root.collectiblesModel, key)
root.selectedHoldingsModel.set(
itemIndex,
{ type: HoldingTypes.Type.Collectible, key, amount })
dropdown.close()
}
onRemoveClicked: {
root.selectedHoldingsModel.remove(tokensSelector.editedIndex)
dropdown.close()
}
}
addButton.onClicked: {
dropdown.parent = tokensSelector.addButton
dropdown.x = tokensSelector.addButton.width + d.dropdownHorizontalOffset
dropdown.y = 0
dropdown.open()
editedIndex = -1
}
onItemClicked: {
if (mouse.button !== Qt.LeftButton)
return
dropdown.parent = item
dropdown.x = mouse.x + d.dropdownHorizontalOffset
dropdown.y = d.dropdownVerticalOffset
const modelItem = tokensSelector.itemsModel.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)
dropdown.openUpdateFlow()
editedIndex = index
}
}
Rectangle {
Layout.leftMargin: 16
Layout.preferredWidth: 2
Layout.preferredHeight: 24
color: Style.current.separator
}
// TEMPORAL
StatusInput {
id: addressInput
Layout.fillWidth: true
placeholderText: qsTr("Example: 0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7999")
}
Rectangle {
Layout.leftMargin: 16
Layout.preferredWidth: 2
Layout.preferredHeight: 24
color: Style.current.separator
}
StatusItemSelector {
id: addressess
Layout.fillWidth: true
icon: Style.svg("member")
title: qsTr("To")
defaultItemText: qsTr("Example: 12 addresses and 3 members")
tagLeftPadding: 2
asset.height: 28
asset.width: asset.height
addButton.onClicked: {
if(addressInput.text.length > 0)
itemsModel.append({text: addressInput.text})
}
onItemClicked: addressess.itemsModel.remove(index)
}
StatusButton {
Layout.preferredHeight: 44
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
Layout.topMargin: Style.current.bigPadding
text: qsTr("Create airdrop")
enabled: root.isFullyFilled
onClicked: {
const airdropTokens = ModelUtils.modelToArray(
root.selectedHoldingsModel,
["key", "type", "amount"])
root.airdropClicked(airdropTokens, addressess.itemsModel)
}
}
}
}

View File

@ -1,5 +1,6 @@
ChannelsSelectionModel 1.0 ChannelsSelectionModel.qml
CommunityCollectibleView 1.0 CommunityCollectibleView.qml
CommunityNewAirdropView 1.0 CommunityNewAirdropView.qml
CommunityMintedTokensView 1.0 CommunityMintedTokensView.qml
CommunityNewCollectibleView 1.0 CommunityNewCollectibleView.qml
CommunityNewPermissionView 1.0 CommunityNewPermissionView.qml

View File

@ -0,0 +1,4 @@
<svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.9995 5C12.9995 7.76142 10.7609 10 7.99951 10C5.23809 10 2.99951 7.76142 2.99951 5C2.99951 2.23858 5.23809 0 7.99951 0C10.7609 0 12.9995 2.23858 12.9995 5ZM11.4995 5C11.4995 6.933 9.93251 8.5 7.99951 8.5C6.06651 8.5 4.49951 6.933 4.49951 5C4.49951 3.067 6.06651 1.5 7.99951 1.5C9.93251 1.5 11.4995 3.067 11.4995 5Z" fill="#4360DF"/>
<path d="M14.383 17.3874C14.6855 17.7312 15.2215 17.7779 15.5453 17.4541C15.806 17.1933 15.8374 16.7784 15.5976 16.4984C13.7635 14.357 11.0399 13 7.99936 13C4.95884 13 2.23525 14.357 0.401185 16.4984C0.161325 16.7784 0.192703 17.1933 0.453434 17.454C0.777236 17.7779 1.31325 17.7312 1.61575 17.3874C3.17347 15.6171 5.45596 14.5 7.99936 14.5C10.5428 14.5 12.8253 15.6171 14.383 17.3874Z" fill="#4360DF"/>
</svg>

After

Width:  |  Height:  |  Size: 892 B

View File

@ -0,0 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.09383 13.3807C10.2449 13.6891 11.4157 13.3863 12.2617 12.6713C12.4724 12.4931 12.7863 12.471 12.9928 12.6541L13.3671 12.9861C13.5739 13.1694 13.5943 13.4877 13.3911 13.6749C12.8023 14.2172 12.0897 14.6125 11.3164 14.8243C11.1344 14.8742 10.9865 15.0114 10.9377 15.1936L10.6204 16.3779C10.5489 16.6447 10.2747 16.8029 10.008 16.7315L9.52504 16.6021C9.25831 16.5306 9.10002 16.2564 9.17149 15.9897L9.29443 15.5309C9.37357 15.2355 9.1705 14.9405 8.87263 14.8714C8.81694 14.8584 8.76126 14.8445 8.70561 14.8296C8.65006 14.8147 8.59498 14.799 8.54038 14.7823C8.24787 14.6933 7.92451 14.8473 7.84537 15.1426L7.72243 15.6014C7.65096 15.8682 7.37679 16.0265 7.11006 15.955L6.6271 15.8256C6.36036 15.7541 6.20207 15.4799 6.27354 15.2132L6.59082 14.0291C6.63966 13.8468 6.58014 13.654 6.44745 13.5199C5.23912 12.2981 4.69324 10.4855 5.17007 8.70589C5.64692 6.92625 7.02609 5.62935 8.68347 5.1755C8.86548 5.12566 9.01343 4.98846 9.06227 4.80618L9.37954 3.6221C9.45102 3.35537 9.72518 3.19708 9.99192 3.26855L10.4749 3.39796C10.7416 3.46943 10.8999 3.7436 10.8284 4.01033L10.7055 4.46923C10.6263 4.76457 10.8294 5.05959 11.1272 5.12872C11.1828 5.14161 11.2383 5.15549 11.2938 5.17036C11.3494 5.18525 11.4045 5.20102 11.4591 5.21764C11.7516 5.30671 12.0749 5.15274 12.1541 4.85739L12.277 4.39859C12.3485 4.13186 12.6226 3.97357 12.8894 4.04504L13.3723 4.17445C13.6391 4.24592 13.7974 4.52009 13.7259 4.78682L13.4086 5.97091C13.3598 6.15318 13.4193 6.34594 13.552 6.48011C14.1158 7.05022 14.5354 7.74901 14.7742 8.51307C14.8566 8.77679 14.6797 9.04229 14.409 9.09769L13.9189 9.19799C13.6485 9.25332 13.3877 9.07726 13.2943 8.81757C12.9191 7.77531 12.0566 6.92767 10.9056 6.61925C9.03844 6.11895 7.11926 7.22699 6.61896 9.09412C6.11866 10.9613 7.2267 12.8804 9.09383 13.3807Z" fill="#4360DF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20 10C20 15.5228 15.5228 20 10 20C4.47715 20 0 15.5228 0 10C0 4.47715 4.47715 0 10 0C15.5228 0 20 4.47715 20 10ZM18.5 10C18.5 14.6944 14.6944 18.5 10 18.5C5.30558 18.5 1.5 14.6944 1.5 10C1.5 5.30558 5.30558 1.5 10 1.5C14.6944 1.5 18.5 5.30558 18.5 10Z" fill="#4360DF"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB