fix(wallet): Added adaptor to properly refresh send modal total balances (#15409)
This commit is contained in:
parent
c1cc74750f
commit
e199e7f9da
|
@ -0,0 +1,240 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
import StatusQ.Models 0.1
|
||||||
|
|
||||||
|
import Storybook 1.0
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
import shared.popups.send.models 1.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: listModel
|
||||||
|
|
||||||
|
readonly property var data: [
|
||||||
|
{
|
||||||
|
tokensKey: "key_ETH",
|
||||||
|
name: "Ether",
|
||||||
|
symbol: "ETH",
|
||||||
|
addressPerChain: [
|
||||||
|
{ chainId: 1, address: "0x0000000000000000000000000000000000000000"},
|
||||||
|
{ chainId: 5, address: "0x0000000000000000000000000000000000000000"},
|
||||||
|
{ chainId: 10, address: "0x0000000000000000000000000000000000000000"},
|
||||||
|
{ chainId: 420, address: "0x0000000000000000000000000000000000000000"},
|
||||||
|
{ chainId: 42161, address: "0x0000000000000000000000000000000000000000"},
|
||||||
|
{ chainId: 421613, address: "0x0000000000000000000000000000000000000000"}
|
||||||
|
],
|
||||||
|
balances: [
|
||||||
|
{
|
||||||
|
chainId: "chain_id_1",
|
||||||
|
balance: "186316672770338050",
|
||||||
|
account: "account_1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: "chain_id_1",
|
||||||
|
balance: "386318672772348050",
|
||||||
|
account: "account_2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: "chain_id_2",
|
||||||
|
balance: "186311232772348990",
|
||||||
|
account: "account_1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: "chain_id_2",
|
||||||
|
balance: "986317232772348990",
|
||||||
|
account: "account_1",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
decimals: 18,
|
||||||
|
communityId: "",
|
||||||
|
communityName: "",
|
||||||
|
communityImage: Qt.resolvedUrl(""),
|
||||||
|
marketDetails: {
|
||||||
|
changePct24hour: -2.1232,
|
||||||
|
currencyPrice: {
|
||||||
|
amount: 3423.23898
|
||||||
|
}
|
||||||
|
},
|
||||||
|
detailsLoading: false,
|
||||||
|
image: Qt.resolvedUrl("")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tokensKey: "key_SNT",
|
||||||
|
name: "Status",
|
||||||
|
symbol: "SNT",
|
||||||
|
addressPerChain: [
|
||||||
|
{ chainId: 1, address: "0x0000000000000000000000000000000000000000"},
|
||||||
|
{ chainId: 5, address: "0x0000000000000000000000000000000000000000"},
|
||||||
|
{ chainId: 10, address: "0x0000000000000000000000000000000000000000"},
|
||||||
|
{ chainId: 420, address: "0x0000000000000000000000000000000000000000"},
|
||||||
|
{ chainId: 42161, address: "0x0000000000000000000000000000000000000000"},
|
||||||
|
{ chainId: 421613, address: "0x0000000000000000000000000000000000000000"}
|
||||||
|
],
|
||||||
|
balances: [
|
||||||
|
{
|
||||||
|
chainId: "chain_id_1",
|
||||||
|
balance: "386316672770338850",
|
||||||
|
account: "account_1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: "chain_id_1",
|
||||||
|
balance: "377778672772348050",
|
||||||
|
account: "account_2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: "chain_id_2",
|
||||||
|
balance: "146311232772348990",
|
||||||
|
account: "account_1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: "chain_id_3",
|
||||||
|
balance: "86317232772348990",
|
||||||
|
account: "account_1",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
decimals: 18,
|
||||||
|
communityId: "",
|
||||||
|
communityName: "",
|
||||||
|
communityImage: Qt.resolvedUrl(""),
|
||||||
|
marketDetails: {
|
||||||
|
changePct24hour: 9.232,
|
||||||
|
currencyPrice: {
|
||||||
|
amount: 33.23898
|
||||||
|
}
|
||||||
|
},
|
||||||
|
detailsLoading: false,
|
||||||
|
image: Qt.resolvedUrl("")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tokensKey: "key_MYASST",
|
||||||
|
name: "Community Asset",
|
||||||
|
symbol: "MYASST",
|
||||||
|
balances: [
|
||||||
|
{
|
||||||
|
chainId: "chain_id_1",
|
||||||
|
balance: "23234",
|
||||||
|
account: "account_1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: "chain_id_1",
|
||||||
|
balance: "63234",
|
||||||
|
account: "account_2",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
decimals: 3,
|
||||||
|
communityId: "0x033f36ccb",
|
||||||
|
communityName: "My Community",
|
||||||
|
communityImage: Constants.tokenIcon("DAI", false),
|
||||||
|
marketDetails: {
|
||||||
|
changePct24hour: 0,
|
||||||
|
currencyPrice: {
|
||||||
|
amount: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
detailsLoading: false,
|
||||||
|
image: Constants.tokenIcon("ZRX", false)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
append(data)
|
||||||
|
|
||||||
|
const accounts = new Set()
|
||||||
|
|
||||||
|
data.forEach(e => e.balances.forEach(
|
||||||
|
e => { accounts.add(e.account) }))
|
||||||
|
|
||||||
|
accountsSelector.model = [...accounts.values()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ManageTokensController {
|
||||||
|
id: manageTokensController
|
||||||
|
|
||||||
|
sourceModel: listModel
|
||||||
|
serializeAsCollectibles: false
|
||||||
|
|
||||||
|
onRequestLoadSettings: {
|
||||||
|
loadingStarted()
|
||||||
|
|
||||||
|
const jsonData = [
|
||||||
|
{
|
||||||
|
"key": "ETH",
|
||||||
|
"position": 1,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "SNT",
|
||||||
|
"position": 2,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "MYASST",
|
||||||
|
"position": 5,
|
||||||
|
"visible": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
loadingFinished(JSON.stringify(jsonData))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SendModalAssetsAdaptor {
|
||||||
|
id: adaptor
|
||||||
|
|
||||||
|
controller: manageTokensController
|
||||||
|
account: accountsSelector.selection[0]
|
||||||
|
tokensModel: listModel
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Label { text: "ACCOUNTS:" }
|
||||||
|
|
||||||
|
CheckBoxFlowSelector {
|
||||||
|
id: accountsSelector
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
initialSelection: true
|
||||||
|
exclusive: true
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
GenericListView {
|
||||||
|
label: "Input model"
|
||||||
|
|
||||||
|
model: listModel
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
skipEmptyRoles: true
|
||||||
|
}
|
||||||
|
|
||||||
|
GenericListView {
|
||||||
|
label: "Adapter's output model"
|
||||||
|
|
||||||
|
model: adaptor.model
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
roles:
|
||||||
|
["key", "error", "currentBalance", "currentCurrencyBalance", "currentBalanceText",
|
||||||
|
"icon", "visible", "marketDetailsAvailable", "marketDetailsLoading",
|
||||||
|
"marketPrice", "marketChangePct24hour", "isCommunityAsset", "balancesModel"]
|
||||||
|
|
||||||
|
skipEmptyRoles: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// category: Adaptors
|
|
@ -13,6 +13,12 @@ Flow {
|
||||||
|
|
||||||
property var selection: []
|
property var selection: []
|
||||||
property bool initialSelection
|
property bool initialSelection
|
||||||
|
property bool exclusive: false
|
||||||
|
|
||||||
|
ButtonGroup {
|
||||||
|
id: checkboxGroup
|
||||||
|
exclusive: root.exclusive
|
||||||
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: repeater
|
id: repeater
|
||||||
|
@ -33,6 +39,7 @@ Flow {
|
||||||
text: modelData
|
text: modelData
|
||||||
checked: root.initialSelection
|
checked: root.initialSelection
|
||||||
onToggled: repeater.update()
|
onToggled: repeater.update()
|
||||||
|
ButtonGroup.group: checkboxGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
onItemAdded: update()
|
onItemAdded: update()
|
||||||
|
|
|
@ -88,26 +88,6 @@ QtObject {
|
||||||
return SQUtils.ModelUtils.get(nestedCollectiblesModel, idx)
|
return SQUtils.ModelUtils.get(nestedCollectiblesModel, idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHolding(holdingId, holdingType) {
|
|
||||||
if (holdingType === Constants.TokenType.ERC20) {
|
|
||||||
return getAsset(processedAssetsModel, holdingId)
|
|
||||||
} else if (holdingType === Constants.TokenType.ERC721) {
|
|
||||||
return getCollectible(holdingId)
|
|
||||||
} else {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSelectorHolding(holdingId, holdingType) {
|
|
||||||
if (holdingType === Constants.TokenType.ERC20) {
|
|
||||||
return getAsset(processedAssetsModel, holdingId)
|
|
||||||
} else if (holdingType === Constants.TokenType.ERC721) {
|
|
||||||
return getSelectorCollectible(holdingId)
|
|
||||||
} else {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function assetToSelectorAsset(asset) {
|
function assetToSelectorAsset(asset) {
|
||||||
return asset
|
return asset
|
||||||
}
|
}
|
||||||
|
@ -254,84 +234,4 @@ QtObject {
|
||||||
property bool showCommunityAssetsInSend: true
|
property bool showCommunityAssetsInSend: true
|
||||||
property bool balanceThresholdEnabled: true
|
property bool balanceThresholdEnabled: true
|
||||||
property real balanceThresholdAmount
|
property real balanceThresholdAmount
|
||||||
|
|
||||||
// Property set from TokenLIstView and HoldingSelector to search token by name, symbol or contract address
|
|
||||||
property string assetSearchString
|
|
||||||
|
|
||||||
// Model prepared to provide filtered and sorted assets as per the advanced Settings in token management
|
|
||||||
property var processedAssetsModel: SortFilterProxyModel {
|
|
||||||
sourceModel: walletAssetStore.groupedAccountAssetsModel
|
|
||||||
proxyRoles: [
|
|
||||||
FastExpressionRole {
|
|
||||||
name: "isCommunityAsset"
|
|
||||||
expression: !!model.communityId
|
|
||||||
expectedRoles: ["communityId"]
|
|
||||||
},
|
|
||||||
FastExpressionRole {
|
|
||||||
name: "currentBalance"
|
|
||||||
expression: __getTotalBalance(model.balances, model.decimals)
|
|
||||||
expectedRoles: ["balances", "decimals", "symbol"]
|
|
||||||
},
|
|
||||||
FastExpressionRole {
|
|
||||||
name: "currentCurrencyBalance"
|
|
||||||
expression: {
|
|
||||||
if (!!model.marketDetails) {
|
|
||||||
return model.currentBalance * model.marketDetails.currencyPrice.amount
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
expectedRoles: ["marketDetails", "currentBalance"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
filters: [
|
|
||||||
FastExpressionFilter {
|
|
||||||
function search(symbol, name, addressPerChain, searchString) {
|
|
||||||
return (
|
|
||||||
symbol.startsWith(searchString.toUpperCase()) ||
|
|
||||||
name.toUpperCase().startsWith(searchString.toUpperCase()) || __searchAddressInList(addressPerChain, searchString)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
expression: search(model.symbol, model.name, model.addressPerChain, root.assetSearchString)
|
|
||||||
expectedRoles: ["symbol", "name", "addressPerChain"]
|
|
||||||
},
|
|
||||||
ValueFilter {
|
|
||||||
roleName: "isCommunityAsset"
|
|
||||||
value: false
|
|
||||||
enabled: !showCommunityAssetsInSend
|
|
||||||
},
|
|
||||||
FastExpressionFilter {
|
|
||||||
expression: {
|
|
||||||
return model.currentCurrencyBalance > balanceThresholdAmount
|
|
||||||
}
|
|
||||||
expectedRoles: ["currentCurrencyBalance"]
|
|
||||||
enabled: balanceThresholdEnabled
|
|
||||||
}
|
|
||||||
]
|
|
||||||
sorters: RoleSorter {
|
|
||||||
roleName: "isCommunityAsset"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Internal function to search token address */
|
|
||||||
function __searchAddressInList(addressPerChain, searchString) {
|
|
||||||
let addressFound = false
|
|
||||||
let tokenAddresses = SQUtils.ModelUtils.modelToFlatArray(addressPerChain, "address")
|
|
||||||
for (let i =0; i< tokenAddresses.length; i++){
|
|
||||||
if(tokenAddresses[i].toUpperCase().startsWith(searchString.toUpperCase())) {
|
|
||||||
addressFound = true
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return addressFound
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Internal function to calculate total balance */
|
|
||||||
function __getTotalBalance(balances, decimals) {
|
|
||||||
let totalBalance = 0
|
|
||||||
for(let i=0; i<balances.count; i++) {
|
|
||||||
let balancePerAddressPerChain = SQUtils.ModelUtils.get(balances, i)
|
|
||||||
totalBalance+=SQUtils.AmountsArithmetic.toNumber(balancePerAddressPerChain.balance, decimals)
|
|
||||||
}
|
|
||||||
return totalBalance
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import AppLayouts.Wallet.controls 1.0
|
||||||
import "./panels"
|
import "./panels"
|
||||||
import "./controls"
|
import "./controls"
|
||||||
import "./views"
|
import "./views"
|
||||||
|
import "./models"
|
||||||
|
|
||||||
StatusDialog {
|
StatusDialog {
|
||||||
id: popup
|
id: popup
|
||||||
|
@ -102,8 +103,18 @@ StatusDialog {
|
||||||
property var hoveredHoldingType: Constants.TokenType.Unknown
|
property var hoveredHoldingType: Constants.TokenType.Unknown
|
||||||
readonly property bool isHoveredHoldingValidAsset: !!hoveredHolding && hoveredHoldingType === Constants.TokenType.ERC20
|
readonly property bool isHoveredHoldingValidAsset: !!hoveredHolding && hoveredHoldingType === Constants.TokenType.ERC20
|
||||||
|
|
||||||
|
function getHolding(holdingId, holdingType) {
|
||||||
|
if (holdingType === Constants.TokenType.ERC20) {
|
||||||
|
return store.getAsset(assetsAdaptor.model, holdingId)
|
||||||
|
} else if (holdingType === Constants.TokenType.ERC721 || holdingType === Constants.TokenType.ERC1155) {
|
||||||
|
return store.getCollectible(holdingId)
|
||||||
|
} else {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function setSelectedHoldingId(holdingId, holdingType) {
|
function setSelectedHoldingId(holdingId, holdingType) {
|
||||||
let holding = store.getHolding(holdingId, holdingType)
|
let holding = getHolding(holdingId, holdingType)
|
||||||
setSelectedHolding(holding, holdingType)
|
setSelectedHolding(holding, holdingType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +126,7 @@ StatusDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setHoveredHoldingId(holdingId, holdingType) {
|
function setHoveredHoldingId(holdingId, holdingType) {
|
||||||
let holding = store.getHolding(holdingId, holdingType)
|
let holding = getHolding(holdingId, holdingType)
|
||||||
setHoveredHolding(holding, holdingType)
|
setHoveredHolding(holding, holdingType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,6 +159,19 @@ StatusDialog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SendModalAssetsAdaptor {
|
||||||
|
id: assetsAdaptor
|
||||||
|
|
||||||
|
controller: popup.store.walletAssetStore.assetsController
|
||||||
|
showCommunityAssets: popup.store.tokensStore.showCommunityAssetsInSend
|
||||||
|
tokensModel: popup.store.walletAssetStore.groupedAccountAssetsModel
|
||||||
|
account: popup.store.selectedSenderAccount.address
|
||||||
|
marketValueThreshold:
|
||||||
|
popup.store.tokensStore.displayAssetsBelowBalance
|
||||||
|
? popup.store.tokensStore.getDisplayAssetsBelowBalanceThresholdDisplayAmount()
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
|
||||||
bottomPadding: 16
|
bottomPadding: 16
|
||||||
padding: 0
|
padding: 0
|
||||||
background: StatusDialogBackground {
|
background: StatusDialogBackground {
|
||||||
|
@ -273,7 +297,8 @@ StatusDialog {
|
||||||
id: holdingSelector
|
id: holdingSelector
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
assetsModel: popup.store.processedAssetsModel
|
// assetsModel: popup.store.processedAssetsModel
|
||||||
|
assetsModel: assetsAdaptor.model
|
||||||
collectiblesModel: popup.preSelectedAccount ? popup.nestedCollectiblesModel : null
|
collectiblesModel: popup.preSelectedAccount ? popup.nestedCollectiblesModel : null
|
||||||
networksModel: popup.store.flatNetworksModel
|
networksModel: popup.store.flatNetworksModel
|
||||||
visible: (!!d.selectedHolding && d.selectedHoldingType !== Constants.TokenType.Unknown) ||
|
visible: (!!d.selectedHolding && d.selectedHoldingType !== Constants.TokenType.Unknown) ||
|
||||||
|
@ -281,7 +306,7 @@ StatusDialog {
|
||||||
onItemSelected: {
|
onItemSelected: {
|
||||||
d.setSelectedHoldingId(holdingId, holdingType)
|
d.setSelectedHoldingId(holdingId, holdingType)
|
||||||
}
|
}
|
||||||
onSearchTextChanged: popup.store.assetSearchString = searchText
|
onSearchTextChanged: assetsAdaptor.assetSearchString = assetSearchString
|
||||||
formatCurrentCurrencyAmount: function(balance){
|
formatCurrentCurrencyAmount: function(balance){
|
||||||
return popup.store.currencyStore.formatCurrencyAmount(balance, popup.store.currencyStore.currentCurrency)
|
return popup.store.currencyStore.formatCurrencyAmount(balance, popup.store.currencyStore.currentCurrency)
|
||||||
}
|
}
|
||||||
|
@ -391,7 +416,7 @@ StatusDialog {
|
||||||
Layout.bottomMargin: Style.current.xlPadding
|
Layout.bottomMargin: Style.current.xlPadding
|
||||||
visible: !d.selectedHolding
|
visible: !d.selectedHolding
|
||||||
|
|
||||||
assets: popup.store.processedAssetsModel
|
assets: assetsAdaptor.model
|
||||||
collectibles: popup.preSelectedAccount ? popup.nestedCollectiblesModel : null
|
collectibles: popup.preSelectedAccount ? popup.nestedCollectiblesModel : null
|
||||||
networksModel: popup.store.flatNetworksModel
|
networksModel: popup.store.flatNetworksModel
|
||||||
onlyAssets: holdingSelector.onlyAssets
|
onlyAssets: holdingSelector.onlyAssets
|
||||||
|
@ -405,7 +430,7 @@ StatusDialog {
|
||||||
d.setHoveredHoldingId("", Constants.TokenType.Unknown)
|
d.setHoveredHoldingId("", Constants.TokenType.Unknown)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onAssetSearchStringChanged: store.assetSearchString = assetSearchString
|
onAssetSearchStringChanged: assetsAdaptor.assetSearchString = assetSearchString
|
||||||
formatCurrentCurrencyAmount: function(balance){
|
formatCurrentCurrencyAmount: function(balance){
|
||||||
return popup.store.currencyStore.formatCurrencyAmount(balance, popup.store.currencyStore.currentCurrency)
|
return popup.store.currencyStore.formatCurrencyAmount(balance, popup.store.currencyStore.currentCurrency)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,199 @@
|
||||||
|
import QtQml 2.15
|
||||||
|
|
||||||
|
import StatusQ 0.1
|
||||||
|
import StatusQ.Models 0.1
|
||||||
|
import StatusQ.Core.Utils 0.1
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
QObject {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// Controller providing information about visibility and order defined
|
||||||
|
// by a user (token management)
|
||||||
|
required property ManageTokensController controller
|
||||||
|
|
||||||
|
property bool showCommunityAssets: false
|
||||||
|
|
||||||
|
property string assetSearchString: ""
|
||||||
|
|
||||||
|
/**
|
||||||
|
Expected model structure:
|
||||||
|
|
||||||
|
Tokens related part:
|
||||||
|
|
||||||
|
tokensKey [string] - unique identifier of a token, e.g "0x3234235"
|
||||||
|
symbol [string] - token's symbol e.g. "ETH" or "SNT"
|
||||||
|
name [string] - token's name e.g. "Ether" or "Dai"
|
||||||
|
image [url] - token's icon for custom tokens
|
||||||
|
decimals [int] - number of decimal places, e.g. 18 for ETH
|
||||||
|
balances [model] - submodel of balances per chain/account
|
||||||
|
chainId [string] - unique identifier of a chain
|
||||||
|
account [string] - unique identifier of an account
|
||||||
|
balance [string] - balance in basic unit as big integer string
|
||||||
|
marketDetails [object] - object holding market details
|
||||||
|
changePct24hour [double] - percentage change of fiat price in last day
|
||||||
|
currencyPrice [object] - object holding fiat price details
|
||||||
|
amount [double] - fiat prace of 1 logical unit of cryptocurrency
|
||||||
|
detailsLoading [bool] - indicatator if market details are ready to use
|
||||||
|
addressPerChain [model] - submodel of addresses per chain
|
||||||
|
chainId [string] - unique identifier of a chain
|
||||||
|
address [string] - address of a token contract
|
||||||
|
|
||||||
|
Community related part (relevant for community minted assets, empty otherwise):
|
||||||
|
|
||||||
|
communityId [string] - unique identifier of a community, e.g. "0x6734235"
|
||||||
|
**/
|
||||||
|
property var tokensModel
|
||||||
|
|
||||||
|
// function formatting tokens balance expressed in a commonly used units,
|
||||||
|
// e.g. 1.2 for 1.2 ETH, according to rules specific for given symbol
|
||||||
|
property var formatBalance:
|
||||||
|
(balance, symbol) => `${balance.toLocaleString(Qt.locale())} ${symbol}`
|
||||||
|
|
||||||
|
// account used for balance calculation
|
||||||
|
property string account: ""
|
||||||
|
|
||||||
|
// threshold below which the token is omitted from the output model
|
||||||
|
property double marketValueThreshold
|
||||||
|
|
||||||
|
/**
|
||||||
|
Model structure:
|
||||||
|
|
||||||
|
All roles from the source model are passed directly to the output model,
|
||||||
|
additionally:
|
||||||
|
|
||||||
|
key [string] - renamed from tokensKey
|
||||||
|
icon [url] - from image or fetched by symbol for well-known tokens
|
||||||
|
currentbalance [double] - tokens balance is the commonly used unit, e.g. 1.2 for 1.2 ETH,
|
||||||
|
computed from balances according to provided criteria
|
||||||
|
currentBalanceText [string] - formatted and localized balance
|
||||||
|
currentCurrencyBalance [double] - tokens fiat balance computed from balance and market price
|
||||||
|
|
||||||
|
marketDetailsAvailable [bool] - specifies if market datails are available for given token
|
||||||
|
marketDetailsLoading [bool] - specifies if market datails are available for given token
|
||||||
|
marketPrice [double] - specifies market price in currently used currency
|
||||||
|
marketChangePct24hour [double] - percentage price change in last 24 hours, e.g. 0.5 for 0.5% of price change
|
||||||
|
balancesModel [model] - filtered balances model by selected account
|
||||||
|
**/
|
||||||
|
readonly property alias model: sfpm
|
||||||
|
|
||||||
|
ObjectProxyModel {
|
||||||
|
id: proxyModel
|
||||||
|
|
||||||
|
sourceModel: root.tokensModel ?? null
|
||||||
|
|
||||||
|
delegate: QObject {
|
||||||
|
readonly property var rootModel: model
|
||||||
|
readonly property bool isCommunityAsset: !!model.communityId
|
||||||
|
readonly property var marketDetails: model.marketDetails
|
||||||
|
|
||||||
|
// Read-only roles exposed to the model:
|
||||||
|
readonly property string key: model.tokensKey
|
||||||
|
|
||||||
|
readonly property double currentBalance: AmountsArithmetic.toNumber(totalBalanceAggregator.value, model.decimals)
|
||||||
|
readonly property double currentCurrencyBalance: currentBalance * marketPrice
|
||||||
|
|
||||||
|
readonly property string currentBalanceText: root.formatBalance(currentBalance, model.symbol)
|
||||||
|
|
||||||
|
readonly property bool marketDetailsAvailable: !isCommunityAsset
|
||||||
|
readonly property bool marketDetailsLoading: model.detailsLoading
|
||||||
|
readonly property real marketPrice: marketDetails.currencyPrice.amount ?? 0
|
||||||
|
readonly property real marketChangePct24hour: marketDetails.changePct24hour ?? 0
|
||||||
|
|
||||||
|
readonly property bool visible: {
|
||||||
|
root.controller.revision
|
||||||
|
|
||||||
|
if (!root.controller.filterAcceptsSymbol(model.symbol))
|
||||||
|
return false
|
||||||
|
|
||||||
|
if (isCommunityAsset) {
|
||||||
|
return root.showCommunityAssets
|
||||||
|
}
|
||||||
|
return currentCurrencyBalance >= root.marketValueThreshold
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property url icon: !!model.image ? model.image : Constants.tokenIcon(model.symbol, false)
|
||||||
|
|
||||||
|
readonly property var balancesModel: filteredBalances
|
||||||
|
|
||||||
|
SortFilterProxyModel {
|
||||||
|
id: filteredBalances
|
||||||
|
|
||||||
|
sourceModel: rootModel.balances
|
||||||
|
|
||||||
|
filters: [
|
||||||
|
FastExpressionFilter {
|
||||||
|
expression: root.account === model.account
|
||||||
|
expectedRoles: ["account"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionAggregator {
|
||||||
|
id: totalBalanceAggregator
|
||||||
|
|
||||||
|
model: filteredBalances
|
||||||
|
initialValue: "0"
|
||||||
|
roleName: "balance"
|
||||||
|
|
||||||
|
aggregateFunction: (aggr, value) => AmountsArithmetic.sum(
|
||||||
|
AmountsArithmetic.fromString(aggr),
|
||||||
|
AmountsArithmetic.fromString(value)).toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedRoles:
|
||||||
|
["tokensKey", "symbol", "image", "balances", "decimals",
|
||||||
|
"detailsLoading", "marketDetails", "communityId", "addressPerChain"]
|
||||||
|
exposedRoles:
|
||||||
|
["key", "error", "currentBalance", "currentCurrencyBalance", "currentBalanceText",
|
||||||
|
"icon", "visible", "marketDetailsAvailable", "marketDetailsLoading",
|
||||||
|
"marketPrice", "marketChangePct24hour", "isCommunityAsset", "balancesModel"]
|
||||||
|
|
||||||
|
|
||||||
|
/* Internal function to search token address */
|
||||||
|
function __searchAddressInList(addressPerChain, searchString) {
|
||||||
|
const uppercaseSearchString = searchString.toUpperCase()
|
||||||
|
let addressFound = false
|
||||||
|
let tokenAddresses = ModelUtils.modelToFlatArray(addressPerChain, "address")
|
||||||
|
for (let i =0; i< tokenAddresses.length; i++){
|
||||||
|
if(tokenAddresses[i].toUpperCase().startsWith(uppercaseSearchString)) {
|
||||||
|
addressFound = true
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addressFound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SortFilterProxyModel {
|
||||||
|
id: sfpm
|
||||||
|
|
||||||
|
sourceModel: proxyModel
|
||||||
|
objectName: "SendModalAssetsAdaptorModel"
|
||||||
|
|
||||||
|
filters: [
|
||||||
|
FastExpressionFilter {
|
||||||
|
function search(symbol, name, addressPerChain, searchString) {
|
||||||
|
const uppercaseSearchString = searchString.toUpperCase()
|
||||||
|
return (
|
||||||
|
symbol.toUpperCase().startsWith(uppercaseSearchString) ||
|
||||||
|
name.toUpperCase().startsWith(uppercaseSearchString) || proxyModel.__searchAddressInList(addressPerChain, searchString)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
expression: search(symbol, name, addressPerChain, root.assetSearchString)
|
||||||
|
expectedRoles: ["symbol", "name", "addressPerChain"]
|
||||||
|
},
|
||||||
|
ValueFilter {
|
||||||
|
roleName: "visible"
|
||||||
|
value: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
sorters: RoleSorter {
|
||||||
|
roleName: "isCommunityAsset"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
SendModalAssetsAdaptor 1.0 SendModalAssetsAdaptor.qml
|
|
@ -211,7 +211,7 @@ Item {
|
||||||
property var totalBalance: model.totalBalance
|
property var totalBalance: model.totalBalance
|
||||||
property var marketDetails: model.marketDetails
|
property var marketDetails: model.marketDetails
|
||||||
property var decimals: model.decimals
|
property var decimals: model.decimals
|
||||||
property var balances: model.balances
|
property var balances: model.balancesModel
|
||||||
// collectible
|
// collectible
|
||||||
property var uid: model.uid
|
property var uid: model.uid
|
||||||
property var iconUrl: model.iconUrl
|
property var iconUrl: model.iconUrl
|
||||||
|
|
|
@ -221,7 +221,7 @@ Item {
|
||||||
width: tokenList.width
|
width: tokenList.width
|
||||||
|
|
||||||
balancesModel: LeftJoinModel {
|
balancesModel: LeftJoinModel {
|
||||||
leftModel: !!model & !!model.balances ? model.balances : null
|
leftModel: !!model & !!model.balancesModel ? model.balancesModel : null
|
||||||
rightModel: root.networksModel
|
rightModel: root.networksModel
|
||||||
joinRole: "chainId"
|
joinRole: "chainId"
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,26 +110,6 @@ QtObject {
|
||||||
return ModelUtils.get(nestedCollectiblesModel, idx)
|
return ModelUtils.get(nestedCollectiblesModel, idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHolding(holdingId, holdingType) {
|
|
||||||
if (holdingType === Constants.TokenType.ERC20) {
|
|
||||||
return getAsset(processedAssetsModel, holdingId)
|
|
||||||
} else if (holdingType === Constants.TokenType.ERC721 || holdingType === Constants.TokenType.ERC1155) {
|
|
||||||
return getCollectible(holdingId)
|
|
||||||
} else {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSelectorHolding(holdingId, holdingType) {
|
|
||||||
if (holdingType === Constants.TokenType.ERC20) {
|
|
||||||
return getAsset(processedAssetsModel, holdingId)
|
|
||||||
} else if (holdingType === Constants.TokenType.ERC721 || holdingType === Constants.TokenType.ERC1155) {
|
|
||||||
return getSelectorCollectible(holdingId)
|
|
||||||
} else {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function assetToSelectorAsset(asset) {
|
function assetToSelectorAsset(asset) {
|
||||||
return asset
|
return asset
|
||||||
}
|
}
|
||||||
|
@ -223,7 +203,6 @@ QtObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetStoredProperties() {
|
function resetStoredProperties() {
|
||||||
assetSearchString = ""
|
|
||||||
walletSectionSendInst.resetStoredProperties()
|
walletSectionSendInst.resetStoredProperties()
|
||||||
nestedCollectiblesModel.currentCollectionUid = ""
|
nestedCollectiblesModel.currentCollectionUid = ""
|
||||||
}
|
}
|
||||||
|
@ -242,112 +221,4 @@ QtObject {
|
||||||
function formatCurrencyAmountFromBigInt(balance, symbol, decimals, options = null) {
|
function formatCurrencyAmountFromBigInt(balance, symbol, decimals, options = null) {
|
||||||
return currencyStore.formatCurrencyAmountFromBigInt(balance, symbol, decimals, options)
|
return currencyStore.formatCurrencyAmountFromBigInt(balance, symbol, decimals, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Property set from TokenLIstView and HoldingSelector to search token by name, symbol or contract address
|
|
||||||
property string assetSearchString
|
|
||||||
|
|
||||||
// Internal model filtering balances by the account selected on the SendModalPage
|
|
||||||
property SubmodelProxyModel __assetsWithFilteredBalances: SubmodelProxyModel {
|
|
||||||
sourceModel: walletAssetStore.groupedAccountAssetsModel
|
|
||||||
submodelRoleName: "balances"
|
|
||||||
delegateModel: SortFilterProxyModel {
|
|
||||||
sourceModel: submodel
|
|
||||||
filters: [
|
|
||||||
ValueFilter {
|
|
||||||
roleName: "account"
|
|
||||||
value: root.selectedSenderAccount.address
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property Connections tokensStoreConnections: Connections {
|
|
||||||
target: tokensStore
|
|
||||||
function onDisplayAssetsBelowBalanceThresholdChanged() {
|
|
||||||
processedAssetsModel.displayAssetsBelowBalanceThresholdAmount = tokensStore.getDisplayAssetsBelowBalanceThresholdDisplayAmount()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Model prepared to provide filtered and sorted assets as per the advanced Settings in token management
|
|
||||||
property var processedAssetsModel: SortFilterProxyModel {
|
|
||||||
property real displayAssetsBelowBalanceThresholdAmount: tokensStore.getDisplayAssetsBelowBalanceThresholdDisplayAmount()
|
|
||||||
sourceModel: __assetsWithFilteredBalances
|
|
||||||
proxyRoles: [
|
|
||||||
FastExpressionRole {
|
|
||||||
name: "isCommunityAsset"
|
|
||||||
expression: !!model.communityId
|
|
||||||
expectedRoles: ["communityId"]
|
|
||||||
},
|
|
||||||
FastExpressionRole {
|
|
||||||
name: "currentBalance"
|
|
||||||
expression: __getTotalBalance(model.balances, model.decimals)
|
|
||||||
expectedRoles: ["balances", "decimals"]
|
|
||||||
},
|
|
||||||
FastExpressionRole {
|
|
||||||
name: "currentCurrencyBalance"
|
|
||||||
expression: {
|
|
||||||
if (!!model.marketDetails) {
|
|
||||||
return model.currentBalance * model.marketDetails.currencyPrice.amount
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
expectedRoles: ["marketDetails", "currentBalance"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
filters: [
|
|
||||||
FastExpressionFilter {
|
|
||||||
function search(symbol, name, addressPerChain, searchString) {
|
|
||||||
return (
|
|
||||||
symbol.toUpperCase().startsWith(searchString.toUpperCase()) ||
|
|
||||||
name.toUpperCase().startsWith(searchString.toUpperCase()) || __searchAddressInList(addressPerChain, searchString)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
expression: search(symbol, name, addressPerChain, root.assetSearchString)
|
|
||||||
expectedRoles: ["symbol", "name", "addressPerChain"]
|
|
||||||
},
|
|
||||||
ValueFilter {
|
|
||||||
roleName: "isCommunityAsset"
|
|
||||||
value: false
|
|
||||||
enabled: !tokensStore.showCommunityAssetsInSend
|
|
||||||
},
|
|
||||||
FastExpressionFilter {
|
|
||||||
expression: {
|
|
||||||
root.walletAssetStore.assetsController.revision
|
|
||||||
|
|
||||||
if (!root.walletAssetStore.assetsController.filterAcceptsSymbol(model.symbol)) // explicitely hidden
|
|
||||||
return false
|
|
||||||
if (tokensStore.displayAssetsBelowBalance)
|
|
||||||
return model.currentCurrencyBalance > processedAssetsModel.displayAssetsBelowBalanceThresholdAmount
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
expectedRoles: ["symbol", "currentCurrencyBalance"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
sorters: RoleSorter {
|
|
||||||
roleName: "isCommunityAsset"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Internal function to search token address */
|
|
||||||
function __searchAddressInList(addressPerChain, searchString) {
|
|
||||||
let addressFound = false
|
|
||||||
let tokenAddresses = ModelUtils.modelToFlatArray(addressPerChain, "address")
|
|
||||||
for (let i =0; i< tokenAddresses.length; i++){
|
|
||||||
if(tokenAddresses[i].toUpperCase().startsWith(searchString.toUpperCase())) {
|
|
||||||
addressFound = true
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return addressFound
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Internal function to calculate total balance */
|
|
||||||
function __getTotalBalance(balances, decimals) {
|
|
||||||
let totalBalance = 0
|
|
||||||
for(let i=0; i<balances.count; i++) {
|
|
||||||
let balancePerAddressPerChain = ModelUtils.get(balances, i)
|
|
||||||
totalBalance+=AmountsArithmetic.toNumber(balancePerAddressPerChain.balance, decimals)
|
|
||||||
}
|
|
||||||
return totalBalance
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue