feat(@desktop/wallet): display balance in collectibles view

Closes #12940
This commit is contained in:
Dario Gabriel Lipicar 2024-03-05 17:16:37 -03:00 committed by dlipicar
parent 96c7795153
commit af84d788ff
7 changed files with 136 additions and 36 deletions

View File

@ -1,4 +1,5 @@
import NimQml, Tables, strutils, strformat import NimQml, Tables, strutils, strformat
import stint
import backend/collectibles_types as backend import backend/collectibles_types as backend
@ -61,7 +62,7 @@ QtObject:
of ModelRole.AccountAddress: of ModelRole.AccountAddress:
result = newQVariant(item.address) result = newQVariant(item.address)
of ModelRole.Balance: of ModelRole.Balance:
result = newQVariant($item.balance) result = newQVariant(item.balance.toString(10))
of ModelRole.TxTimestamp: of ModelRole.TxTimestamp:
result = newQVariant(item.txTimestamp) result = newQVariant(item.txTimestamp)

View File

@ -31,12 +31,12 @@ ListModel {
ownership: [ ownership: [
{ {
accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240",
balance: 1, balance: "1",
txTimestamp: 1 txTimestamp: 1
}, },
{ {
accountAddress: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", accountAddress: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881",
balance: 1, balance: "1",
txTimestamp: 2 txTimestamp: 2
}, },
] ]
@ -56,7 +56,7 @@ ListModel {
ownership: [ ownership: [
{ {
accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240",
balance: 1, balance: "8",
txTimestamp: 3 txTimestamp: 3
}, },
] ]
@ -76,7 +76,7 @@ ListModel {
ownership: [ ownership: [
{ {
accountAddress: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", accountAddress: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881",
balance: 1, balance: "1",
txTimestamp: 3 txTimestamp: 3
}, },
] ]
@ -96,7 +96,7 @@ ListModel {
ownership: [ ownership: [
{ {
accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240",
balance: 1, balance: "1",
txTimestamp: 6 txTimestamp: 6
}, },
] ]
@ -116,12 +116,12 @@ ListModel {
ownership: [ ownership: [
{ {
accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240",
balance: 1, balance: "1",
txTimestamp: 50 txTimestamp: 50
}, },
{ {
accountAddress: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", accountAddress: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881",
balance: 1, balance: "1",
txTimestamp: 10 txTimestamp: 10
}, },
] ]
@ -141,7 +141,7 @@ ListModel {
ownership: [ ownership: [
{ {
accountAddress: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", accountAddress: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881",
balance: 1, balance: "1",
txTimestamp: 16 txTimestamp: 16
}, },
] ]
@ -161,7 +161,7 @@ ListModel {
ownership: [ ownership: [
{ {
accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240",
balance: 1, balance: "1",
txTimestamp: 19 txTimestamp: 19
}, },
] ]
@ -184,7 +184,7 @@ ListModel {
ownership: [ ownership: [
{ {
accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240",
balance: 1, balance: "15",
txTimestamp: 20 txTimestamp: 20
}, },
] ]
@ -204,7 +204,7 @@ ListModel {
ownership: [ ownership: [
{ {
accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240",
balance: 1, balance: "4",
txTimestamp: 21 txTimestamp: 21
}, },
] ]
@ -224,7 +224,7 @@ ListModel {
ownership: [ ownership: [
{ {
accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240",
balance: 1, balance: "1",
txTimestamp: 22 txTimestamp: 22
}, },
] ]
@ -244,7 +244,7 @@ ListModel {
ownership: [ ownership: [
{ {
accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240",
balance: 1, balance: "1",
txTimestamp: 23 txTimestamp: 23
}, },
] ]
@ -264,7 +264,7 @@ ListModel {
ownership: [ ownership: [
{ {
accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240",
balance: 1, balance: "1000",
txTimestamp: 25 txTimestamp: 25
}, },
] ]
@ -284,7 +284,7 @@ ListModel {
ownership: [ ownership: [
{ {
accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240",
balance: 1, balance: "1",
txTimestamp: 26 txTimestamp: 26
}, },
] ]
@ -304,9 +304,14 @@ ListModel {
ownership: [ ownership: [
{ {
accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240",
balance: 1, balance: "60",
txTimestamp: 27 txTimestamp: 27
}, },
{
accountAddress: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881",
balance: "70",
txTimestamp: 60
},
] ]
}, },
{ {

View File

@ -123,6 +123,16 @@ QtObject {
return divident.div(divisor) return divident.div(divisor)
} }
/*!
\qmlmethod AmountsArithmetic::sum(amount1, amount2)
\brief Returns a Big number whose value is the sum of amount1 and amount2.
*/
function sum(amount1, amount2) {
console.assert(amount1 instanceof Big.Big)
console.assert(amount2 instanceof Big.Big)
return amount1.plus(amount2)
}
/*! /*!
\qmlmethod AmountsArithmetic::cmp(amount1, amount2) \qmlmethod AmountsArithmetic::cmp(amount1, amount2)
\brief Compares two amounts. \brief Compares two amounts.

View File

@ -0,0 +1,35 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
Control {
id: root
property int balance: 1
implicitHeight: 24
horizontalPadding: 8
background: Rectangle {
color: Theme.palette.indirectColor4
radius: height / 2
}
contentItem: StatusBaseText {
color: Theme.palette.directColor1
font.pixelSize: 10
font.family: Theme.palette.baseFont.name
text: {
if (root.balance > 99) {
return "99+"
} else {
return root.balance
}
}
verticalAlignment: Text.AlignVCenter
}
}

View File

@ -12,3 +12,4 @@ ManageTokensDelegate 1.0 ManageTokensDelegate.qml
ManageTokensGroupDelegate 1.0 ManageTokensGroupDelegate.qml ManageTokensGroupDelegate 1.0 ManageTokensGroupDelegate.qml
InformationTileAssetDetails 1.0 InformationTileAssetDetails.qml InformationTileAssetDetails 1.0 InformationTileAssetDetails.qml
StatusNetworkListItemTag 1.0 StatusNetworkListItemTag.qml StatusNetworkListItemTag 1.0 StatusNetworkListItemTag.qml
CollectibleBalanceTag 1.0 CollectibleBalanceTag.qml

View File

@ -10,7 +10,6 @@ import StatusQ.Controls 0.1
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1 import StatusQ.Core.Utils 0.1
import StatusQ.Internal 0.1
import StatusQ.Models 0.1 import StatusQ.Models 0.1
import StatusQ.Popups 0.1 import StatusQ.Popups 0.1
import StatusQ.Popups.Dialog 0.1 import StatusQ.Popups.Dialog 0.1
@ -124,27 +123,44 @@ ColumnLayout {
readonly property var nwFilters: root.networkFilters.split(":") readonly property var nwFilters: root.networkFilters.split(":")
readonly property var addrFilters: root.addressFilters.split(":").map((addr) => addr.toLowerCase()) readonly property var addrFilters: root.addressFilters.split(":").map((addr) => addr.toLowerCase())
function containsAnyAddress(ownership, filterList) {
for (let i = 0; i < ownership.count; i++) {
let accountAddress = ModelUtils.get(ownership, i, "accountAddress").toLowerCase()
if (filterList.includes(accountAddress)) {
return true
}
}
return false
}
function getLatestTimestmap(ownership, filterList) { function getLatestTimestmap(ownership, filterList) {
let latest = 0 let latest = 0
for (let i = 0; i < ownership.count; i++) {
let accountAddress = ModelUtils.get(ownership, i, "accountAddress").toLowerCase() if (!!ownership) {
if (filterList.includes(accountAddress)) { for (let i = 0; i < ownership.count; i++) {
let txTimestamp = ModelUtils.get(ownership, i, "txTimestamp") let accountAddress = ModelUtils.get(ownership, i, "accountAddress").toLowerCase()
latest = Math.max(latest, txTimestamp) if (filterList.includes(accountAddress)) {
let txTimestamp = ModelUtils.get(ownership, i, "txTimestamp")
latest = Math.max(latest, txTimestamp)
}
} }
} }
return latest return latest
} }
function getBalance(ownership, filterList) {
// Balance is a Uint256, so we need to use AmountsArithmetic to handle it
let balance = AmountsArithmetic.fromNumber(0)
if (!!ownership) {
for (let i = 0; i < ownership.count; i++) {
let accountAddress = ModelUtils.get(ownership, i, "accountAddress").toLowerCase()
if (filterList.includes(accountAddress)) {
let tokenBalanceStr = ModelUtils.get(ownership, i, "balance")+""
if (tokenBalanceStr !== "") {
let tokenBalance = AmountsArithmetic.fromString(tokenBalanceStr)
balance = AmountsArithmetic.sum(balance, tokenBalance)
}
}
}
// For simplicity, we limit the result to the maximum int manageable by QML
const maxInt = 2147483647
if (AmountsArithmetic.cmp(balance, AmountsArithmetic.fromNumber(maxInt)) === 1) {
return maxInt
}
}
return AmountsArithmetic.toNumber(balance)
}
} }
component CustomSFPM: SortFilterProxyModel { component CustomSFPM: SortFilterProxyModel {
@ -157,6 +173,11 @@ ColumnLayout {
name: "groupName" name: "groupName"
roleNames: ["collectionName", "communityName"] roleNames: ["collectionName", "communityName"]
}, },
FastExpressionRole {
name: "balance"
expression: d.addrFilters, d.getBalance(model.ownership, d.addrFilters)
expectedRoles: ["ownership"]
},
FastExpressionRole { FastExpressionRole {
name: "lastTxTimestamp" name: "lastTxTimestamp"
expression: d.addrFilters, d.getLatestTimestmap(model.ownership, d.addrFilters) expression: d.addrFilters, d.getLatestTimestmap(model.ownership, d.addrFilters)
@ -166,10 +187,14 @@ ColumnLayout {
filters: [ filters: [
FastExpressionFilter { FastExpressionFilter {
expression: { expression: {
d.addrFilters return d.nwFilters.includes(model.chainId+"")
return d.nwFilters.includes(model.chainId+"") && d.containsAnyAddress(model.ownership, d.addrFilters)
} }
expectedRoles: ["chainId", "ownership"] expectedRoles: ["chainId"]
},
ValueFilter {
roleName: "balance"
value: 0
inverted: true
}, },
FastExpressionFilter { FastExpressionFilter {
expression: { expression: {
@ -461,6 +486,7 @@ ColumnLayout {
communityId: model.communityId ?? "" communityId: model.communityId ?? ""
communityName: model.communityName ?? "" communityName: model.communityName ?? ""
communityImage: model.communityImage ?? "" communityImage: model.communityImage ?? ""
balance: model.balance ?? 1
onClicked: root.collectibleClicked(model.chainId, model.contractAddress, model.tokenId, model.symbol, model.tokenType) onClicked: root.collectibleClicked(model.chainId, model.contractAddress, model.tokenId, model.symbol, model.tokenType)
onRightClicked: { onRightClicked: {

View File

@ -27,6 +27,7 @@ Control {
property string communityId: "" property string communityId: ""
property string communityName property string communityName
property string communityImage property string communityImage
property int balance: 1
// Special Owner and TMaster token properties // Special Owner and TMaster token properties
readonly property bool isCommunityCollectible: communityId !== "" readonly property bool isCommunityCollectible: communityId !== ""
@ -65,6 +66,13 @@ Control {
cursorShape: !root.isLoading ? Qt.PointingHandCursor : undefined cursorShape: !root.isLoading ? Qt.PointingHandCursor : undefined
} }
property Component balanceTag: Component {
CollectibleBalanceTag {
visible: !root.isLoading && (root.balance > 1)
balance: root.balance
}
}
contentItem: ColumnLayout { contentItem: ColumnLayout {
spacing: 0 spacing: 0
@ -89,6 +97,13 @@ Control {
active: root.isLoading active: root.isLoading
sourceComponent: LoadingComponent {radius: image.radius} sourceComponent: LoadingComponent {radius: image.radius}
} }
Loader {
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: Style.current.halfPadding
sourceComponent: root.balanceTag
}
} }
PrivilegedTokenArtworkPanel { PrivilegedTokenArtworkPanel {
@ -110,6 +125,13 @@ Control {
active: root.isLoading active: root.isLoading
sourceComponent: LoadingComponent {radius: image.radius} sourceComponent: LoadingComponent {radius: image.radius}
} }
Loader {
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: Style.current.halfPadding
sourceComponent: root.balanceTag
}
} }
RowLayout { RowLayout {