feat(@desktop/wallet): add collectibles list to send modal

Fixes #12072
This commit is contained in:
Dario Gabriel Lipicar 2023-09-11 07:20:36 -03:00 committed by dlipicar
parent bad497cc90
commit 50ffbb9dce
25 changed files with 1048 additions and 66 deletions

View File

@ -9,6 +9,8 @@ import utils 1.0
import shared.popups 1.0 import shared.popups 1.0
import shared.stores 1.0 import shared.stores 1.0
import StatusQ.Core.Utils 0.1
SplitView { SplitView {
id: root id: root
@ -32,11 +34,14 @@ SplitView {
visible: true visible: true
modal: false modal: false
closePolicy: Popup.NoAutoClose closePolicy: Popup.NoAutoClose
onlyAssets: false
store: TransactionStore { store: TransactionStore {
readonly property QtObject selectedSenderAccount: QtObject { readonly property QtObject selectedSenderAccount: QtObject {
readonly property var assets: WalletAssetsModel {} readonly property var assets: WalletAssetsModel {}
} }
readonly property QtObject collectiblesModel: WalletCollectiblesModel {}
readonly property QtObject nestedCollectiblesModel: WalletNestedCollectiblesModel {}
readonly property QtObject walletSectionSendInst: QtObject {} readonly property QtObject walletSectionSendInst: QtObject {}
readonly property QtObject mainModuleInst: QtObject {} readonly property QtObject mainModuleInst: QtObject {}
@ -61,6 +66,75 @@ SplitView {
return "" return ""
} }
function getAsset(assetsList, symbol) {
const idx = ModelUtils.indexOf(assetsList, "symbol", symbol)
if (idx < 0) {
return {}
}
return ModelUtils.get(assetsList, idx)
}
function getCollectible(uid) {
const idx = ModelUtils.indexOf(collectiblesModel, "uid", uid)
if (idx < 0) {
return {}
}
return ModelUtils.get(collectiblesModel, idx)
}
function getSelectorCollectible(uid) {
const idx = ModelUtils.indexOf(nestedCollectiblesModel, "uid", uid)
if (idx < 0) {
return {}
}
return ModelUtils.get(nestedCollectiblesModel, idx)
}
function getHolding(holdingId, holdingType) {
if (holdingType === Constants.HoldingType.Asset) {
return getAsset(selectedSenderAccount.assets, holdingId)
} else if (holdingType === Constants.HoldingType.Collectible) {
return getCollectible(holdingId)
} else {
return {}
}
}
function getSelectorHolding(holdingId, holdingType) {
if (holdingType === Constants.HoldingType.Asset) {
return getAsset(selectedSenderAccount.assets, holdingId)
} else if (holdingType === Constants.HoldingType.Collectible) {
return getSelectorCollectible(holdingId)
} else {
return {}
}
}
function assetToSelectorAsset(asset) {
return asset
}
function collectibleToSelectorCollectible(collectible) {
return {
uid: collectible.uid,
chainId: collectible.chainId,
name: collectible.name,
iconUrl: collectible.imageUrl,
collectionUid: collectible.collectionUid,
collectionName: collectible.collectionName,
isCollection: false
}
}
function holdingToSelectorHolding(holding, holdingType) {
if (holdingType === Constants.HoldingType.Asset) {
return assetToSelectorAsset(holding)
} else if (holdingType === Constants.HoldingType.Collectible) {
return collectibleToSelectorCollectible(holding)
} else {
return {}
}
}
readonly property string currentCurrency: "USD" readonly property string currentCurrency: "USD"

View File

@ -3,7 +3,12 @@ import QtQuick 2.15
ListModel { ListModel {
readonly property var data: [ readonly property var data: [
{ {
totalBalance: 323.3, totalBalance: ({
displayDecimals: true,
stripTrailingZeroes: true,
amount: 323.3,
symbol: "ETH"
}),
decimals: 2, decimals: 2,
totalCurrencyBalance: ({ totalCurrencyBalance: ({
displayDecimals: true, displayDecimals: true,
@ -27,7 +32,12 @@ ListModel {
allChecked: true allChecked: true
}, },
{ {
totalBalance: 324343.3, totalBalance: ({
displayDecimals: true,
stripTrailingZeroes: true,
amount: 324343.3,
symbol: "SNT"
}),
decimals: 2, decimals: 2,
totalCurrencyBalance: ({ totalCurrencyBalance: ({
displayDecimals: true, displayDecimals: true,

View File

@ -0,0 +1,88 @@
import QtQuick 2.15
ListModel {
readonly property var rootData: [
{
uid: "ID-Kitty1",
chainId: 1,
contractAddress: "0x1",
tokenId: "1",
name: "Furbeard",
imageUrl: ModelsData.collectibles.kitty1Big,
collectionUid: "cryptokitties",
collectionName: "CryptoKitties",
},
{
uid: "ID-Kitty2",
chainId: 1,
contractAddress: "0x1",
tokenId: "2",
name: "Magicat",
imageUrl: ModelsData.collectibles.kitty2Big,
collectionUid: "cryptokitties",
collectionName: "CryptoKitties",
},
{
uid: "ID-Kitty3",
chainId: 1,
contractAddress: "0x1",
tokenId: "3",
name: "Happy Meow",
imageUrl: ModelsData.collectibles.kitty3Big,
collectionUid: "cryptokitties",
collectionName: "CryptoKitties",
},
{
uid: "ID-Kitty4",
chainId: 1,
contractAddress: "0x1",
tokenId: "4",
name: "Furbeard-2",
imageUrl: ModelsData.collectibles.kitty4Big,
collectionUid: "cryptokitties",
collectionName: "CryptoKitties",
},
{
uid: "ID-Kitty5",
chainId: 1,
contractAddress: "0x1",
tokenId: "4",
name: "Magicat-3",
imageUrl: ModelsData.collectibles.kitty5Big,
collectionUid: "cryptokitties",
collectionName: "CryptoKitties"
},
{
uid: "ID-Anniversary",
chainId: 1,
contractAddress: "0x2",
tokenId: "1",
name: "Anniversary",
imageUrl: ModelsData.collectibles.anniversary,
collectionUid: "anniversary",
collectionName: "Anniversary",
},
{
uid: "ID-SuperRare",
chainId: 1,
contractAddress: "0x3",
tokenId: "101",
name: "SuperRare",
imageUrl: ModelsData.collectibles.superRare,
collectionUid: "super-rare",
collectionName: "SuperRare",
},
{
uid: "ID-Custom",
chainId: 1,
contractAddress: "0x04",
tokenId: "403",
name: "Custom Collectible",
imageUrl: ModelsData.collectibles.custom,
collectionUid: "custom",
collectionName: "Custom",
}
]
Component.onCompleted: append(rootData)
}

View File

@ -0,0 +1,103 @@
import QtQuick 2.15
ListModel {
readonly property var rootData: [
{
uid: "ID-Anniversary",
chainId: 1,
name: "Anniversary",
iconUrl: ModelsData.collectibles.anniversary,
collectionUid: "anniversary",
collectionName: "Anniversary",
isCollection: false,
},
{
uid: "ID-SuperRare",
chainId: 1,
name: "SuperRare",
iconUrl: ModelsData.collectibles.superRare,
collectionUid: "super-rare",
collectionName: "SuperRare",
isCollection: false,
},
{
uid: "cryptokitties",
chainId: 1,
name: "CryptoKitties",
iconUrl: ModelsData.collectibles.cryptoKitties,
collectionUid: "cryptokitties",
collectionName: "CryptoKitties",
isCollection: true,
},
{
uid: "ID-Custom",
chainId: 1,
name: "Custom Collectible",
iconUrl: ModelsData.collectibles.custom,
collectionUid: "custom",
collectionName: "Custom",
isCollection: false,
}
]
readonly property var criptoKittiesData: [
{
uid: "ID-Kitty1",
chainId: 1,
name: "Furbeard",
iconUrl: ModelsData.collectibles.kitty1Big,
collectionUid: "cryptokitties",
collectionName: "CryptoKitties",
isCollection: false,
},
{
uid: "ID-Kitty2",
chainId: 1,
name: "Magicat",
iconUrl: ModelsData.collectibles.kitty2Big,
collectionUid: "cryptokitties",
collectionName: "CryptoKitties",
isCollection: false,
},
{
uid: "ID-Kitty3",
chainId: 1,
name: "Happy Meow",
iconUrl: ModelsData.collectibles.kitty3Big,
collectionUid: "cryptokitties",
collectionName: "CryptoKitties",
isCollection: false,
},
{
uid: "ID-Kitty4",
chainId: 1,
name: "Furbeard-2",
iconUrl: ModelsData.collectibles.kitty4Big,
collectionUid: "cryptokitties",
collectionName: "CryptoKitties",
isCollection: false,
},
{
uid: "ID-Kitty5",
chainId: 1,
name: "Magicat-3",
iconUrl: ModelsData.collectibles.kitty5Big,
collectionUid: "cryptokitties",
collectionName: "CryptoKitties",
isCollection: false,
}
]
property string currentCollectionUid
onCurrentCollectionUidChanged: {
clear()
if (currentCollectionUid === "") {
append(rootData)
} else if (currentCollectionUid === "cryptokitties") {
append(criptoKittiesData)
}
}
Component.onCompleted: append(rootData)
}

View File

@ -12,7 +12,9 @@ TokenHoldersModel 1.0 TokenHoldersModel.qml
UsersModel 1.0 UsersModel.qml UsersModel 1.0 UsersModel.qml
WalletAccountsModel 1.0 WalletAccountsModel.qml WalletAccountsModel 1.0 WalletAccountsModel.qml
WalletAssetsModel 1.0 WalletAssetsModel.qml WalletAssetsModel 1.0 WalletAssetsModel.qml
WalletCollectiblesModel 1.0 WalletCollectiblesModel.qml
WalletKeyPairModel 1.0 WalletKeyPairModel.qml WalletKeyPairModel 1.0 WalletKeyPairModel.qml
WalletNestedCollectiblesModel 1.0 WalletNestedCollectiblesModel.qml
singleton ModelsData 1.0 ModelsData.qml singleton ModelsData 1.0 ModelsData.qml
singleton NetworksModel 1.0 NetworksModel.qml singleton NetworksModel 1.0 NetworksModel.qml
singleton PermissionsModel 1.0 PermissionsModel.qml singleton PermissionsModel 1.0 PermissionsModel.qml

View File

@ -64,7 +64,8 @@ StatusSectionLayout {
property Component sendTransactionModalComponent: SendModal { property Component sendTransactionModalComponent: SendModal {
anchors.centerIn: parent anchors.centerIn: parent
selectedAccount: WalletStore.dappBrowserAccount selectedAccount: WalletStore.dappBrowserAccount
preSelectedAsset: store.getAsset(WalletStore.dappBrowserAccount.assets, "ETH") preSelectedHolding: store.getAsset(WalletStore.dappBrowserAccount.assets, "ETH")
preSelectedHoldingType: Constants.HoldingType.Asset
} }
property Component signMessageModalComponent: SignMessageModal {} property Component signMessageModalComponent: SignMessageModal {}

View File

@ -120,7 +120,8 @@ Item {
sendType: Constants.SendType.ENSRelease sendType: Constants.SendType.ENSRelease
preSelectedRecipient: root.ensUsernamesStore.getEnsRegisteredAddress() preSelectedRecipient: root.ensUsernamesStore.getEnsRegisteredAddress()
preDefinedAmountToSend: LocaleUtils.numberToLocaleString(0) preDefinedAmountToSend: LocaleUtils.numberToLocaleString(0)
preSelectedAsset: store.getAsset(releaseEnsModal.store.assets, "ETH") preSelectedHolding: store.getAsset(releaseEnsModal.store.assets, Constants.ethToken)
preSelectedHoldingType: Constants.HoldingType.Asset
sendTransaction: function() { sendTransaction: function() {
if(bestRoutes.count === 1) { if(bestRoutes.count === 1) {
let path = bestRoutes.firstItem() let path = bestRoutes.firstItem()

View File

@ -66,7 +66,8 @@ Item {
sendType: Constants.SendType.ENSSetPubKey sendType: Constants.SendType.ENSSetPubKey
preSelectedRecipient: root.ensUsernamesStore.getEnsRegisteredAddress() preSelectedRecipient: root.ensUsernamesStore.getEnsRegisteredAddress()
preDefinedAmountToSend: LocaleUtils.numberToLocaleString(0) preDefinedAmountToSend: LocaleUtils.numberToLocaleString(0)
preSelectedAsset: store.getAsset(connectEnsModal.store.assets, "ETH") preSelectedHolding: store.getAsset(connectEnsModal.store.assets, Constants.ethToken)
preSelectedHoldingType: Constants.HoldingType.Asset
sendTransaction: function() { sendTransaction: function() {
if(bestRoutes.count === 1) { if(bestRoutes.count === 1) {
let path = bestRoutes.firstItem() let path = bestRoutes.firstItem()

View File

@ -50,7 +50,8 @@ Item {
sendType: Constants.SendType.ENSRegister sendType: Constants.SendType.ENSRegister
preSelectedRecipient: root.ensUsernamesStore.getEnsRegisteredAddress() preSelectedRecipient: root.ensUsernamesStore.getEnsRegisteredAddress()
preDefinedAmountToSend: LocaleUtils.numberToLocaleString(10) preDefinedAmountToSend: LocaleUtils.numberToLocaleString(10)
preSelectedAsset: store.getAsset(buyEnsModal.store.assets, JSON.parse(root.stickersStore.getStatusToken()).symbol) preSelectedHolding: store.getAsset(buyEnsModal.store.assets, JSON.parse(root.stickersStore.getStatusToken()).symbol)
preSelectedHoldingType: Constants.HoldingType.Asset
sendTransaction: function() { sendTransaction: function() {
if(bestRoutes.count === 1) { if(bestRoutes.count === 1) {
let path = bestRoutes.firstItem() let path = bestRoutes.firstItem()

View File

@ -40,6 +40,8 @@ Rectangle {
text: qsTr("Send") text: qsTr("Send")
interactive: networkConnectionStore.sendBuyBridgeEnabled interactive: networkConnectionStore.sendBuyBridgeEnabled
onClicked: function() { onClicked: function() {
sendModal.preSelectedHoldingID = walletStore.currentViewedHoldingID
sendModal.preSelectedHoldingType = walletStore.currentViewedHoldingType
sendModal.open() sendModal.open()
} }
tooltipText: networkConnectionStore.sendBuyBridgeToolTipText tooltipText: networkConnectionStore.sendBuyBridgeToolTipText
@ -61,6 +63,8 @@ Rectangle {
interactive: networkConnectionStore.sendBuyBridgeEnabled interactive: networkConnectionStore.sendBuyBridgeEnabled
onClicked: function() { onClicked: function() {
sendModal.isBridgeTx = true sendModal.isBridgeTx = true
sendModal.preSelectedHoldingID = walletStore.currentViewedHoldingID
sendModal.preSelectedHoldingType = walletStore.currentViewedHoldingType
sendModal.open() sendModal.open()
} }
tooltipText: networkConnectionStore.sendBuyBridgeToolTipText tooltipText: networkConnectionStore.sendBuyBridgeToolTipText

View File

@ -118,6 +118,24 @@ QtObject {
property var cryptoRampServicesModel: walletSectionBuySellCrypto.model property var cryptoRampServicesModel: walletSectionBuySellCrypto.model
function resetCurrentViewedHolding() {
currentViewedHoldingID = ""
currentViewedHoldingType = null
}
function setCurrentViewedHoldingType(type) {
currentViewedHoldingID = ""
currentViewedHoldingType = type
}
function setCurrentViewedHolding(id, type) {
currentViewedHoldingID = id
currentViewedHoldingType = type
}
property string currentViewedHoldingID: ""
property var currentViewedHoldingType
// This should be exposed to the UI via "walletModule", WalletModule should use // This should be exposed to the UI via "walletModule", WalletModule should use
// Accounts Service which keeps the info about that (isFirstTimeAccountLogin). // Accounts Service which keeps the info about that (isFirstTimeAccountLogin).
// Then in the View of WalletModule we may have either QtProperty or // Then in the View of WalletModule we may have either QtProperty or

View File

@ -16,7 +16,7 @@ Item {
property var collectiblesModel property var collectiblesModel
width: parent.width width: parent.width
signal collectibleClicked(int chainId, string contractAddress, string tokenId) signal collectibleClicked(int chainId, string contractAddress, string tokenId, string uid)
Loader { Loader {
id: contentLoader id: contentLoader
@ -64,7 +64,7 @@ Item {
backgroundColor: model.backgroundColor ? model.backgroundColor : "transparent" backgroundColor: model.backgroundColor ? model.backgroundColor : "transparent"
isLoading: !!model.isLoading isLoading: !!model.isLoading
onClicked: root.collectibleClicked(model.chainId, model.contractAddress, model.tokenId) onClicked: root.collectibleClicked(model.chainId, model.contractAddress, model.tokenId, model.uid)
} }
ScrollBar.vertical: StatusScrollBar {} ScrollBar.vertical: StatusScrollBar {}

View File

@ -8,6 +8,7 @@ import utils 1.0
import shared.controls 1.0 import shared.controls 1.0
import shared.views 1.0 import shared.views 1.0
import shared.stores 1.0 import shared.stores 1.0
import shared.panels 1.0
import "./" import "./"
import "../stores" import "../stores"
@ -124,6 +125,7 @@ Item {
assetDetailsLaunched: stack.currentIndex === 2 assetDetailsLaunched: stack.currentIndex === 2
onAssetClicked: { onAssetClicked: {
assetDetailView.token = token assetDetailView.token = token
RootStore.setCurrentViewedHolding(token.symbol, Constants.HoldingType.Asset)
stack.currentIndex = 2 stack.currentIndex = 2
} }
} }
@ -131,6 +133,7 @@ Item {
collectiblesModel: RootStore.collectiblesStore.ownedCollectibles collectiblesModel: RootStore.collectiblesStore.ownedCollectibles
onCollectibleClicked: { onCollectibleClicked: {
RootStore.collectiblesStore.getDetailedCollectible(chainId, contractAddress, tokenId) RootStore.collectiblesStore.getDetailedCollectible(chainId, contractAddress, tokenId)
RootStore.setCurrentViewedHolding(uid, Constants.HoldingType.Collectible)
stack.currentIndex = 1 stack.currentIndex = 1
} }
} }
@ -151,6 +154,11 @@ Item {
Layout.fillHeight: true Layout.fillHeight: true
collectible: RootStore.collectiblesStore.detailedCollectible collectible: RootStore.collectiblesStore.detailedCollectible
isCollectibleLoading: RootStore.collectiblesStore.isDetailedCollectibleLoading isCollectibleLoading: RootStore.collectiblesStore.isDetailedCollectibleLoading
onVisibleChanged: {
if (!visible)
RootStore.resetCurrentViewedHolding()
}
} }
AssetsDetailView { AssetsDetailView {
id: assetDetailView id: assetDetailView
@ -163,6 +171,11 @@ Item {
address: RootStore.overview.mixedcaseAddress address: RootStore.overview.mixedcaseAddress
networkConnectionStore: root.networkConnectionStore networkConnectionStore: root.networkConnectionStore
onVisibleChanged: {
if (!visible)
RootStore.resetCurrentViewedHolding()
}
} }
TransactionDetailView { TransactionDetailView {

View File

@ -1345,19 +1345,30 @@ Item {
} }
property var selectedAccount property var selectedAccount
property bool isBridgeTx property bool isBridgeTx
property string preSelectedHoldingID
property var preSelectedHoldingType
sourceComponent: SendModal { sourceComponent: SendModal {
onlyAssets: false
onClosed: { onClosed: {
sendModal.closed() sendModal.closed()
sendModal.isBridgeTx = false sendModal.isBridgeTx = false
sendModal.preSelectedHoldingID = ""
sendModal.preSelectedHoldingType = Constants.HoldingType.Unknown
} }
} }
onLoaded: { onLoaded: {
if (!!sendModal.selectedAccount) { if (!!sendModal.selectedAccount) {
item.selectedAccount = sendModal.selectedAccount item.selectedAccount = sendModal.selectedAccount
} }
if(isBridgeTx) if(isBridgeTx) {
item.isBridgeTx = sendModal.isBridgeTx item.isBridgeTx = sendModal.isBridgeTx
} }
if(preSelectedHoldingType !== Constants.HoldingType.Unknown) {
item.preSelectedHoldingID = sendModal.preSelectedHoldingID
item.preSelectedHoldingType = sendModal.preSelectedHoldingType
}
}
} }
Action { Action {

View File

@ -0,0 +1,62 @@
import QtQuick 2.13
import StatusQ.Core 0.1
import StatusQ.Components 0.1
import StatusQ.Core.Theme 0.1
import utils 1.0
StatusListItem {
id: root
property var getNetworkIcon: function(chainId) {
return ""
}
signal itemSelected(var selectedItem)
signal itemHovered(var selectedItem, bool hovered)
QtObject {
id: d
function selectItem() {
root.itemSelected(model)
}
}
Connections {
target: root.sensor
function onContainsMouseChanged() {
root.itemHovered(model, root.sensor.containsMouse)
}
}
title: name
statusListItemTitleAside.font.pixelSize: 15
asset.name: iconUrl ? iconUrl : ""
asset.isImage: true
asset.width: 32
asset.height: 32
statusListItemLabel.color: Theme.palette.directColor1
statusListItemInlineTagsSlot.spacing: 0
radius: sensor.containsMouse || root.highlighted ? 0 : 8
color: sensor.containsMouse || root.highlighted ? Theme.palette.baseColor2 : "transparent"
onClicked: d.selectItem()
components: [
StatusRoundedImage {
width: 20
height: 20
image.source: Style.svg("tiny/%1".arg(root.getNetworkIcon(chainId)))
visible: !isCollection && root.sensor.containsMouse
},
StatusIcon {
icon: "tiny/chevron-right"
color: Theme.palette.baseColor1
width: 16
height: 16
visible: isCollection
}
]
}

View File

@ -2,6 +2,7 @@ AddressInput 1.0 AddressInput.qml
AmountInput 1.0 AmountInput.qml AmountInput 1.0 AmountInput.qml
AssetAndAmountInput 1.0 AssetAndAmountInput.qml AssetAndAmountInput 1.0 AssetAndAmountInput.qml
AssetDelegate 1.0 AssetDelegate.qml AssetDelegate 1.0 AssetDelegate.qml
CollectibleNestedDelegate 1.0 CollectibleNestedDelegate.qml
ContactSelector 1.0 ContactSelector.qml ContactSelector 1.0 ContactSelector.qml
ContactsListAndSearch 1.0 ContactsListAndSearch.qml ContactsListAndSearch 1.0 ContactsListAndSearch.qml
CopyToClipBoardButton 1.0 CopyToClipBoardButton.qml CopyToClipBoardButton 1.0 CopyToClipBoardButton.qml

View File

@ -0,0 +1,147 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import SortFilterProxyModel 0.2
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Core.Backpressure 1.0
import shared.controls 1.0
import utils 1.0
Item {
id: root
property var comboBoxModel
property var selectedItem
property var hoveredItem
property string defaultIconSource
property string placeholderText
property var itemIconSourceFn: function (item) {
return ""
}
property var itemTextFn: function (item) {
return ""
}
property alias comboBoxControl: comboBox.control
property alias comboBoxDelegate: comboBox.delegate
property var comboBoxPopupHeader
property int contentIconSize: 21
property int contentTextSize: 28
function resetInternal() {
items = null
selectedItem = null
hoveredItem = null
}
function openPopup() {
root.comboBoxControl.popup.open()
}
implicitWidth: comboBox.width
implicitHeight: comboBox.implicitHeight
onSelectedItemChanged: {
if (!!selectedItem) {
d.iconSource = itemIconSourceFn(selectedItem) ?? defaultIconSource
d.text = itemTextFn(selectedItem) ?? placeholderText
}
}
onHoveredItemChanged: {
if (!!hoveredItem) {
d.iconSource = itemIconSourceFn(hoveredItem) ?? defaultIconSource
d.text = itemTextFn(hoveredItem) ?? placeholderText
}
}
QtObject {
id: d
property string iconSource: ""
property string text: ""
readonly property bool isItemSelected: !!root.selectedItem || !!root.hoveredItem
}
StatusComboBox {
id: comboBox
objectName: "assetSelectorButton"
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
control.padding: 4
control.popup.width: 492
control.popup.x: -root.x
control.popup.verticalPadding: 0
popupContentItemObjectName: "assetSelectorList"
model: root.comboBoxModel
control.background: Rectangle {
color: "transparent"
border.width: d.isItemSelected ? 0 : 1
border.color: Theme.palette.directColor7
radius: 12
}
contentItem: RowLayout {
id: rowLayout
implicitHeight: 38
StatusRoundedImage {
Layout.preferredWidth: root.contentIconSize
Layout.preferredHeight: root.contentIconSize
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
visible: !!d.iconSource
image.source: d.iconSource
image.onStatusChanged: {
if (image.status === Image.Error) {
image.source = root.defaultIconSource
}
}
}
StatusBaseText {
Layout.alignment: Qt.AlignVCenter
font.pixelSize: root.contentTextSize
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
color: Theme.palette.miscColor1
text: d.text
visible: d.isItemSelected
}
StatusIcon {
Layout.leftMargin: -3
Layout.alignment: Qt.AlignVCenter
Layout.preferredWidth: 16
Layout.preferredHeight: 16
icon: "chevron-down"
color: Theme.palette.miscColor1
visible: d.isItemSelected
}
}
control.indicator: null
Component.onCompleted: {
control.currentIndex = -1
control.popup.contentItem.header = root.comboBoxPopupHeader
}
control.popup.onOpened: {
control.currentIndex = -1
}
}
}

View File

@ -0,0 +1,346 @@
import QtQml 2.15
import QtQuick 2.13
import QtQuick.Layouts 1.13
import shared.controls 1.0
import utils 1.0
import SortFilterProxyModel 0.2
import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import "../controls"
Item {
id: root
property var assetsModel
property var collectiblesModel
property string currentCurrencySymbol
property bool onlyAssets: true
implicitWidth: holdingItemSelector.implicitWidth
implicitHeight: holdingItemSelector.implicitHeight
property var searchAssetSymbolByAddressFn: function (address) {
return ""
}
property var getNetworkIcon: function(chainId){
return ""
}
signal itemHovered(string holdingId, var holdingType)
signal itemSelected(string holdingId, var holdingType)
property alias selectedItem: holdingItemSelector.selectedItem
property alias hoveredItem: holdingItemSelector.hoveredItem
function setSelectedItem(item, holdingType) {
d.browsingHoldingType = holdingType
holdingItemSelector.selectedItem = null
d.currentHoldingType = holdingType
holdingItemSelector.selectedItem = item
}
function setHoveredItem(item, holdingType) {
d.browsingHoldingType = holdingType
holdingItemSelector.hoveredItem = null
d.currentHoldingType = holdingType
holdingItemSelector.hoveredItem = item
}
QtObject {
id: d
// Internal management properties and signals:
readonly property var holdingTypes: onlyAssets ?
[Constants.HoldingType.Asset] :
[Constants.HoldingType.Asset, Constants.HoldingType.Collectible]
readonly property var tabsModel: onlyAssets ?
[qsTr("Assets")] :
[qsTr("Assets"), qsTr("Collectibles")]
readonly property var updateSearchText: Backpressure.debounce(root, 1000, function(inputText) {
searchText = inputText
})
function isAsset(type) {
return type === Constants.HoldingType.Asset
}
property int browsingHoldingType: Constants.HoldingType.Asset
readonly property bool isCurrentBrowsingTypeAsset: isAsset(browsingHoldingType)
readonly property bool isBrowsingCollection: !isCurrentBrowsingTypeAsset && !!collectiblesModel && collectiblesModel.currentCollectionUid !== ""
property string currentBrowsingCollectionName
property var currentHoldingType: Constants.HoldingType.Unknown
property string searchText
readonly property string assetSymbolByAddress: isCurrentBrowsingTypeAsset ? "": root.searchAssetSymbolByAddressFn(searchText)
readonly property string uppercaseSearchText: searchText.toUpperCase()
property var assetTextFn: function (asset) {
return asset.symbol ? asset.symbol : ""
}
property var assetIconSourceFn: function (asset) {
return asset.symbol ? Style.png("tokens/%1".arg(asset.symbol)) : ""
}
property var assetComboBoxModel: SortFilterProxyModel {
sourceModel: root.assetsModel
filters: [
ExpressionFilter {
expression: {
d.uppercaseSearchText; // Force re-evaluation when searchText changes
return visibleForNetwork && (
d.uppercaseSearchText === "" ||
symbol.startsWith(d.uppercaseSearchText) ||
name.toUpperCase().startsWith(d.uppercaseSearchText) |
(d.assetSymbolByAddress !== "" && symbol.startsWith(d.assetSymbolByAddress))
)
}
}
]
}
property var collectibleTextFn: function (item) {
if (!!item) {
return !!item.collectionName ? item.collectionName + ": " + item.name : item.name
}
return ""
}
property var collectibleIconSourceFn: function (item) {
return item.iconUrl ? item.iconUrl : ""
}
property var collectibleComboBoxModel: SortFilterProxyModel {
sourceModel: root.collectiblesModel
filters: [
ExpressionFilter {
expression: {
return d.uppercaseSearchText === "" || name.toUpperCase().startsWith(d.uppercaseSearchText)
}
}
]
sorters: RoleSorter {
roleName: "isCollection"
sortOrder: Qt.DescendingOrder
}
}
readonly property string searchPlaceholderText: {
if (isCurrentBrowsingTypeAsset) {
return qsTr("Search for token or enter token address")
} else if (isBrowsingCollection) {
return qsTr("Search %1").arg(d.currentBrowsingCollectionName ?? qsTr("collectibles in collection"))
} else {
return qsTr("Search collectibles")
}
}
// By design values:
readonly property int padding: 16
readonly property int headerTopMargin: 5
readonly property int tabBarTopMargin: 20
readonly property int tabBarHeight: 35
readonly property int backButtonWidth: 56
readonly property int backButtonHeight: 24
readonly property int backButtonToContentSpace: 8
readonly property int bottomInset: 20
readonly property int assetContentIconSize: 21
readonly property int collectibleContentIconSize: 28
readonly property int assetContentTextSize: 28
readonly property int collectibleContentTextSize: 15
}
HoldingItemSelector {
id: holdingItemSelector
anchors.fill: parent
defaultIconSource: Style.png("tokens/DEFAULT-TOKEN@3x")
placeholderText: d.isCurrentBrowsingTypeAsset ? qsTr("Select token") : qsTr("Select collectible")
comboBoxDelegate: Item {
property var itemModel: model // read 'model' from the delegate's context
width: loader.width
height: loader.height
Loader {
id: loader
// inject model properties to the loaded item's context
// common
property var model: itemModel
property var chainId: model.chainId
property var name: model.name
// asset
property var symbol: model.symbol
property var totalBalance: model.totalBalance
property var totalCurrencyBalance: model.totalCurrencyBalance
property var decimals: model.decimals
property var balances: model.balances
// collectible
property var uid: model.uid
property var iconUrl: model.iconUrl
property var collectionUid: model.collectionUid
property var collectionName: model.collectionName
property var isCollection: model.isCollection
sourceComponent: d.isCurrentBrowsingTypeAsset ? assetComboBoxDelegate : collectibleComboBoxDelegate
}
}
comboBoxPopupHeader: headerComponent
itemTextFn: d.isCurrentBrowsingTypeAsset ? d.assetTextFn : d.collectibleTextFn
itemIconSourceFn: d.isCurrentBrowsingTypeAsset ? d.assetIconSourceFn : d.collectibleIconSourceFn
comboBoxModel: d.isCurrentBrowsingTypeAsset ? d.assetComboBoxModel : d.collectibleComboBoxModel
contentIconSize: d.isAsset(d.currentHoldingType) ? d.assetContentIconSize : d.collectibleContentIconSize
contentTextSize: d.isAsset(d.currentHoldingType) ? d.assetContentTextSize : d.collectibleContentTextSize
}
Component {
id: headerComponent
ColumnLayout {
width: holdingItemSelector.comboBoxControl.popup.width
Layout.topMargin: d.headerTopMargin
spacing: -1 // Used to overlap rectangles from row components
StatusTabBar {
id: tabBar
visible: !root.onlyAssets
Layout.preferredHeight: d.tabBarHeight
Layout.fillWidth: true
Layout.leftMargin: d.padding
Layout.rightMargin: d.padding
Layout.topMargin: d.tabBarTopMargin
Layout.bottomMargin: 6
currentIndex: d.holdingTypes.indexOf(d.browsingHoldingType)
onCurrentIndexChanged: {
if (currentIndex >= 0) {
d.browsingHoldingType = d.holdingTypes[currentIndex]
}
}
Repeater {
id: tabLabelsRepeater
model: d.tabsModel
StatusTabButton {
text: modelData
width: implicitWidth
}
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 40
visible: d.isBrowsingCollection
color: "transparent"
border.color: Theme.palette.baseColor2
border.width: 1
RowLayout{
anchors.fill: parent
StatusIconTextButton {
id: backButton
Layout.preferredWidth: d.backButtonWidth
Layout.preferredHeight: d.backButtonHeight
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.leftMargin: d.padding
statusIcon: "previous"
icon.width: 16
icon.height: 16
text: qsTr("Back")
onClicked: {
if (!d.isCurrentBrowsingTypeAsset) {
root.collectiblesModel.currentCollectionUid = ""
}
}
}
StatusBaseText {
Layout.fillWidth: true
Layout.rightMargin: d.padding
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignRight
text: "%1 %2".arg(collectiblesModel.count).arg(d.currentBrowsingCollectionName)
font.pixelSize: 13
lineHeight: 18
lineHeightMode: Text.FixedHeight
color: Theme.palette.baseColor1
}
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: searchInput.input.implicitHeight
color: "transparent"
border.color: Theme.palette.baseColor2
border.width: 1
StatusInput {
id: searchInput
anchors.fill: parent
input.showBackground: false
placeholderText: d.searchPlaceholderText
onTextChanged: Qt.callLater(d.updateSearchText, text)
input.clearable: true
input.implicitHeight: 56
input.rightComponent: StatusFlatRoundButton {
icon.name: "search"
type: StatusFlatRoundButton.Type.Secondary
enabled: false
}
}
}
}
}
Component {
id: assetComboBoxDelegate
TokenBalancePerChainDelegate {
objectName: "AssetSelector_ItemDelegate_" + symbol
width: holdingItemSelector.comboBoxControl.popup.width
getNetworkIcon: root.getNetworkIcon
onTokenSelected: {
holdingItemSelector.selectedItem = selectedToken
d.currentHoldingType = Constants.HoldingType.Asset
root.itemSelected(selectedToken.symbol, Constants.HoldingType.Asset)
holdingItemSelector.comboBoxControl.popup.close()
}
}
}
Component {
id: collectibleComboBoxDelegate
CollectibleNestedDelegate {
objectName: "CollectibleSelector_ItemDelegate_" + collectionUid
width: holdingItemSelector.comboBoxControl.popup.width
getNetworkIcon: root.getNetworkIcon
onItemSelected: {
if (isCollection) {
d.currentBrowsingCollectionName = collectionName
root.collectiblesModel.currentCollectionUid = collectionUid
} else {
holdingItemSelector.selectedItem = selectedItem
d.currentHoldingType = Constants.HoldingType.Collectible
root.itemSelected(selectedItem.uid, Constants.HoldingType.Collectible)
holdingItemSelector.comboBoxControl.popup.close()
}
}
}
}
}

View File

@ -22,6 +22,9 @@ Separator 1.0 Separator.qml
SeparatorWithIcon 1.0 SeparatorWithIcon.qml SeparatorWithIcon 1.0 SeparatorWithIcon.qml
SequenceColumnLayout 1.0 SequenceColumnLayout.qml SequenceColumnLayout 1.0 SequenceColumnLayout.qml
SplitViewHandle 1.0 SplitViewHandle.qml SplitViewHandle 1.0 SplitViewHandle.qml
HoldingItemSelector 1.0 HoldingItemSelector.qml
HoldingSelector 1.0 HoldingSelector.qml
HoldingTypes 1.0 HoldingTypes.qml
StatusAssetSelector 1.0 StatusAssetSelector.qml StatusAssetSelector 1.0 StatusAssetSelector.qml
StyledText 1.0 StyledText.qml StyledText 1.0 StyledText.qml
TextWithLabel 1.0 TextWithLabel.qml TextWithLabel 1.0 TextWithLabel.qml

View File

@ -28,14 +28,19 @@ StatusDialog {
property string preSelectedRecipient property string preSelectedRecipient
property string preDefinedAmountToSend property string preDefinedAmountToSend
property var preSelectedAsset property var preSelectedHolding
property string preSelectedHoldingID
property var preSelectedHoldingType
property bool interactive: true property bool interactive: true
property alias onlyAssets: holdingSelector.onlyAssets
property alias modalHeader: modalHeader.text property alias modalHeader: modalHeader.text
property var store: TransactionStore{} property var store: TransactionStore{}
property var currencyStore: store.currencyStore property var currencyStore: store.currencyStore
property var selectedAccount: store.selectedSenderAccount property var selectedAccount: store.selectedSenderAccount
property var collectiblesModel: store.collectiblesModel
property var nestedCollectiblesModel: store.nestedCollectiblesModel
property var bestRoutes property var bestRoutes
property alias addressText: recipientLoader.addressText property alias addressText: recipientLoader.addressText
property bool isLoading: false property bool isLoading: false
@ -59,9 +64,9 @@ StatusDialog {
} }
property var recalculateRoutesAndFees: Backpressure.debounce(popup, 600, function() { property var recalculateRoutesAndFees: Backpressure.debounce(popup, 600, function() {
if(!!popup.selectedAccount && !!assetSelector.selectedAsset && recipientLoader.ready && amountToSendInput.inputNumberValid) { if(!!popup.selectedAccount && d.isSelectedHoldingValidAsset && recipientLoader.ready && amountToSendInput.inputNumberValid) {
popup.isLoading = true popup.isLoading = true
let amount = Math.round(amountToSendInput.cryptoValueToSend * Math.pow(10, assetSelector.selectedAsset.decimals)) let amount = Math.round(amountToSendInput.cryptoValueToSend * Math.pow(10, d.selectedHolding.decimals))
popup.store.suggestedRoutes(amount.toString(16), popup.sendType) popup.store.suggestedRoutes(amount.toString(16), popup.sendType)
} }
}) })
@ -72,8 +77,8 @@ StatusDialog {
(popup.bestRoutes && popup.bestRoutes.count === 0 && (popup.bestRoutes && popup.bestRoutes.count === 0 &&
!!amountToSendInput.input.text && recipientLoader.ready && !popup.isLoading) ? !!amountToSendInput.input.text && recipientLoader.ready && !popup.isLoading) ?
Constants.NoRoute : Constants.NoError Constants.NoRoute : Constants.NoError
readonly property double maxFiatBalance: !!assetSelector.selectedAsset ? assetSelector.selectedAsset.totalCurrencyBalance.amount : 0 readonly property double maxFiatBalance: isSelectedHoldingValidAsset ? selectedHolding.totalCurrencyBalance.amount : 0
readonly property double maxCryptoBalance: !!assetSelector.selectedAsset ? assetSelector.selectedAsset.totalBalance.amount : 0 readonly property double maxCryptoBalance: isSelectedHoldingValidAsset ? selectedHolding.totalBalance.amount : 0
readonly property double maxInputBalance: amountToSendInput.inputIsFiat ? maxFiatBalance : maxCryptoBalance readonly property double maxInputBalance: amountToSendInput.inputIsFiat ? maxFiatBalance : maxCryptoBalance
readonly property string selectedSymbol: store.selectedAssetSymbol readonly property string selectedSymbol: store.selectedAssetSymbol
readonly property string inputSymbol: amountToSendInput.inputIsFiat ? popup.currencyStore.currentCurrency : selectedSymbol readonly property string inputSymbol: amountToSendInput.inputIsFiat ? popup.currencyStore.currentCurrency : selectedSymbol
@ -83,6 +88,37 @@ StatusDialog {
property string totalTimeEstimate property string totalTimeEstimate
property double totalFeesInFiat property double totalFeesInFiat
property double totalAmountToReceive property double totalAmountToReceive
property var selectedHolding: null
property var selectedHoldingType: Constants.HoldingType.Unknown
readonly property bool isSelectedHoldingValidAsset: !!selectedHolding && selectedHoldingType === Constants.HoldingType.Asset
property var hoveredHolding: null
property var hoveredHoldingType: Constants.HoldingType.Unknown
readonly property bool isHoveredHoldingValidAsset: !!hoveredHolding && hoveredHoldingType === Constants.HoldingType.Asset
function setSelectedHoldingId(holdingId, holdingType) {
let holding = store.getHolding(holdingId, holdingType)
setSelectedHolding(holding, holdingType)
}
function setSelectedHolding(holding, holdingType) {
d.selectedHolding = holding
d.selectedHoldingType = holdingType
let selectorHolding = store.holdingToSelectorHolding(holding, holdingType)
holdingSelector.setSelectedItem(selectorHolding, holdingType)
}
function setHoveredHoldingId(holdingId, holdingType) {
let holding = store.getHolding(holdingId, holdingType)
setHoveredHolding(holding, holdingType)
}
function setHoveredHolding(holding, holdingType) {
d.hoveredHolding = holding
d.hoveredHoldingType = holdingType
let selectorHolding = store.holdingToSelectorHolding(holding, holdingType)
holdingSelector.setHoveredItem(selectorHolding, holdingType)
}
} }
width: 556 width: 556
@ -99,8 +135,12 @@ StatusDialog {
onOpened: { onOpened: {
amountToSendInput.input.input.edit.forceActiveFocus() amountToSendInput.input.input.edit.forceActiveFocus()
if(!!popup.preSelectedAsset) { if (popup.preSelectedHoldingType !== Constants.HoldingType.Unknown) {
assetSelector.selectedAsset = popup.preSelectedAsset if(!!popup.preSelectedHolding) {
d.setSelectedHolding(popup.preSelectedHolding, popup.preSelectedHoldingType)
} else if (!!popup.preSelectedHoldingID) {
d.setSelectedHoldingId(popup.preSelectedHoldingID, popup.preSelectedHoldingType)
}
} }
if(!!popup.preDefinedAmountToSend) { if(!!popup.preDefinedAmountToSend) {
@ -118,8 +158,6 @@ StatusDialog {
} }
} }
onClosed: popup.store.resetTxStoreProperties()
header: AccountsModalHeader { header: AccountsModalHeader {
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: -height - 18 anchors.topMargin: -height - 18
@ -176,6 +214,7 @@ StatusDialog {
StatusBaseText { StatusBaseText {
id: modalHeader id: modalHeader
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
verticalAlignment: Text.AlignVCenter
text: popup.isBridgeTx ? qsTr("Bridge") : qsTr("Send") text: popup.isBridgeTx ? qsTr("Bridge") : qsTr("Send")
font.pixelSize: 28 font.pixelSize: 28
lineHeight: 38 lineHeight: 38
@ -184,45 +223,29 @@ StatusDialog {
color: Theme.palette.directColor1 color: Theme.palette.directColor1
Layout.maximumWidth: contentWidth Layout.maximumWidth: contentWidth
} }
StatusAssetSelector { HoldingSelector {
id: assetSelector id: holdingSelector
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignTop | Qt.AlignLeft Layout.fillHeight: true
enabled: popup.interactive assetsModel: popup.selectedAccount && popup.selectedAccount.assets ? popup.selectedAccount.assets : null
assets: popup.selectedAccount && popup.selectedAccount.assets ? popup.selectedAccount.assets : null collectiblesModel: popup.selectedAccount ? popup.nestedCollectiblesModel : null
defaultToken: Style.png("tokens/DEFAULT-TOKEN@3x")
placeholderText: qsTr("Select token")
currentCurrencySymbol: RootStore.currencyStore.currentCurrencySymbol currentCurrencySymbol: RootStore.currencyStore.currentCurrencySymbol
tokenAssetSourceFn: function (symbol) { visible: !!d.selectedHolding || !!d.hoveredHolding
return symbol ? Style.png("tokens/%1".arg(symbol)) : defaultToken
}
searchTokenSymbolByAddressFn: function (address) {
return store.findTokenSymbolByAddress(address)
}
getNetworkIcon: function(chainId){ getNetworkIcon: function(chainId){
return RootStore.getNetworkIcon(chainId) return RootStore.getNetworkIcon(chainId)
} }
onAssetsChanged: { onItemSelected: {
// Todo we should not need to do this, this should be automatic when selected account changes d.setSelectedHoldingId(holdingId, holdingType)
if(!!selectedAccount && !!assetSelector.selectedAsset)
assetSelector.selectedAsset = store.getAsset(selectedAccount.assets, assetSelector.selectedAsset.symbol)
} }
onSelectedAssetChanged: {
store.setSelectedAssetSymbol(assetSelector.selectedAsset.symbol)
if (!assetSelector.selectedAsset || !amountToSendInput.inputNumberValid) {
return
}
popup.recalculateRoutesAndFees()
}
visible: !!assetSelector.selectedAsset || !!assetSelector.hoveredToken
} }
StatusListItemTag { StatusListItemTag {
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
Layout.preferredHeight: 22 Layout.preferredHeight: 22
visible: !!assetSelector.selectedAsset || !!assetSelector.hoveredToken visible: d.isSelectedHoldingValidAsset || d.isHoveredHoldingValidAsset
title: { title: {
if(!!assetSelector.hoveredToken) { if(d.isHoveredHoldingValidAsset) {
const balance = popup.currencyStore.formatCurrencyAmount((amountToSendInput.inputIsFiat ? assetSelector.hoveredToken.totalCurrencyBalance.amount : assetSelector.hoveredToken.totalBalance.amount) , assetSelector.hoveredToken.symbol) const balance = popup.currencyStore.formatCurrencyAmount((amountToSendInput.inputIsFiat ? d.hoveredHolding.totalCurrencyBalance.amount : d.hoveredHolding.totalBalance.amount) , d.hoveredHolding.symbol)
return qsTr("Max: %1").arg(balance) return qsTr("Max: %1").arg(balance)
} }
if (d.maxInputBalance <= 0) if (d.maxInputBalance <= 0)
@ -245,7 +268,7 @@ StatusDialog {
Layout.fillWidth: true Layout.fillWidth: true
visible: !assetSelector.selectedAsset visible: !d.selectedHolding
assets: popup.selectedAccount && popup.selectedAccount.assets ? popup.selectedAccount.assets : null assets: popup.selectedAccount && popup.selectedAccount.assets ? popup.selectedAccount.assets : null
searchTokenSymbolByAddressFn: function (address) { searchTokenSymbolByAddressFn: function (address) {
return store.findTokenSymbolByAddress(address) return store.findTokenSymbolByAddress(address)
@ -254,17 +277,18 @@ StatusDialog {
return RootStore.getNetworkIcon(chainId) return RootStore.getNetworkIcon(chainId)
} }
onTokenSelected: { onTokenSelected: {
assetSelector.selectedAsset = selectedToken d.setSelectedHoldingId(symbol, Constants.HoldingType.Asset)
} }
onTokenHovered: { onTokenHovered: {
if(hovered) if(hovered) {
assetSelector.hoveredToken = selectedToken d.setHoveredHoldingId(symbol, Constants.HoldingType.Asset)
else } else {
assetSelector.hoveredToken = null d.setHoveredHoldingId("", Constants.HoldingType.Unknown)
}
} }
} }
RowLayout { RowLayout {
visible: !!assetSelector.selectedAsset visible: d.isSelectedHoldingValidAsset
AmountToSend { AmountToSend {
id: amountToSendInput id: amountToSendInput
Layout.fillWidth:true Layout.fillWidth:true
@ -342,7 +366,7 @@ StatusDialog {
anchors.right: parent.right anchors.right: parent.right
anchors.leftMargin: Style.current.bigPadding anchors.leftMargin: Style.current.bigPadding
anchors.rightMargin: Style.current.bigPadding anchors.rightMargin: Style.current.bigPadding
visible: !isBridgeTx && !!assetSelector.selectedAsset visible: !isBridgeTx && !!d.selectedHolding
StatusBaseText { StatusBaseText {
id: label id: label
elide: Text.ElideRight elide: Text.ElideRight
@ -356,7 +380,7 @@ StatusDialog {
store: popup.store store: popup.store
isBridgeTx: popup.isBridgeTx isBridgeTx: popup.isBridgeTx
interactive: popup.interactive interactive: popup.interactive
selectedAsset: assetSelector.selectedAsset selectedAsset: d.selectedHolding
onIsLoading: popup.isLoading = true onIsLoading: popup.isLoading = true
onRecalculateRoutesAndFees: popup.recalculateRoutesAndFees() onRecalculateRoutesAndFees: popup.recalculateRoutesAndFees()
} }
@ -374,7 +398,7 @@ StatusDialog {
recipientLoader.selectedRecipientType = type recipientLoader.selectedRecipientType = type
recipientLoader.selectedRecipient = recipient recipientLoader.selectedRecipient = recipient
} }
visible: !recipientLoader.ready && !isBridgeTx && !!assetSelector.selectedAsset visible: !recipientLoader.ready && !isBridgeTx && !!d.selectedHolding
} }
NetworkSelector { NetworkSelector {
@ -390,9 +414,9 @@ StatusDialog {
amountToSend: amountToSendInput.cryptoValueToSend amountToSend: amountToSendInput.cryptoValueToSend
minSendCryptoDecimals: amountToSendInput.minSendCryptoDecimals minSendCryptoDecimals: amountToSendInput.minSendCryptoDecimals
minReceiveCryptoDecimals: amountToSendInput.minReceiveCryptoDecimals minReceiveCryptoDecimals: amountToSendInput.minReceiveCryptoDecimals
selectedAsset: assetSelector.selectedAsset selectedAsset: d.selectedHolding
onReCalculateSuggestedRoute: popup.recalculateRoutesAndFees() onReCalculateSuggestedRoute: popup.recalculateRoutesAndFees()
visible: recipientLoader.ready && !!assetSelector.selectedAsset && amountToSendInput.inputNumberValid visible: recipientLoader.ready && !!d.selectedHolding && amountToSendInput.inputNumberValid
errorType: d.errorType errorType: d.errorType
isLoading: popup.isLoading isLoading: popup.isLoading
isBridgeTx: popup.isBridgeTx isBridgeTx: popup.isBridgeTx
@ -404,7 +428,7 @@ StatusDialog {
anchors.right: parent.right anchors.right: parent.right
anchors.leftMargin: Style.current.bigPadding anchors.leftMargin: Style.current.bigPadding
anchors.rightMargin: Style.current.bigPadding anchors.rightMargin: Style.current.bigPadding
visible: recipientLoader.ready && !!assetSelector.selectedAsset && networkSelector.advancedOrCustomMode && amountToSendInput.inputNumberValid visible: recipientLoader.ready && !!d.selectedHolding && networkSelector.advancedOrCustomMode && amountToSendInput.inputNumberValid
selectedTokenSymbol: d.selectedSymbol selectedTokenSymbol: d.selectedSymbol
isLoading: popup.isLoading isLoading: popup.isLoading
bestRoutes: popup.bestRoutes bestRoutes: popup.bestRoutes
@ -439,7 +463,7 @@ StatusDialog {
d.totalTimeEstimate = popup.store.getLabelForEstimatedTxTime(gasTimeEstimate.totalTime) d.totalTimeEstimate = popup.store.getLabelForEstimatedTxTime(gasTimeEstimate.totalTime)
d.totalFeesInFiat = popup.currencyStore.getFiatValue( gasTimeEstimate.totalFeesInEth, "ETH", popup.currencyStore.currentCurrency) + d.totalFeesInFiat = popup.currencyStore.getFiatValue( gasTimeEstimate.totalFeesInEth, "ETH", popup.currencyStore.currentCurrency) +
popup.currencyStore.getFiatValue(gasTimeEstimate.totalTokenFees, fees.selectedTokenSymbol, popup.currencyStore.currentCurrency) popup.currencyStore.getFiatValue(gasTimeEstimate.totalTokenFees, fees.selectedTokenSymbol, popup.currencyStore.currentCurrency)
d.totalAmountToReceive = popup.store.getWei2Eth(txRoutes.amountToReceive, assetSelector.selectedAsset.decimals) d.totalAmountToReceive = popup.store.getWei2Eth(txRoutes.amountToReceive, d.selectedHolding.decimals)
networkSelector.toNetworksList = txRoutes.toNetworksModel networkSelector.toNetworksList = txRoutes.toNetworksModel
popup.isLoading = false popup.isLoading = false
} }

View File

@ -202,7 +202,8 @@ Item {
sendType: Constants.SendType.StickersBuy sendType: Constants.SendType.StickersBuy
preSelectedRecipient: root.store.stickersStore.getStickersMarketAddress() preSelectedRecipient: root.store.stickersStore.getStickersMarketAddress()
preDefinedAmountToSend: LocaleUtils.numberToLocaleString(parseFloat(price)) preDefinedAmountToSend: LocaleUtils.numberToLocaleString(parseFloat(price))
preSelectedAsset: store.getAsset(buyStickersModal.store.assets, JSON.parse(root.store.stickersStore.getStatusToken()).symbol) preSelectedHolding: store.getAsset(buyStickersModal.store.assets, JSON.parse(root.store.stickersStore.getStatusToken()).symbol)
preSelectedHoldingType: Constants.HoldingType.Asset
sendTransaction: function() { sendTransaction: function() {
if(bestRoutes.count === 1) { if(bestRoutes.count === 1) {
let path = bestRoutes.firstItem() let path = bestRoutes.firstItem()

View File

@ -71,7 +71,8 @@ ModalPopup {
sendType: Constants.SendType.StickersBuy sendType: Constants.SendType.StickersBuy
preSelectedRecipient: stickerPackDetailsPopup.store.stickersStore.getStickersMarketAddress() preSelectedRecipient: stickerPackDetailsPopup.store.stickersStore.getStickersMarketAddress()
preDefinedAmountToSend: LocaleUtils.numberToLocaleString(parseFloat(price)) preDefinedAmountToSend: LocaleUtils.numberToLocaleString(parseFloat(price))
preSelectedAsset: store.getAsset(buyStickersPackModal.store.assets, JSON.parse(stickerPackDetailsPopup.store.stickersStore.getStatusToken()).symbol) preSelectedHolding: store.getAsset(buyStickersPackModal.store.assets, JSON.parse(stickerPackDetailsPopup.store.stickersStore.getStatusToken()).symbol)
preSelectedHoldingType: Constants.HoldingType.Asset
sendTransaction: function() { sendTransaction: function() {
if(bestRoutes.count === 1) { if(bestRoutes.count === 1) {
let path = bestRoutes.firstItem() let path = bestRoutes.firstItem()

View File

@ -6,6 +6,8 @@ import shared.stores 1.0
import utils 1.0 import utils 1.0
import StatusQ.Core.Utils 0.1
QtObject { QtObject {
id: root id: root
@ -19,6 +21,8 @@ QtObject {
property var senderAccounts: walletSectionSendInst.senderAccounts property var senderAccounts: walletSectionSendInst.senderAccounts
property var selectedSenderAccount: walletSectionSendInst.selectedSenderAccount property var selectedSenderAccount: walletSectionSendInst.selectedSenderAccount
property var accounts: walletSectionSendInst.accounts property var accounts: walletSectionSendInst.accounts
property var collectiblesModel: walletSectionSendInst.collectiblesModel
property var nestedCollectiblesModel: walletSectionSendInst.nestedCollectiblesModel
property bool areTestNetworksEnabled: networksModule.areTestNetworksEnabled property bool areTestNetworksEnabled: networksModule.areTestNetworksEnabled
property var tmpActivityController: walletSection.tmpActivityController property var tmpActivityController: walletSection.tmpActivityController
property var savedAddressesModel: SortFilterProxyModel { property var savedAddressesModel: SortFilterProxyModel {
@ -105,6 +109,68 @@ QtObject {
return {} return {}
} }
function getCollectible(uid) {
const idx = ModelUtils.indexOf(collectiblesModel, "uid", uid)
if (idx < 0) {
return {}
}
return ModelUtils.get(collectiblesModel, idx)
}
function getSelectorCollectible(uid) {
const idx = ModelUtils.indexOf(nestedCollectiblesModel, "uid", uid)
if (idx < 0) {
return {}
}
return ModelUtils.get(nestedCollectiblesModel, idx)
}
function getHolding(holdingId, holdingType) {
if (holdingType === Constants.HoldingType.Asset) {
return getAsset(selectedSenderAccount.assets, holdingId)
} else if (holdingType === Constants.HoldingType.Collectible) {
return getCollectible(holdingId)
} else {
return {}
}
}
function getSelectorHolding(holdingId, holdingType) {
if (holdingType === Constants.HoldingType.Asset) {
return getAsset(selectedSenderAccount.assets, holdingId)
} else if (holdingType === Constants.HoldingType.Collectible) {
return getSelectorCollectible(holdingId)
} else {
return {}
}
}
function assetToSelectorAsset(asset) {
return asset
}
function collectibleToSelectorCollectible(collectible) {
return {
uid: collectible.uid,
chainId: collectible.chainId,
name: collectible.name,
iconUrl: collectible.imageUrl,
collectionUid: collectible.collectionUid,
collectionName: collectible.collectionName,
isCollection: false
}
}
function holdingToSelectorHolding(holding, holdingType) {
if (holdingType === Constants.HoldingType.Asset) {
return assetToSelectorAsset(holding)
} else if (holdingType === Constants.HoldingType.Collectible) {
return collectibleToSelectorCollectible(holding)
} else {
return {}
}
}
function switchSenderAccount(index) { function switchSenderAccount(index) {
walletSectionSendInst.switchSenderAccount(index) walletSectionSendInst.switchSenderAccount(index)
} }

View File

@ -16,8 +16,8 @@ Item {
id: root id: root
property var assets: null property var assets: null
signal tokenSelected(var selectedToken) signal tokenSelected(string symbol)
signal tokenHovered(var selectedToken, bool hovered) signal tokenHovered(string symbol, bool hovered)
property var searchTokenSymbolByAddressFn: function (address) { property var searchTokenSymbolByAddressFn: function (address) {
return "" return ""
} }
@ -84,8 +84,8 @@ Item {
delegate: TokenBalancePerChainDelegate { delegate: TokenBalancePerChainDelegate {
width: ListView.view.width width: ListView.view.width
getNetworkIcon: root.getNetworkIcon getNetworkIcon: root.getNetworkIcon
onTokenSelected: root.tokenSelected(selectedToken) onTokenSelected: root.tokenSelected(symbol)
onTokenHovered: root.tokenHovered(selectedToken, hovered) onTokenHovered: root.tokenHovered(symbol, hovered)
} }
} }
} }

View File

@ -1211,4 +1211,8 @@ QtObject {
Link = 0, Link = 0,
Image = 1 Image = 1
} }
enum HoldingType {
Unknown, Asset, Collectible
}
} }