diff --git a/src/app/modules/shared_models/collectible_ownership_model.nim b/src/app/modules/shared_models/collectible_ownership_model.nim index 2fe5b31176..d1e4e5614d 100644 --- a/src/app/modules/shared_models/collectible_ownership_model.nim +++ b/src/app/modules/shared_models/collectible_ownership_model.nim @@ -1,4 +1,5 @@ import NimQml, Tables, strutils, strformat +import stint import backend/collectibles_types as backend @@ -61,7 +62,7 @@ QtObject: of ModelRole.AccountAddress: result = newQVariant(item.address) of ModelRole.Balance: - result = newQVariant($item.balance) + result = newQVariant(item.balance.toString(10)) of ModelRole.TxTimestamp: result = newQVariant(item.txTimestamp) diff --git a/storybook/src/Models/ManageCollectiblesModel.qml b/storybook/src/Models/ManageCollectiblesModel.qml index 38987f8fba..6354f36963 100644 --- a/storybook/src/Models/ManageCollectiblesModel.qml +++ b/storybook/src/Models/ManageCollectiblesModel.qml @@ -31,12 +31,12 @@ ListModel { ownership: [ { accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", - balance: 1, + balance: "1", txTimestamp: 1 }, { accountAddress: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", - balance: 1, + balance: "1", txTimestamp: 2 }, ] @@ -56,7 +56,7 @@ ListModel { ownership: [ { accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", - balance: 1, + balance: "8", txTimestamp: 3 }, ] @@ -76,7 +76,7 @@ ListModel { ownership: [ { accountAddress: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", - balance: 1, + balance: "1", txTimestamp: 3 }, ] @@ -96,7 +96,7 @@ ListModel { ownership: [ { accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", - balance: 1, + balance: "1", txTimestamp: 6 }, ] @@ -116,12 +116,12 @@ ListModel { ownership: [ { accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", - balance: 1, + balance: "1", txTimestamp: 50 }, { accountAddress: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", - balance: 1, + balance: "1", txTimestamp: 10 }, ] @@ -141,7 +141,7 @@ ListModel { ownership: [ { accountAddress: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", - balance: 1, + balance: "1", txTimestamp: 16 }, ] @@ -161,7 +161,7 @@ ListModel { ownership: [ { accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", - balance: 1, + balance: "1", txTimestamp: 19 }, ] @@ -184,7 +184,7 @@ ListModel { ownership: [ { accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", - balance: 1, + balance: "15", txTimestamp: 20 }, ] @@ -204,7 +204,7 @@ ListModel { ownership: [ { accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", - balance: 1, + balance: "4", txTimestamp: 21 }, ] @@ -224,7 +224,7 @@ ListModel { ownership: [ { accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", - balance: 1, + balance: "1", txTimestamp: 22 }, ] @@ -244,7 +244,7 @@ ListModel { ownership: [ { accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", - balance: 1, + balance: "1", txTimestamp: 23 }, ] @@ -264,7 +264,7 @@ ListModel { ownership: [ { accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", - balance: 1, + balance: "1000", txTimestamp: 25 }, ] @@ -284,7 +284,7 @@ ListModel { ownership: [ { accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", - balance: 1, + balance: "1", txTimestamp: 26 }, ] @@ -304,9 +304,14 @@ ListModel { ownership: [ { accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", - balance: 1, + balance: "60", txTimestamp: 27 }, + { + accountAddress: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", + balance: "70", + txTimestamp: 60 + }, ] }, { diff --git a/ui/StatusQ/src/StatusQ/Core/Utils/AmountsArithmetic.qml b/ui/StatusQ/src/StatusQ/Core/Utils/AmountsArithmetic.qml index e35a75c666..84e0af74e1 100644 --- a/ui/StatusQ/src/StatusQ/Core/Utils/AmountsArithmetic.qml +++ b/ui/StatusQ/src/StatusQ/Core/Utils/AmountsArithmetic.qml @@ -123,6 +123,16 @@ QtObject { 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) \brief Compares two amounts. diff --git a/ui/app/AppLayouts/Wallet/controls/CollectibleBalanceTag.qml b/ui/app/AppLayouts/Wallet/controls/CollectibleBalanceTag.qml new file mode 100644 index 0000000000..ea2d2c843c --- /dev/null +++ b/ui/app/AppLayouts/Wallet/controls/CollectibleBalanceTag.qml @@ -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 + } +} diff --git a/ui/app/AppLayouts/Wallet/controls/qmldir b/ui/app/AppLayouts/Wallet/controls/qmldir index 3d43a86c06..cd2b1d480b 100644 --- a/ui/app/AppLayouts/Wallet/controls/qmldir +++ b/ui/app/AppLayouts/Wallet/controls/qmldir @@ -12,3 +12,4 @@ ManageTokensDelegate 1.0 ManageTokensDelegate.qml ManageTokensGroupDelegate 1.0 ManageTokensGroupDelegate.qml InformationTileAssetDetails 1.0 InformationTileAssetDetails.qml StatusNetworkListItemTag 1.0 StatusNetworkListItemTag.qml +CollectibleBalanceTag 1.0 CollectibleBalanceTag.qml diff --git a/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml b/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml index 9a7e9c1f95..29fdaca847 100644 --- a/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml +++ b/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml @@ -10,7 +10,6 @@ import StatusQ.Controls 0.1 import StatusQ.Core 0.1 import StatusQ.Core.Theme 0.1 import StatusQ.Core.Utils 0.1 -import StatusQ.Internal 0.1 import StatusQ.Models 0.1 import StatusQ.Popups 0.1 import StatusQ.Popups.Dialog 0.1 @@ -124,27 +123,44 @@ ColumnLayout { readonly property var nwFilters: root.networkFilters.split(":") 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) { let latest = 0 - for (let i = 0; i < ownership.count; i++) { - let accountAddress = ModelUtils.get(ownership, i, "accountAddress").toLowerCase() - if (filterList.includes(accountAddress)) { - let txTimestamp = ModelUtils.get(ownership, i, "txTimestamp") - latest = Math.max(latest, txTimestamp) + + if (!!ownership) { + for (let i = 0; i < ownership.count; i++) { + let accountAddress = ModelUtils.get(ownership, i, "accountAddress").toLowerCase() + if (filterList.includes(accountAddress)) { + let txTimestamp = ModelUtils.get(ownership, i, "txTimestamp") + latest = Math.max(latest, txTimestamp) + } } } 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 { @@ -157,6 +173,11 @@ ColumnLayout { name: "groupName" roleNames: ["collectionName", "communityName"] }, + FastExpressionRole { + name: "balance" + expression: d.addrFilters, d.getBalance(model.ownership, d.addrFilters) + expectedRoles: ["ownership"] + }, FastExpressionRole { name: "lastTxTimestamp" expression: d.addrFilters, d.getLatestTimestmap(model.ownership, d.addrFilters) @@ -166,10 +187,14 @@ ColumnLayout { filters: [ FastExpressionFilter { expression: { - d.addrFilters - return d.nwFilters.includes(model.chainId+"") && d.containsAnyAddress(model.ownership, d.addrFilters) + return d.nwFilters.includes(model.chainId+"") } - expectedRoles: ["chainId", "ownership"] + expectedRoles: ["chainId"] + }, + ValueFilter { + roleName: "balance" + value: 0 + inverted: true }, FastExpressionFilter { expression: { @@ -461,6 +486,7 @@ ColumnLayout { communityId: model.communityId ?? "" communityName: model.communityName ?? "" communityImage: model.communityImage ?? "" + balance: model.balance ?? 1 onClicked: root.collectibleClicked(model.chainId, model.contractAddress, model.tokenId, model.symbol, model.tokenType) onRightClicked: { diff --git a/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleView.qml b/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleView.qml index ccf2910e84..96e1447982 100644 --- a/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleView.qml +++ b/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleView.qml @@ -27,6 +27,7 @@ Control { property string communityId: "" property string communityName property string communityImage + property int balance: 1 // Special Owner and TMaster token properties readonly property bool isCommunityCollectible: communityId !== "" @@ -65,6 +66,13 @@ Control { cursorShape: !root.isLoading ? Qt.PointingHandCursor : undefined } + property Component balanceTag: Component { + CollectibleBalanceTag { + visible: !root.isLoading && (root.balance > 1) + balance: root.balance + } + } + contentItem: ColumnLayout { spacing: 0 @@ -89,6 +97,13 @@ Control { active: root.isLoading sourceComponent: LoadingComponent {radius: image.radius} } + + Loader { + anchors.top: parent.top + anchors.left: parent.left + anchors.margins: Style.current.halfPadding + sourceComponent: root.balanceTag + } } PrivilegedTokenArtworkPanel { @@ -110,6 +125,13 @@ Control { active: root.isLoading sourceComponent: LoadingComponent {radius: image.radius} } + + Loader { + anchors.top: parent.top + anchors.left: parent.left + anchors.margins: Style.current.halfPadding + sourceComponent: root.balanceTag + } } RowLayout {