diff --git a/storybook/pages/CollectibleDetailViewPage.qml b/storybook/pages/CollectibleDetailViewPage.qml index 63c3b2c89d..51fc8c04cc 100644 --- a/storybook/pages/CollectibleDetailViewPage.qml +++ b/storybook/pages/CollectibleDetailViewPage.qml @@ -19,10 +19,23 @@ import utils 1.0 SplitView { id: root + QtObject { + function isValidURL(url) { + return true + } + + Component.onCompleted: { + Utils.globalUtilsInst = this + } + Component.onDestruction: { + Utils.globalUtilsInst = {} + } + } + QtObject { id: d - readonly property QtObject collectiblesModel: WalletCollectiblesModel { + readonly property QtObject collectiblesModel: ManageCollectiblesModel { Component.onCompleted: { d.refreshCurrentCollectible() } @@ -34,6 +47,15 @@ SplitView { } readonly property QtObject transactionsModel: WalletTransactionsModel{} + + readonly property string addressesSelected: { + let supportedAddresses = "" + for (let i =0; i< accountsRepeater.count; i++) { + if (accountsRepeater.itemAt(i).checked && accountsRepeater.itemAt(i).visible) + supportedAddresses += accountsRepeater.itemAt(i).address + ":" + } + return supportedAddresses + } } SplitView { @@ -62,6 +84,7 @@ SplitView { collectible: d.currentCollectible isCollectibleLoading: isLoadingCheckbox.checked activityModel: d.transactionsModel + addressFilters: d.addressesSelected rootStore: QtObject { readonly property string currentCurrency: "EUR" @@ -83,6 +106,26 @@ SplitView { } readonly property bool showAllAccounts: true + + function getExplorerUrl(networkShortName, contractAddress, tokenId) { + let link = Constants.networkExplorerLinks.etherscan + if (networkShortName === Constants.networkShortChainNames.mainnet) { + return "%1/nft/%2/%3".arg(link).arg(contractAddress).arg(tokenId) + } + else { + return "%1/token/%2?a=%3".arg(link).arg(contractAddress).arg(tokenId) + } + } + + function getOpenSeaCollectionUrl(networkShortName, contractAddress) { + let baseLink = root.areTestNetworksEnabled ? Constants.openseaExplorerLinks.testnetLink : Constants.openseaExplorerLinks.mainnetLink + return "%1/assets/%2/%3".arg(baseLink).arg(networkShortName).arg(contractAddress) + } + + function getOpenSeaCollectibleUrl(networkShortName, contractAddress, tokenId) { + let baseLink = root.areTestNetworksEnabled ? Constants.openseaExplorerLinks.testnetLink : Constants.openseaExplorerLinks.mainnetLink + return "%1/assets/%2/%3/%4".arg(baseLink).arg(networkShortName).arg(contractAddress).arg(tokenId) + } } communitiesStore: QtObject { function getCommunityDetailsAsJson(communityId) { @@ -130,6 +173,23 @@ SplitView { text: "isLoading" checked: false } + ColumnLayout { + Layout.fillWidth: true + Text { + text: "select account(s)" + } + Repeater { + id: accountsRepeater + model: WalletAccountsModel {} + delegate: CheckBox { + property string address: model.address + checked: true + visible: index<2 + width: parent.width + text: name + } + } + } } } } diff --git a/storybook/qmlTests/tests/tst_ManageCollectiblesPanel.qml b/storybook/qmlTests/tests/tst_ManageCollectiblesPanel.qml index 3c1b7353e0..8bea35dcf2 100644 --- a/storybook/qmlTests/tests/tst_ManageCollectiblesPanel.qml +++ b/storybook/qmlTests/tests/tst_ManageCollectiblesPanel.qml @@ -117,7 +117,7 @@ Item { const lvOther = findChild(controlUnderTest, "otherTokensListView") verify(!!lvOther) - tryCompare(lvOther, "count", 7) + tryCompare(lvOther, "count", 9) const delegate0 = findChild(lvOther, "manageTokensDelegate-0") verify(!!delegate0) const title = delegate0.title @@ -127,7 +127,7 @@ Item { tryCompare(notificationSpy, "count", 1) // verify we now have -1 regular tokens after the "hide" operation - tryCompare(lvOther, "count", 6) + tryCompare(lvOther, "count", 8) } function test_showHideCommunityGroup() { @@ -149,7 +149,7 @@ Item { verify(!!lvCommunity) // verify we have 2 community collectible groups - tryCompare(lvCommunity, "count", 4) + tryCompare(lvCommunity, "count", 6) tryCompare(notificationSpy, "count", 0) triggerDelegateMenuAction(lvCommunity, 0, "miHideTokenGroup", true) // verify the signal to show the notification toast got fired @@ -157,7 +157,7 @@ Item { // verify we have one less group waitForItemPolished(lvCommunity) - tryCompare(lvCommunity, "count", 3) + tryCompare(lvCommunity, "count", 5) } function test_dnd() { @@ -200,7 +200,7 @@ Item { const lvCommunity = findChild(controlUnderTest, "communityTokensListView") verify(!!lvCommunity) waitForItemPolished(lvCommunity) - tryCompare(lvCommunity, "count", 4) + tryCompare(lvCommunity, "count", 6) const group0 = findChild(lvCommunity, "manageTokensGroupDelegate-0") const title0 = group0.title @@ -235,7 +235,7 @@ Item { const lvCommunity = findChild(controlUnderTest, "communityTokensListView") verify(!!lvCommunity) waitForItemPolished(lvCommunity) - tryCompare(lvCommunity, "count", 4) + tryCompare(lvCommunity, "count", 6) // get the "Bearz" group at index 1 var bearzGroupTokenDelegate = findChild(lvCommunity, "manageTokensGroupDelegate-1") diff --git a/storybook/src/Models/ManageCollectiblesModel.qml b/storybook/src/Models/ManageCollectiblesModel.qml index f67db0302b..e4b6d6cf15 100644 --- a/storybook/src/Models/ManageCollectiblesModel.qml +++ b/storybook/src/Models/ManageCollectiblesModel.qml @@ -22,7 +22,7 @@ ListModel { userHas: 9, name: "Punx not dead!", collectionUid: "", - collectionName: "", + collectionName: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", communityId: "", communityName: "", communityImage: ModelsData.icons.status, @@ -40,7 +40,26 @@ ListModel { balance: "1", txTimestamp: 2 }, - ] + ], + networkShortName: "ETH", + networkColor: "blue", + networkIconUrl: ModelsData.networks.ethereum, + description: "Punx not dead is a very rare CryptoKitty. It's a Gen 0 and has a lot of special traits.", + traits: [ + { + traitType: "Fur", + value: "White" + }, + { + traitType: "Eyes", + value: "Blue" + }, + { + traitType: "Pattern", + value: "Tigerpunk" + } + ], + tokenId: "403" }, { uid: "pp23", @@ -61,7 +80,26 @@ ListModel { balance: "8", txTimestamp: 3 }, - ] + ], + networkShortName: "ETH", + networkColor: "blue", + networkIconUrl: ModelsData.networks.ethereum, + description: "pepepunk not dead is a very rare CryptoKitty. It's a Gen 0 and has a lot of special traits.", + traits: [ + { + traitType: "Fur", + value: "White" + }, + { + traitType: "Eyes", + value: "Green" + }, + { + traitType: "Pattern", + value: "Tigerpunk" + } + ], + tokenId: "123" }, { uid: "34545656768", @@ -82,7 +120,26 @@ ListModel { balance: "1", txTimestamp: 3 }, - ] + ], + networkShortName: "ETH", + networkColor: "blue", + networkIconUrl: ModelsData.networks.ethereum, + description: "Furbeard is a very rare CryptoKitty. It's a Gen 0 cat and has a lot of special traits.", + traits: [ + { + traitType: "Fur", + value: "White" + }, + { + traitType: "Eyes", + value: "Green" + }, + { + traitType: "Pattern", + value: "Tigerpunk" + } + ], + tokenId: "7123" }, { uid: "123456", @@ -103,7 +160,29 @@ ListModel { balance: "1", txTimestamp: 6 }, - ] + ], + networkShortName: "OPT", + networkColor: "red", + networkIconUrl: ModelsData.networks.optimism, + networkShortName: "OPT", + networkColor: "red", + networkIconUrl: ModelsData.networks.optimism, + description: "Furbeard is a very rare CryptoKitty. It's a Gen 0 cat and has a lot of special traits.", + traits: [ + { + traitType: "Fur", + value: "White" + }, + { + traitType: "Eyes", + value: "Green" + }, + { + traitType: "Pattern", + value: "Tigerpunk" + } + ], + tokenId: "403123" }, { uid: "12345645459537432", @@ -129,7 +208,26 @@ ListModel { balance: "1", txTimestamp: 10 }, - ] + ], + networkShortName: "OPT", + networkColor: "red", + networkIconUrl: ModelsData.networks.optimism, + description: "Big Kitty is a very rare CryptoKitty. It's a Gen 0 cat and has a lot of special traits.", + traits: [ + { + traitType: "Fur", + value: "White" + }, + { + traitType: "Eyes", + value: "Blue" + }, + { + traitType: "Pattern", + value: "Tigerpunk" + } + ], + tokenId: "1" }, { uid: "pp21", @@ -150,7 +248,18 @@ ListModel { balance: "1", txTimestamp: 16 }, - ] + ], + networkShortName: "OPT", + networkColor: "red", + networkIconUrl: ModelsData.networks.optimism, + description: "pepepunk not dead is a very rare nft. It's a Gen 0 and has a lot of special traits.", + traits: [ + { + traitType: "Type", + value: "Special" + } + ], + tokenId: "12568" }, { uid: "lp#666a", @@ -171,7 +280,18 @@ ListModel { balance: "1", txTimestamp: 19 }, - ] + ], + networkShortName: "OPT", + networkColor: "red", + networkIconUrl: ModelsData.networks.optimism, + description: "Lonely Panda #666 is a very rare NFT. It's a Gen 0 and has a lot of special traits.likie sjasja sajhash jhasjas", + traits: [ + { + traitType: "Type", + value: "Rare" + } + ], + tokenId: "1445" }, ] @@ -194,7 +314,13 @@ ListModel { balance: "15", txTimestamp: 20 }, - ] + ], + networkShortName: "OPT", + networkColor: "red", + networkIconUrl: ModelsData.networks.optimism, + description: "Frenly Pandas is a community for all the fiendly pandas! Welcome onboard and enjoy :)", + traits: [], + tokenId: "4" }, { uid: "691", @@ -214,7 +340,13 @@ ListModel { balance: "4", txTimestamp: 21 }, - ] + ], + networkShortName: "OPT", + networkColor: "red", + networkIconUrl: ModelsData.networks.optimism, + description: "Bearz is a community for all the ferocious Bearz! Welcome onboard and enjoy :)", + traits: [], + tokenId: "3" }, { uid: "8876", @@ -234,7 +366,13 @@ ListModel { balance: "1", txTimestamp: 22 }, - ] + ], + networkShortName: "ETH", + networkColor: "blue", + networkIconUrl: ModelsData.networks.ethereum, + description: "Bearz is a community for all the ferocious Bearz! Welcome onboard and enjoy :)", + traits: [], + tokenId: "341" }, { uid: "fp#3195", @@ -254,7 +392,13 @@ ListModel { balance: "1", txTimestamp: 23 }, - ] + ], + networkShortName: "ETH", + networkColor: "blue", + networkIconUrl: ModelsData.networks.ethereum, + description: "Frenly Pandas is a community for all the fiendly pandas! Welcome onboard and enjoy :)", + traits: [], + tokenId: "765" }, { uid: "fp#4297", @@ -274,7 +418,13 @@ ListModel { balance: "1000", txTimestamp: 25 }, - ] + ], + networkShortName: "ETH", + networkColor: "blue", + networkIconUrl: ModelsData.networks.ethereum, + description: "Frenly Pandas is a community for all the fiendly pandas! Welcome onboard and enjoy :)", + traits: [], + tokenId: "166" }, { uid: "fp#909", @@ -294,7 +444,13 @@ ListModel { balance: "1", txTimestamp: 26 }, - ] + ], + networkShortName: "OPT", + networkColor: "red", + networkIconUrl: ModelsData.networks.optimism, + description: "Frenly Pandas is a community for all the fiendly pandas! Welcome onboard and enjoy :)", + traits: [], + tokenId: "1111" }, { uid: "lb#666", @@ -319,7 +475,13 @@ ListModel { balance: "70", txTimestamp: 60 } - ] + ], + networkShortName: "OPT", + networkColor: "red", + networkIconUrl: ModelsData.networks.optimism, + description: "Bearz is a community for all the ferocious Bearz! Welcome onboard and enjoy", + traits: [], + tokenId: "6" }, { uid: "lb#777", @@ -339,8 +501,114 @@ ListModel { balance: "1", txTimestamp: 27 }, - ] + ], + networkShortName: "OPT", + networkColor: "red", + networkIconUrl: ModelsData.networks.optimism, + description: "Lonely Turtle is a community for all of us to talk and communicate! Welcome onboard and enjoy", + traits: [], + tokenId: "7" }, + { + uid: "ID-Custom", + chainId: 1, + contractAddress: "0x04", + tokenId: "403", + name: "Custom Collectible", + imageUrl: ModelsData.collectibles.custom, + backgroundColor: "transparent", + description: "This is a custom collectible. It's a unique piece of art.", + collectionUid: "custom", + collectionName: "Custom", + collectionImageUrl: "", + traits: [], + ownership: [ + { + accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", + balance: "1", + txTimestamp: 27 + }, + ], + communityId: "", + networkShortName: "ARB", + networkColor: "blue", + networkIconUrl: ModelsData.networks.arbitrum + }, + { + uid: "ID-MissingMetadata", + chainId: 1, + contractAddress: "0x05", + tokenId: "405", + name: "", + imageUrl: "", + backgroundColor: "transparent", + description: "", + collectionUid: "missing", + collectionName: "", + collectionImageUrl: "", + traits: [], + ownership: [ + { + accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", + balance: "1", + txTimestamp: 27 + }, + ], + communityId: "", + networkShortName: "OPT", + networkColor: "red", + networkIconUrl: ModelsData.networks.optimism + }, + { + uid: "ID-Community1", + chainId: 1, + contractAddress: "0x06", + tokenId: "406", + name: "Community Admin Token", + imageUrl: ModelsData.collectibles.mana, + backgroundColor: "transparent", + description: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.", + collectionUid: "community-uid-1", + collectionName: "", + collectionImageUrl: "", + traits: [], + ownership: [ + { + accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", + balance: "1", + txTimestamp: 27 + }, + ], + communityId: "community-id-1", + networkShortName: "OPT", + networkColor: "red", + networkIconUrl: ModelsData.networks.optimism + }, + { + uid: "ID-Community-Unknown", + chainId: 1, + contractAddress: "0x07", + tokenId: "407", + name: "Removed community token", + imageUrl: ModelsData.collectibles.mana, + backgroundColor: "transparent", + description: "This is unkown community community token", + collectionUid: "community-uid-unknown", + collectionName: "", + collectionImageUrl: "", + traits: [], + ownership: [ + { + accountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", + balance: "1", + txTimestamp: 27 + }, + ], + communityId: "community-id-unknown", + networkShortName: "OPT", + networkColor: "red", + networkIconUrl: ModelsData.networks.optimism + } ] Component.onCompleted: { diff --git a/storybook/src/Models/WalletCollectiblesModel.qml b/storybook/src/Models/WalletCollectiblesModel.qml deleted file mode 100644 index f209610460..0000000000 --- a/storybook/src/Models/WalletCollectiblesModel.qml +++ /dev/null @@ -1,284 +0,0 @@ -import QtQuick 2.15 - -import StatusQ.Core 0.1 -import utils 1.0 - -ListModel { - readonly property var rootData: [ - { - uid: "ID-Kitty1", - chainId: 1, - contractAddress: "0x1", - tokenId: "1", - name: "Furbeard", - imageUrl: ModelsData.collectibles.kitty1Big, - backgroundColor: "#f5f5f5", - description: "Furbeard is a very rare CryptoKitty. It's a Gen 0 cat and has a lot of special traits.", - collectionUid: "cryptokitties", - collectionName: "CryptoKitties", - collectionImageUrl: ModelsData.collectibles.cryptoKitties, - traits: [ - { - traitType: "Fur", - value: "White" - }, - { - traitType: "Eyes", - value: "Green" - }, - { - traitType: "Pattern", - value: "Tigerpunk" - } - ], - communityId: "", - networkShortName: "ETH", - networkColor: "blue", - networkIconUrl: ModelsData.networks.ethereum - }, - { - uid: "ID-Kitty2", - chainId: 1, - contractAddress: "0x1", - tokenId: "2", - name: "Magicat", - imageUrl: ModelsData.collectibles.kitty2Big, - backgroundColor: "transparent", - description: "Magicat is a very rare CryptoKitty. It's a Gen 0 cat and has a lot of special traits.", - collectionUid: "cryptokitties", - collectionName: "CryptoKitties", - collectionImageUrl: ModelsData.collectibles.cryptoKitties, - traits: [ - { - traitType: "Fur", - value: "White" - }, - { - traitType: "Eyes", - value: "Blue" - }, - { - traitType: "Pattern", - value: "Tigerpunk" - } - ], - communityId: "", - networkShortName: "ETH", - networkColor: "blue", - networkIconUrl: ModelsData.networks.ethereum - }, - { - uid: "ID-Kitty3", - chainId: 1, - contractAddress: "0x1", - tokenId: "3", - name: "Happy Meow", - imageUrl: ModelsData.collectibles.kitty3Big, - backgroundColor: "blue", - description: "Happy Meow is a very rare CryptoKitty. It's a Gen 0 cat and has a lot of special traits.", - collectionUid: "cryptokitties", - collectionName: "CryptoKitties", - collectionImageUrl: ModelsData.collectibles.cryptoKitties, - traits: [ - { - traitType: "Fur", - value: "White" - }, - { - traitType: "Eyes", - value: "Green" - }, - { - traitType: "Pattern", - value: "Tigerpunk" - } - ], - communityId: "", - networkShortName: "ETH", - networkColor: "blue", - networkIconUrl: ModelsData.networks.ethereum - }, - { - uid: "ID-Kitty4", - chainId: 1, - contractAddress: "0x1", - tokenId: "4", - name: "Furbeard-2", - imageUrl: ModelsData.collectibles.kitty4Big, - backgroundColor: "red", - description: "Furbeard-2 is a very rare CryptoKitty. It's a Gen 0 cat and has a lot of special traits.", - collectionUid: "cryptokitties", - collectionName: "CryptoKitties", - collectionImageUrl: ModelsData.collectibles.cryptoKitties, - traits: [ - { - traitType: "Fur", - value: "White" - }, - { - traitType: "Eyes", - value: "Green" - }, - { - traitType: "Pattern", - value: "Tigerpunk" - } - ], - communityId: "", - networkShortName: "ETH", - networkColor: "blue", - networkIconUrl: ModelsData.networks.ethereum - }, - { - uid: "ID-Kitty5", - chainId: 1, - contractAddress: "0x1", - tokenId: "4", - name: "Magicat-3", - imageUrl: ModelsData.collectibles.kitty5Big, - backgroundColor: "yellow", - description: "Magicat-3 is a very rare CryptoKitty. It's a Gen 0 cat and has a lot of special traits.", - collectionUid: "cryptokitties", - collectionName: "CryptoKitties", - collectionImageUrl: ModelsData.collectibles.cryptoKitties, - traits: [ - { - traitType: "Fur", - value: "White" - }, - { - traitType: "Eyes", - value: "Blue" - }, - { - traitType: "Pattern", - value: "Tigerpunk" - } - ], - communityId: "", - networkShortName: "ETH", - networkColor: "blue", - networkIconUrl: ModelsData.networks.ethereum - }, - { - uid: "ID-Anniversary", - chainId: 1, - contractAddress: "0x2", - tokenId: "1", - name: "Anniversary", - imageUrl: ModelsData.collectibles.anniversary, - backgroundColor: "black", - description: "This is a special collectible to celebrate the anniversary of the platform.", - collectionUid: "anniversary", - collectionName: "Anniversary", - collectionImageUrl: ModelsData.collectibles.anniversary, - traits: [ - { - traitType: "Type", - value: "Special" - } - ], - communityId: "", - networkShortName: "OPT", - networkColor: "red", - networkIconUrl: ModelsData.networks.optimism - }, - { - uid: "ID-SuperRare", - chainId: 1, - contractAddress: "0x3", - tokenId: "101", - name: "SuperRare", - imageUrl: ModelsData.collectibles.superRare, - backgroundColor: "transparent", - description: "This is a very rare collectible. It's a unique piece of art.", - collectionUid: "super-rare", - collectionName: "SuperRare", - collectionImageUrl: ModelsData.collectibles.doodles, - traits: [ - { - traitType: "Type", - value: "Rare" - } - ], - communityId: "", - networkShortName: "OPT", - networkColor: "red", - networkIconUrl: ModelsData.networks.optimism - }, - { - uid: "ID-Custom", - chainId: 1, - contractAddress: "0x04", - tokenId: "403", - name: "Custom Collectible", - imageUrl: ModelsData.collectibles.custom, - backgroundColor: "transparent", - description: "This is a custom collectible. It's a unique piece of art.", - collectionUid: "custom", - collectionName: "Custom", - collectionImageUrl: "", - traits: [], - communityId: "", - networkShortName: "ARB", - networkColor: "blue", - networkIconUrl: ModelsData.networks.arbitrum - }, - { - uid: "ID-MissingMetadata", - chainId: 1, - contractAddress: "0x05", - tokenId: "405", - name: "", - imageUrl: "", - backgroundColor: "transparent", - description: "", - collectionUid: "missing", - collectionName: "", - collectionImageUrl: "", - traits: [], - communityId: "", - networkShortName: "OPT", - networkColor: "red", - networkIconUrl: ModelsData.networks.optimism - }, - { - uid: "ID-Community1", - chainId: 1, - contractAddress: "0x06", - tokenId: "406", - name: "Community Admin Token", - imageUrl: ModelsData.collectibles.mana, - backgroundColor: "transparent", - description: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.", - collectionUid: "community-uid-1", - collectionName: "", - collectionImageUrl: "", - traits: [], - communityId: "community-id-1", - networkShortName: "OPT", - networkColor: "red", - networkIconUrl: ModelsData.networks.optimism - }, - { - uid: "ID-Community-Unknown", - chainId: 1, - contractAddress: "0x07", - tokenId: "407", - name: "Removed community token", - imageUrl: ModelsData.collectibles.mana, - backgroundColor: "transparent", - description: "This is unkown community community token", - collectionUid: "community-uid-unknown", - collectionName: "", - collectionImageUrl: "", - traits: [], - communityId: "community-id-unknown", - networkShortName: "OPT", - networkColor: "red", - networkIconUrl: ModelsData.networks.optimism - } - ] - - Component.onCompleted: append(rootData) -} diff --git a/storybook/src/Models/qmldir b/storybook/src/Models/qmldir index ebd65a9e77..062845538f 100644 --- a/storybook/src/Models/qmldir +++ b/storybook/src/Models/qmldir @@ -16,7 +16,6 @@ TokenHoldersModel 1.0 TokenHoldersModel.qml UsersModel 1.0 UsersModel.qml WalletSendAccountsModel 1.0 WalletSendAccountsModel.qml WalletAccountsModel 1.0 WalletAccountsModel.qml -WalletCollectiblesModel 1.0 WalletCollectiblesModel.qml WalletKeyPairModel 1.0 WalletKeyPairModel.qml WalletNestedCollectiblesModel 1.0 WalletNestedCollectiblesModel.qml WalletTransactionsModel 1.0 WalletTransactionsModel.qml diff --git a/storybook/stubs/shared/stores/send/TransactionStore.qml b/storybook/stubs/shared/stores/send/TransactionStore.qml index bee27e98e8..c170bbe673 100644 --- a/storybook/stubs/shared/stores/send/TransactionStore.qml +++ b/storybook/stubs/shared/stores/send/TransactionStore.qml @@ -28,7 +28,7 @@ QtObject { property var fromNetworksModel: NetworksModel.sendFromNetworks property var toNetworksModel: NetworksModel.sendToNetworks property var selectedSenderAccount: senderAccounts.get(0) - readonly property QtObject collectiblesModel: WalletCollectiblesModel {} + readonly property QtObject collectiblesModel: ManageCollectiblesModel {} readonly property QtObject nestedCollectiblesModel: WalletNestedCollectiblesModel {} readonly property QtObject walletSectionSendInst: QtObject { diff --git a/ui/app/AppLayouts/Wallet/controls/CollectibleBalanceTag.qml b/ui/app/AppLayouts/Wallet/controls/CollectibleBalanceTag.qml index ea2d2c843c..17bddfcaf3 100644 --- a/ui/app/AppLayouts/Wallet/controls/CollectibleBalanceTag.qml +++ b/ui/app/AppLayouts/Wallet/controls/CollectibleBalanceTag.qml @@ -15,7 +15,7 @@ Control { horizontalPadding: 8 background: Rectangle { - color: Theme.palette.indirectColor4 + color: Theme.palette.indirectColor2 radius: height / 2 } diff --git a/ui/app/AppLayouts/Wallet/views/RightTabView.qml b/ui/app/AppLayouts/Wallet/views/RightTabView.qml index 57845f2cda..69be197f21 100644 --- a/ui/app/AppLayouts/Wallet/views/RightTabView.qml +++ b/ui/app/AppLayouts/Wallet/views/RightTabView.qml @@ -232,6 +232,7 @@ RightTabBaseView { collectible: RootStore.collectiblesStore.detailedCollectible isCollectibleLoading: RootStore.collectiblesStore.isDetailedCollectibleLoading activityModel: d.detailedCollectibleActivityController.model + addressFilters: RootStore.addressFilters rootStore: SharedStores.RootStore walletRootStore: RootStore communitiesStore: root.communitiesStore diff --git a/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleDetailView.qml b/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleDetailView.qml index a1ff0ccfe9..718b7f459d 100644 --- a/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleDetailView.qml +++ b/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleDetailView.qml @@ -1,6 +1,8 @@ import QtQuick 2.14 import QtQuick.Layouts 1.13 +import SortFilterProxyModel 0.2 +import StatusQ 0.1 import StatusQ.Components 0.1 import StatusQ.Core.Theme 0.1 import StatusQ.Core 0.1 @@ -28,6 +30,7 @@ Item { required property var collectible property var activityModel property bool isCollectibleLoading + required property string addressFilters // Community related token props: readonly property bool isCommunityCollectible: !!collectible ? collectible.communityId !== "" : false @@ -38,9 +41,34 @@ Item { QtObject { id: d - readonly property string collectibleLink: root.walletRootStore.getOpenSeaCollectibleUrl(collectible.networkShortName, collectible.contractAddress, collectible.tokenId) - readonly property string collectionLink: root.walletRootStore.getOpenSeaCollectionUrl(collectible.networkShortName, collectible.contractAddress) - readonly property string blockExplorerLink: root.walletRootStore.getExplorerUrl(collectible.networkShortName, collectible.contractAddress, collectible.tokenId) + readonly property string collectibleLink: !!collectible ? root.walletRootStore.getOpenSeaCollectibleUrl(collectible.networkShortName, collectible.contractAddress, collectible.tokenId): "" + readonly property string collectionLink: !!collectible ? root.walletRootStore.getOpenSeaCollectionUrl(collectible.networkShortName, collectible.contractAddress): "" + readonly property string blockExplorerLink: !!collectible ? root.walletRootStore.getExplorerUrl(collectible.networkShortName, collectible.contractAddress, collectible.tokenId): "" + readonly property var addrFilters: root.addressFilters.split(":").map((addr) => addr.toLowerCase()) + readonly property int imageStackSpacing: 4 + + property Component balanceTag: Component { + CollectibleBalanceTag { + balance: d.balanceAggregator.value + } + } + property SortFilterProxyModel filteredBalances: SortFilterProxyModel { + sourceModel: !!collectible ? collectible.ownership : null + filters: [ + FastExpressionFilter { + expression: { + d.addrFilters + return d.addrFilters.includes(model.accountAddress.toLowerCase()) + } + expectedRoles: ["accountAddress"] + } + ] + } + + property SumAggregator balanceAggregator: SumAggregator { + model: d.filteredBalances + roleName: "balance" + } } CollectibleDetailsHeader { @@ -48,16 +76,16 @@ Item { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right - collectibleName: !!collectible.name ? collectible.name : qsTr("Unknown") - collectibleId: "#" + collectible.tokenId + collectibleName: !!collectible && !!collectible.name ? collectible.name : qsTr("Unknown") + collectibleId: !!collectible ? "#" + collectible.tokenId : "" communityName: !!communityDetails ? communityDetails.name : "" - communityId: collectible.communityId - collectionName: collectible.collectionName - communityImage: !!communityDetails ? communityDetails.image: "" - networkShortName: collectible.networkShortName - networkColor: collectible.networkColor - networkIconURL: collectible.networkIconUrl - networkExplorerName: root.walletRootStore.getExplorerNameForNetwork(collectible.networkShortName) + communityId: !!collectible ? collectible.communityId : "" + collectionName: !!collectible ? collectible.collectionName : "" + communityImage: !!communityDetails ? communityDetails.image : "" + networkShortName: !!collectible ? collectible.networkShortName : "" + networkColor: !!collectible ?collectible.networkColor : "" + networkIconURL: !!collectible ? collectible.networkIconUrl : "" + networkExplorerName: !!collectible ? root.walletRootStore.getExplorerNameForNetwork(collectible.networkShortName): "" collectibleLinkEnabled: Utils.getUrlStatus(d.collectibleLink) collectionLinkEnabled: (!!communityDetails && communityDetails.name) || Utils.getUrlStatus(d.collectionLink) explorerLinkEnabled: Utils.getUrlStatus(d.blockExplorerLink) @@ -78,7 +106,6 @@ Item { anchors.top: collectibleHeader.bottom anchors.topMargin: 25 anchors.left: parent.left - anchors.leftMargin: 52 anchors.right: parent.right anchors.bottom: parent.bottom @@ -87,62 +114,41 @@ Item { Row { id: collectibleImageDetails - readonly property real visibleImageHeight: (collectibleimage.visible ? collectibleimage.height : privilegedCollectibleImage.height) - readonly property real visibleImageWidth: (collectibleimage.visible ? collectibleimage.width : privilegedCollectibleImage.width) + readonly property real visibleImageHeight: artwork.height + readonly property real visibleImageWidth: artwork.width height: collectibleImageDetails.visibleImageHeight width: parent.width spacing: 24 - // Special artwork representation for community `Owner and Master Token` token types: - PrivilegedTokenArtworkPanel { - id: privilegedCollectibleImage - - visible: root.isCommunityCollectible && (root.isOwnerTokenType || root.isTMasterTokenType) - size: PrivilegedTokenArtworkPanel.Size.Large - artwork: root.collectible.imageUrl - color: !!root.collectible && !!root.communityDetails ? root.communityDetails.color : "transparent" - isOwner: root.isOwnerTokenType - } - - StatusRoundedMedia { - id: collectibleimage - - readonly property bool isEmpty: !mediaUrl.toString() && !fallbackImageUrl.toString() - visible: !privilegedCollectibleImage.visible - width: 248 - height: width - radius: Style.current.radius - color: isError || isEmpty ? Theme.palette.baseColor5 : collectible.backgroundColor - border.color: Theme.palette.directColor8 - border.width: 1 - mediaUrl: collectible.mediaUrl ?? "" - mediaType: collectible.mediaType ?? "" - fallbackImageUrl: collectible.imageUrl - - Column { - anchors.centerIn: parent - visible: collectibleimage.isError || collectibleimage.isEmpty - spacing: 10 - - StatusIcon { - anchors.horizontalCenter: parent.horizontalCenter - icon: "frowny" - opacity: 0.1 - color: Theme.palette.directColor1 - } - StatusBaseText { - anchors.horizontalCenter: parent.horizontalCenter - horizontalAlignment: Qt.AlignHCenter - color: Theme.palette.directColor6 - text: { - if (collectibleimage.isError && collectibleimage.componentMediaType === StatusRoundedMedia.MediaType.Unkown) { - return qsTr("Unsupported\nfile format") - } - if (!collectible.description && !collectible.name) { - return qsTr("Info can't\nbe fetched") - } - return qsTr("Failed\nto load") + ColumnLayout { + id: artwork + spacing: 0 + Repeater { + id: repeater + model: Math.min(3, d.balanceAggregator.value) + Item { + Layout.preferredWidth: childrenRect.width + Layout.preferredHeight: childrenRect.height + Layout.leftMargin: index * d.imageStackSpacing + Layout.topMargin: index === 0 ? 0 : -Layout.preferredHeight + d.imageStackSpacing + opacity: index === 0 ? 1: 0.4/index + // so that the first item remains on top in the stack + z: -index + Loader { + property int modelIndex: index + anchors.top: parent.top + anchors.left: parent.left + sourceComponent: root.isCommunityCollectible && (root.isOwnerTokenType || root.isTMasterTokenType) ? privilegedCollectibleImage: collectibleimage + active: root.visible + } + Loader { + anchors.top: parent.top + anchors.left: parent.left + anchors.margins: Style.current.padding + sourceComponent: d.balanceTag + // only show balance tag on top of the first image in stack + active: index === 0 && d.balanceAggregator.value > 1 && root.visible } } } @@ -159,7 +165,8 @@ Item { width: parent.width height: 24 - text: root.isCommunityCollectible && !!communityDetails ? qsTr("Minted by %1").arg(root.communityDetails.name): root.collectible.collectionName + text: root.isCommunityCollectible && !!communityDetails ? qsTr("Minted by %1").arg(root.communityDetails.name): + !!collectible ? collectible.collectionName: "" color: Theme.palette.directColor1 font.pixelSize: 17 lineHeight: 24 @@ -174,7 +181,7 @@ Item { height: collectibleImageDetails.height - collectibleName.height - parent.spacing clip: true - text: collectible.description + text: !!collectible ? collectible.description : "" textFormat: Text.MarkdownText color: Theme.palette.directColor4 font.pixelSize: 15 @@ -190,7 +197,7 @@ Item { id: collectiblesDetailsTab width: parent.width topPadding: Style.current.xlPadding - visible: collectible.traits.count > 0 + visible: !!collectible && collectible.traits.count > 0 StatusTabButton { leftPadding: 0 @@ -228,7 +235,7 @@ Item { width: scrollView.availableWidth spacing: 10 Repeater { - model: collectible.traits + model: !!collectible ? collectible.traits: null InformationTile { maxWidth: parent.width primaryText: model.traitType @@ -240,7 +247,7 @@ Item { Component { id: activityView - StatusListView { + StatusListView { width: scrollView.availableWidth height: scrollView.availableHeight model: root.activityModel @@ -269,4 +276,58 @@ Item { } } } + Component { + id: privilegedCollectibleImage + // Special artwork representation for community `Owner and Master Token` token types: + PrivilegedTokenArtworkPanel { + size: PrivilegedTokenArtworkPanel.Size.Large + artwork: collectible.imageUrl ?? "" + color: !!root.collectible && !!root.communityDetails ? root.communityDetails.color : "transparent" + isOwner: root.isOwnerTokenType + } + } + + Component { + id: collectibleimage + StatusRoundedMedia { + id: collectibleImage + readonly property bool isEmpty: !mediaUrl.toString() && !fallbackImageUrl.toString() + width: 248 + height: width + radius: Style.current.radius + color: isError || isEmpty ? Theme.palette.baseColor5 : collectible.backgroundColor + border.color: Theme.palette.directColor8 + border.width: 1 + mediaUrl: collectible.mediaUrl ?? "" + mediaType: modelIndex === 0 && !!collectible ? collectible.mediaType : "" + fallbackImageUrl: collectible.imageUrl + + Column { + anchors.centerIn: parent + visible: collectibleImage.isError || collectibleImage.isEmpty + spacing: 10 + + StatusIcon { + anchors.horizontalCenter: parent.horizontalCenter + icon: "frowny" + opacity: 0.1 + color: Theme.palette.directColor1 + } + StatusBaseText { + anchors.horizontalCenter: parent.horizontalCenter + horizontalAlignment: Qt.AlignHCenter + color: Theme.palette.directColor6 + text: { + if (collectibleImage.isError && collectibleImage.componentMediaType === StatusRoundedMedia.MediaType.Unkown) { + return qsTr("Unsupported\nfile format") + } + if (!collectible.description && !collectible.name) { + return qsTr("Info can't\nbe fetched") + } + return qsTr("Failed\nto load") + } + } + } + } + } }