From 10c44b8038e0c4b6cf63d9bc77bbf824ea92a9e5 Mon Sep 17 00:00:00 2001 From: Dario Gabriel Lipicar Date: Sun, 25 Feb 2024 23:32:59 -0300 Subject: [PATCH] feat(@desktop/wallet): implement collectible details activity tab Fixes #12311 --- .../wallet_section/activity/controller.nim | 20 +- .../modules/main/wallet_section/module.nim | 21 +- src/app/modules/main/wallet_section/view.nim | 22 ++- storybook/pages/CollectibleDetailViewPage.qml | 134 +++++++++++++ .../src/Models/WalletCollectiblesModel.qml | 162 ++++++++++++++- .../src/Models/WalletTransactionsModel.qml | 187 ++++++++++++++++++ storybook/src/Models/qmldir | 1 + ui/app/AppLayouts/Wallet/stores/RootStore.qml | 3 +- .../AppLayouts/Wallet/views/RightTabView.qml | 17 +- .../collectibles/CollectibleDetailView.qml | 78 ++++++-- .../Wallet/views/collectibles/qmldir | 1 + .../send/views/TabAddressSelectorView.qml | 13 +- .../shared/stores/send/TransactionStore.qml | 3 +- 13 files changed, 627 insertions(+), 35 deletions(-) create mode 100644 storybook/pages/CollectibleDetailViewPage.qml create mode 100644 storybook/src/Models/WalletTransactionsModel.qml diff --git a/src/app/modules/main/wallet_section/activity/controller.nim b/src/app/modules/main/wallet_section/activity/controller.nim index 2bee90f83b..76e586f072 100644 --- a/src/app/modules/main/wallet_section/activity/controller.nim +++ b/src/app/modules/main/wallet_section/activity/controller.nim @@ -197,6 +197,9 @@ QtObject: self.status.setLoadingCollectibles(false) error "error fetching collectibles: ", res.error return + + proc resetFilter*(self: Controller) {.slot.} = + self.currentActivityFilter = backend_activity.getIncludeAllActivityFilter() proc setFilterTime*(self: Controller, startTimestamp: int, endTimestamp: int) {.slot.} = self.currentActivityFilter.period = backend_activity.newPeriod(startTimestamp, endTimestamp) @@ -424,7 +427,7 @@ QtObject: var addresses = newSeq[string]() for i in 0 ..< addressesJson.len: if addressesJson[i].kind != JString: - error "not string entry in the json adday for index ", i + error "not string entry in the addresses json array for index ", i return addresses.add(addressesJson[i].getStr()) @@ -441,6 +444,21 @@ QtObject: self.status.emitFilterChainsChanged() self.updateAssetsIdentities() + proc setFilterChainsJson*(self: Controller, jsonArray: string, allChainsSelected: bool) {.slot.} = + let chainsJson = parseJson(jsonArray) + if chainsJson.kind != JArray: + error "invalid array of json ints" + return + + var chains = newSeq[int]() + for i in 0 ..< chainsJson.len: + if chainsJson[i].kind != JInt: + error "not int entry in the chains json array for index ", i + return + chains.add(chainsJson[i].getInt()) + + self.setFilterChains(chains, allChainsSelected) + proc updateRecipientsModel*(self: Controller) {.slot.} = self.status.setLoadingRecipients(true) let res = backend_activity.getRecipientsAsync(self.requestId, self.chainIds, self.addresses, 0, FETCH_RECIPIENTS_BATCH_COUNT_DEFAULT) diff --git a/src/app/modules/main/wallet_section/module.nim b/src/app/modules/main/wallet_section/module.nim index 456670ec1c..d3a4599c68 100644 --- a/src/app/modules/main/wallet_section/module.nim +++ b/src/app/modules/main/wallet_section/module.nim @@ -51,7 +51,8 @@ export io_interface type ActivityID = enum History - Temporary + Temporary0 + Temporary1 Module* = ref object of io_interface.AccessInterface delegate: delegate_interface.AccessInterface @@ -84,8 +85,11 @@ type activityController: activityc.Controller collectibleDetailsController: collectible_detailsc.Controller - # instance to be used in temporary, short-lived, workflows (e.g. send popup) - tmpActivityController: activityc.Controller + # Instances to be used in temporary, short-lived, workflows (e.g. send popup). There's probably tidier ways of + # doing this (one for each required module, create them dynamically) but for now this will do. + # We need one for each app "layer" that simultaneously needs to show a different list of activity + # entries (e.g. send popup is one "layer" above the collectible details activity tab) + tmpActivityControllers: ActivityControllerArray wcController: wcc.Controller @@ -139,14 +143,18 @@ proc newModule*( result.transactionService = transactionService result.activityController = activityc.newController(int32(ActivityID.History), currencyService, tokenService, savedAddressService, events) - result.tmpActivityController = activityc.newController(int32(ActivityID.Temporary), currencyService, tokenService, + result.tmpActivityControllers = [ + activityc.newController(int32(ActivityID.Temporary0), currencyService, tokenService, + savedAddressService, events), + activityc.newController(int32(ActivityID.Temporary1), currencyService, tokenService, savedAddressService, events) + ] result.collectibleDetailsController = collectible_detailsc.newController(int32(backend_collectibles.CollectiblesRequestID.WalletAccount), networkService, events) result.filter = initFilter(result.controller) result.wcController = wcc.newController(events, walletAccountService) - result.view = newView(result, result.activityController, result.tmpActivityController, result.collectibleDetailsController, result.wcController) + result.view = newView(result, result.activityController, result.tmpActivityControllers, result.collectibleDetailsController, result.wcController) method delete*(self: Module) = self.accountsModule.delete @@ -159,7 +167,8 @@ method delete*(self: Module) = self.controller.delete self.view.delete self.activityController.delete - self.tmpActivityController.delete + for i in 0..self.tmpActivityControllers.len-1: + self.tmpActivityControllers[i].delete self.collectibleDetailsController.delete self.wcController.delete diff --git a/src/app/modules/main/wallet_section/view.nim b/src/app/modules/main/wallet_section/view.nim index 4cb2f73d51..07a88c7ee1 100644 --- a/src/app/modules/main/wallet_section/view.nim +++ b/src/app/modules/main/wallet_section/view.nim @@ -6,6 +6,9 @@ import ./io_interface import ../../shared_models/currency_amount import ./wallet_connect/controller as wcc +type + ActivityControllerArray* = array[2, activityc.Controller] + QtObject: type View* = ref object of QObject @@ -16,7 +19,7 @@ QtObject: tmpAmount: float # shouldn't be used anywhere except in prepare*/getPrepared* procs tmpSymbol: string # shouldn't be used anywhere except in prepare*/getPrepared* procs activityController: activityc.Controller - tmpActivityController: activityc.Controller + tmpActivityControllers: ActivityControllerArray collectibleDetailsController: collectible_detailsc.Controller isNonArchivalNode: bool keypairOperabilityForObservedAccount: string @@ -31,11 +34,11 @@ QtObject: proc delete*(self: View) = self.QObject.delete - proc newView*(delegate: io_interface.AccessInterface, activityController: activityc.Controller, tmpActivityController: activityc.Controller, collectibleDetailsController: collectible_detailsc.Controller, wcController: wcc.Controller): View = + proc newView*(delegate: io_interface.AccessInterface, activityController: activityc.Controller, tmpActivityControllers: ActivityControllerArray, collectibleDetailsController: collectible_detailsc.Controller, wcController: wcc.Controller): View = new(result, delete) result.delegate = delegate result.activityController = activityController - result.tmpActivityController = tmpActivityController + result.tmpActivityControllers = tmpActivityControllers result.collectibleDetailsController = collectibleDetailsController result.wcController = wcController @@ -151,10 +154,15 @@ QtObject: QtProperty[QVariant] collectibleDetailsController: read = getCollectibleDetailsController - proc getTmpActivityController(self: View): QVariant {.slot.} = - return newQVariant(self.tmpActivityController) - QtProperty[QVariant] tmpActivityController: - read = getTmpActivityController + proc getTmpActivityController0(self: View): QVariant {.slot.} = + return newQVariant(self.tmpActivityControllers[0]) + QtProperty[QVariant] tmpActivityController0: + read = getTmpActivityController0 + + proc getTmpActivityController1(self: View): QVariant {.slot.} = + return newQVariant(self.tmpActivityControllers[1]) + QtProperty[QVariant] tmpActivityController1: + read = getTmpActivityController1 proc getLatestBlockNumber*(self: View, chainId: int): string {.slot.} = return self.delegate.getLatestBlockNumber(chainId) diff --git a/storybook/pages/CollectibleDetailViewPage.qml b/storybook/pages/CollectibleDetailViewPage.qml new file mode 100644 index 0000000000..dc02c12f4a --- /dev/null +++ b/storybook/pages/CollectibleDetailViewPage.qml @@ -0,0 +1,134 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +import Storybook 1.0 + +import AppLayouts.Wallet 1.0 +import AppLayouts.Wallet.stores 1.0 as WalletStores +import AppLayouts.Wallet.views.collectibles 1.0 + +import StatusQ.Core.Utils 0.1 + +import shared.controls 1.0 +import shared.stores 1.0 + +import Models 1.0 +import utils 1.0 + +SplitView { + id: root + + QtObject { + id: d + + readonly property QtObject collectiblesModel: WalletCollectiblesModel { + Component.onCompleted: { + d.refreshCurrentCollectible() + } + } + property var currentCollectible + + function refreshCurrentCollectible() { + currentCollectible = ModelUtils.get(collectiblesModel, collectibleComboBox.currentIndex) + } + + readonly property QtObject transactionsModel: WalletTransactionsModel{} + } + + SplitView { + orientation: Qt.Vertical + SplitView.fillWidth: true + Item { + SplitView.fillWidth: true + SplitView.fillHeight: true + + Rectangle { + anchors.fill: viewLoader + anchors.margins: -1 + color: "transparent" + border.width: 1 + border.color: "#808080" + } + + Loader { + id: viewLoader + anchors.fill: parent + anchors.margins: 50 + + active: false + + sourceComponent: CollectibleDetailView { + collectible: d.currentCollectible + isCollectibleLoading: isLoadingCheckbox.checked + activityModel: d.transactionsModel + rootStore: QtObject { + readonly property string currentCurrency: "EUR" + + function getFiatValue(cryptoValue, symbol) { + return cryptoValue * 0.1; + } + + function formatCurrencyAmount(cryptoValue, symbol) { + return "%L1 %2".arg(cryptoValue).arg(symbol) + } + + function getNetworkFullName(chainId) { + return chainId + } + + function getNetworkColor(chainId) { + return "pink" + } + } + walletRootStore: QtObject { + function getNameForAddress(address) { + return "NAMEFOR: %1".arg(address) + } + + readonly property bool showAllAccounts: true + } + communitiesStore: QtObject { + function getCommunityDetailsAsJson(communityId) { + return "" + } + } + } + Component.onCompleted: viewLoader.active = true + } + } + + LogsAndControlsPanel { + SplitView.minimumHeight: 100 + SplitView.preferredHeight: 150 + + SplitView.fillWidth: true + } + } + + Pane { + SplitView.minimumWidth: 300 + SplitView.preferredWidth: 300 + + ColumnLayout { + Label { + text: "Collectible:" + } + ComboBox { + id: collectibleComboBox + Layout.fillWidth: true + textRole: "name" + model: d.collectiblesModel + currentIndex: 0 + onCurrentIndexChanged: d.refreshCurrentCollectible() + } + CheckBox { + id: isLoadingCheckbox + text: "isLoading" + checked: false + } + } + } +} + +// category: Wallet diff --git a/storybook/src/Models/WalletCollectiblesModel.qml b/storybook/src/Models/WalletCollectiblesModel.qml index 8ccc0c73a7..d514c6caa3 100644 --- a/storybook/src/Models/WalletCollectiblesModel.qml +++ b/storybook/src/Models/WalletCollectiblesModel.qml @@ -1,5 +1,8 @@ import QtQuick 2.15 +import StatusQ.Core 0.1 +import utils 1.0 + ListModel { readonly property var rootData: [ { @@ -9,8 +12,29 @@ ListModel { 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", @@ -19,8 +43,29 @@ ListModel { 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", @@ -29,8 +74,29 @@ ListModel { 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", @@ -39,8 +105,29 @@ ListModel { 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", @@ -49,8 +136,29 @@ ListModel { 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" + 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", @@ -59,8 +167,21 @@ ListModel { 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", @@ -69,8 +190,21 @@ ListModel { 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", @@ -79,8 +213,34 @@ ListModel { 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 } ] diff --git a/storybook/src/Models/WalletTransactionsModel.qml b/storybook/src/Models/WalletTransactionsModel.qml new file mode 100644 index 0000000000..d09b19f1e7 --- /dev/null +++ b/storybook/src/Models/WalletTransactionsModel.qml @@ -0,0 +1,187 @@ +import QtQuick 2.15 + +import StatusQ 0.1 +import StatusQ.Core 0.1 + +import utils 1.0 + +ListModel { + readonly property var rootData: [ + { + activityEntry: { + timestamp: Date.now() / 1000, + status: 0, + amount: 123.45, + inAmount: 123.45, + outAmount: 123.45, + symbol: "SNT", + inSymbol: "SNT", + outSymbol: "SNT", + isMultiTransaction: false, + txType: 0, + sender: "0xfB8131c260749c7835a08ccBdb64728De432858E", + recipient: "0x3fb81384583b3910BB14Cc72582E8e8a56E83ae9", + isNFT: false, + isCommunityAssetViaAirdrop: false, + communityName: "Doodles", + communityImageUrl: Style.png("collectibles/HappyMeow"), + tokenID: "4981676894159712808201908443964193325271219637660871887967796332739046670337", + tokenAddress: "0xdeadbeef", + tokenInAddress: "0xdeadbeef-00", + tokenOutAddress: "0xdeadbeef-00", + nftName: "Happy Meow NFT", + nftImageUrl: Style.png("collectibles/HappyMeow"), + chainId: "NETWORKID", + chainIdIn: "NETWORKID-IN", + chainIdOut: "NETWORKID-OUT" + } + }, + { + activityEntry: { + timestamp: Date.now() / 1000, + status: 0, + amount: 123.45, + inAmount: 123.45, + outAmount: 123.45, + symbol: "SNT", + inSymbol: "SNT", + outSymbol: "SNT", + isMultiTransaction: false, + txType: 0, + sender: "0xfB8131c260749c7835a08ccBdb64728De432858E", + recipient: "0x3fb81384583b3910BB14Cc72582E8e8a56E83ae9", + isNFT: false, + isCommunityAssetViaAirdrop: false, + communityName: "Doodles", + communityImageUrl: Style.png("collectibles/HappyMeow"), + tokenID: "4981676894159712808201908443964193325271219637660871887967796332739046670337", + tokenAddress: "0xdeadbeef", + tokenInAddress: "0xdeadbeef-00", + tokenOutAddress: "0xdeadbeef-00", + nftName: "Happy Meow NFT", + nftImageUrl: Style.png("collectibles/HappyMeow"), + chainId: "NETWORKID", + chainIdIn: "NETWORKID-IN", + chainIdOut: "NETWORKID-OUT" + } + }, + { + activityEntry: { + timestamp: Date.now() / 1000, + status: 0, + amount: 123.45, + inAmount: 123.45, + outAmount: 123.45, + symbol: "SNT", + inSymbol: "SNT", + outSymbol: "SNT", + isMultiTransaction: false, + txType: 0, + sender: "0xfB8131c260749c7835a08ccBdb64728De432858E", + recipient: "0x3fb81384583b3910BB14Cc72582E8e8a56E83ae9", + isNFT: false, + isCommunityAssetViaAirdrop: false, + communityName: "Doodles", + communityImageUrl: Style.png("collectibles/HappyMeow"), + tokenID: "4981676894159712808201908443964193325271219637660871887967796332739046670337", + tokenAddress: "0xdeadbeef", + tokenInAddress: "0xdeadbeef-00", + tokenOutAddress: "0xdeadbeef-00", + nftName: "Happy Meow NFT", + nftImageUrl: Style.png("collectibles/HappyMeow"), + chainId: "NETWORKID", + chainIdIn: "NETWORKID-IN", + chainIdOut: "NETWORKID-OUT" + } + }, + { + activityEntry: { + timestamp: Date.now() / 1000, + status: 0, + amount: 123.45, + inAmount: 123.45, + outAmount: 123.45, + symbol: "SNT", + inSymbol: "SNT", + outSymbol: "SNT", + isMultiTransaction: false, + txType: 0, + sender: "0xfB8131c260749c7835a08ccBdb64728De432858E", + recipient: "0x3fb81384583b3910BB14Cc72582E8e8a56E83ae9", + isNFT: false, + isCommunityAssetViaAirdrop: false, + communityName: "Doodles", + communityImageUrl: Style.png("collectibles/HappyMeow"), + tokenID: "4981676894159712808201908443964193325271219637660871887967796332739046670337", + tokenAddress: "0xdeadbeef", + tokenInAddress: "0xdeadbeef-00", + tokenOutAddress: "0xdeadbeef-00", + nftName: "Happy Meow NFT", + nftImageUrl: Style.png("collectibles/HappyMeow"), + chainId: "NETWORKID", + chainIdIn: "NETWORKID-IN", + chainIdOut: "NETWORKID-OUT" + } + }, + { + activityEntry: { + timestamp: Date.now() / 1000, + status: 0, + amount: 123.45, + inAmount: 123.45, + outAmount: 123.45, + symbol: "SNT", + inSymbol: "SNT", + outSymbol: "SNT", + isMultiTransaction: false, + txType: 0, + sender: "0xfB8131c260749c7835a08ccBdb64728De432858E", + recipient: "0x3fb81384583b3910BB14Cc72582E8e8a56E83ae9", + isNFT: false, + isCommunityAssetViaAirdrop: false, + communityName: "Doodles", + communityImageUrl: Style.png("collectibles/HappyMeow"), + tokenID: "4981676894159712808201908443964193325271219637660871887967796332739046670337", + tokenAddress: "0xdeadbeef", + tokenInAddress: "0xdeadbeef-00", + tokenOutAddress: "0xdeadbeef-00", + nftName: "Happy Meow NFT", + nftImageUrl: Style.png("collectibles/HappyMeow"), + chainId: "NETWORKID", + chainIdIn: "NETWORKID-IN", + chainIdOut: "NETWORKID-OUT" + } + }, + { + activityEntry: { + timestamp: Date.now() / 1000, + status: 0, + amount: 123.45, + inAmount: 123.45, + outAmount: 123.45, + symbol: "SNT", + inSymbol: "SNT", + outSymbol: "SNT", + isMultiTransaction: false, + txType: 0, + sender: "0xfB8131c260749c7835a08ccBdb64728De432858E", + recipient: "0x3fb81384583b3910BB14Cc72582E8e8a56E83ae9", + isNFT: false, + isCommunityAssetViaAirdrop: false, + communityName: "Doodles", + communityImageUrl: Style.png("collectibles/HappyMeow"), + tokenID: "4981676894159712808201908443964193325271219637660871887967796332739046670337", + tokenAddress: "0xdeadbeef", + tokenInAddress: "0xdeadbeef-00", + tokenOutAddress: "0xdeadbeef-00", + nftName: "Happy Meow NFT", + nftImageUrl: Style.png("collectibles/HappyMeow"), + chainId: "NETWORKID", + chainIdIn: "NETWORKID-IN", + chainIdOut: "NETWORKID-OUT" + } + }, + ] + + Component.onCompleted: append(rootData) +} diff --git a/storybook/src/Models/qmldir b/storybook/src/Models/qmldir index f4e95baa10..ebd65a9e77 100644 --- a/storybook/src/Models/qmldir +++ b/storybook/src/Models/qmldir @@ -19,6 +19,7 @@ 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 GroupedAccountsAssetsModel 1.0 GroupedAccountsAssetsModel.qml TokensBySymbolModel 1.0 TokensBySymbolModel.qml CommunitiesModel 1.0 CommunitiesModel.qml diff --git a/ui/app/AppLayouts/Wallet/stores/RootStore.qml b/ui/app/AppLayouts/Wallet/stores/RootStore.qml index 04f79798ea..b230ed69fa 100644 --- a/ui/app/AppLayouts/Wallet/stores/RootStore.qml +++ b/ui/app/AppLayouts/Wallet/stores/RootStore.qml @@ -50,7 +50,8 @@ QtObject { property var walletSectionSavedAddressesInst: walletSectionSavedAddresses property var totalCurrencyBalance: walletSectionInst.totalCurrencyBalance property var activityController: walletSectionInst.activityController - property var tmpActivityController: walletSectionInst.tmpActivityController + property var tmpActivityController0: walletSectionInst.tmpActivityController0 + property var tmpActivityController1: walletSectionInst.tmpActivityController1 property string signingPhrase: walletSectionInst.signingPhrase property string mnemonicBackedUp: walletSectionInst.isMnemonicBackedUp property var walletConnectController: walletSectionInst.walletConnectController diff --git a/ui/app/AppLayouts/Wallet/views/RightTabView.qml b/ui/app/AppLayouts/Wallet/views/RightTabView.qml index cbf9b7d432..47a278b5f5 100644 --- a/ui/app/AppLayouts/Wallet/views/RightTabView.qml +++ b/ui/app/AppLayouts/Wallet/views/RightTabView.qml @@ -8,7 +8,7 @@ import StatusQ.Core.Theme 0.1 import utils 1.0 import shared.controls 1.0 import shared.views 1.0 -import shared.stores 1.0 +import shared.stores 1.0 as SharedStores import shared.panels 1.0 import "./" @@ -69,6 +69,8 @@ RightTabBaseView { return "" } } + + readonly property var detailedCollectibleActivityController: RootStore.tmpActivityController0 } ColumnLayout { @@ -174,6 +176,12 @@ RightTabBaseView { onCollectibleClicked: { RootStore.collectiblesStore.getDetailedCollectible(chainId, contractAddress, tokenId) RootStore.setCurrentViewedHolding(uid, Constants.TokenType.ERC721) + d.detailedCollectibleActivityController.resetFilter() + d.detailedCollectibleActivityController.setFilterAddressesJson(JSON.stringify(RootStore.addressFilters.split(":")), RootStore.showAllAccounts) + d.detailedCollectibleActivityController.setFilterChainsJson(JSON.stringify([chainId]), false) + d.detailedCollectibleActivityController.setFilterCollectibles(JSON.stringify([uid])) + d.detailedCollectibleActivityController.updateFilter() + stack.currentIndex = 1 } onSendRequested: (symbol) => { @@ -212,10 +220,15 @@ RightTabBaseView { CollectibleDetailView { collectible: RootStore.collectiblesStore.detailedCollectible isCollectibleLoading: RootStore.collectiblesStore.isDetailedCollectibleLoading + activityModel: d.detailedCollectibleActivityController.model + rootStore: SharedStores.RootStore + walletRootStore: RootStore + communitiesStore: root.communitiesStore onVisibleChanged: { - if (!visible) + if (!visible) { RootStore.resetCurrentViewedHolding(Constants.TokenType.ERC721) + } } } AssetsDetailView { diff --git a/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleDetailView.qml b/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleDetailView.qml index 3d522a3f3a..bd577fbac8 100644 --- a/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleDetailView.qml +++ b/ui/app/AppLayouts/Wallet/views/collectibles/CollectibleDetailView.qml @@ -11,6 +11,7 @@ import AppLayouts.Communities.panels 1.0 import utils 1.0 import shared.controls 1.0 +import shared.views 1.0 import "../../stores" import "../../controls" @@ -18,7 +19,12 @@ import "../../controls" Item { id: root - property var collectible + required property var rootStore + required property var walletRootStore + required property var communitiesStore + + required property var collectible + property var activityModel property bool isCollectibleLoading readonly property int isNarrowMode : width < 700 @@ -70,7 +76,7 @@ Item { visible: root.isCommunityCollectible && (root.isOwnerTokenType || root.isTMasterTokenType) size: root.isNarrowMode ? PrivilegedTokenArtworkPanel.Size.Medium : PrivilegedTokenArtworkPanel.Size.Large artwork: collectible.imageUrl - color: !!collectible ? collectible.communityColor : "transparent" + color: !!collectible && root.isCommunityCollectible? collectible.communityColor : "transparent" isOwner: root.isOwnerTokenType } @@ -86,8 +92,8 @@ Item { color: collectible.backgroundColor border.color: Theme.palette.directColor8 border.width: 1 - mediaUrl: collectible.mediaUrl - mediaType: collectible.mediaType + mediaUrl: collectible.mediaUrl ?? "" + mediaType: collectible.mediaType ?? "" fallbackImageUrl: collectible.imageUrl } @@ -147,6 +153,11 @@ Item { width: implicitWidth text: qsTr("Properties") } + StatusTabButton { + rightPadding: 0 + width: implicitWidth + text: qsTr("Activity") + } } StatusScrollView { @@ -154,15 +165,56 @@ Item { Layout.fillWidth: true Layout.fillHeight: true contentWidth: availableWidth - Flow { - width: scrollView.availableWidth - spacing: 10 - Repeater { - model: collectible.traits - InformationTile { - maxWidth: parent.width - primaryText: model.traitType - secondaryText: model.value + + Loader { + id: tabLoader + Layout.fillWidth: true + Layout.fillHeight: true + sourceComponent: { + switch (collectiblesDetailsTab.currentIndex) { + case 0: return traitsView + case 1: return activityView + } + } + + Component { + id: traitsView + Flow { + width: scrollView.availableWidth + spacing: 10 + Repeater { + model: collectible.traits + InformationTile { + maxWidth: parent.width + primaryText: model.traitType + secondaryText: model.value + } + } + } + } + + Component { + id: activityView + StatusListView { + width: scrollView.availableWidth + height: scrollView.availableHeight + model: root.activityModel + delegate: TransactionDelegate { + required property var model + required property int index + width: parent.width + modelData: model.activityEntry + timeStampText: isModelDataValid ? LocaleUtils.formatRelativeTimestamp(modelData.timestamp * 1000, true) : "" + rootStore: root.rootStore + walletRootStore: root.walletRootStore + showAllAccounts: root.walletRootStore.showAllAccounts + displayValues: true + community: isModelDataValid && !!communityId && !!root.communitiesStore ? root.communitiesStore.getCommunityDetailsAsJson(communityId) : null + loading: false + onClicked: { + // TODO: Implement switch to transaction details screen + } + } } } } diff --git a/ui/app/AppLayouts/Wallet/views/collectibles/qmldir b/ui/app/AppLayouts/Wallet/views/collectibles/qmldir index 12d9db8c93..02f16f67b1 100644 --- a/ui/app/AppLayouts/Wallet/views/collectibles/qmldir +++ b/ui/app/AppLayouts/Wallet/views/collectibles/qmldir @@ -1 +1,2 @@ +CollectibleDetailView 1.0 CollectibleDetailView.qml CollectibleView 1.0 CollectibleView.qml diff --git a/ui/imports/shared/popups/send/views/TabAddressSelectorView.qml b/ui/imports/shared/popups/send/views/TabAddressSelectorView.qml index b254c6b912..c5cdf617fe 100644 --- a/ui/imports/shared/popups/send/views/TabAddressSelectorView.qml +++ b/ui/imports/shared/popups/send/views/TabAddressSelectorView.qml @@ -37,6 +37,13 @@ Item { None } + QtObject { + id: d + + // Use Layer1 controller since this could go on top of other activity lists + readonly property var activityController: root.store.tmpActivityController1 + } + StatusTabBar { id: accountSelectionTabBar anchors.top: parent.top @@ -174,7 +181,7 @@ Item { onClicked: recipientSelected(entry, TabAddressSelectorView.Type.RecentsAddress) } - model: root.store.tmpActivityController.model + model: d.activityController.model onVisibleChanged: { if (visible) { @@ -193,9 +200,9 @@ Item { function updateRecentsActivity() { if(root.selectedAccount) { - root.store.tmpActivityController.setFilterAddressesJson(JSON.stringify([root.selectedAccount.address], false)) + d.activityController.setFilterAddressesJson(JSON.stringify([root.selectedAccount.address]), false) } - root.store.tmpActivityController.updateFilter() + d.activityController.updateFilter() } } } diff --git a/ui/imports/shared/stores/send/TransactionStore.qml b/ui/imports/shared/stores/send/TransactionStore.qml index 9e7a375ef6..decc500a66 100644 --- a/ui/imports/shared/stores/send/TransactionStore.qml +++ b/ui/imports/shared/stores/send/TransactionStore.qml @@ -30,7 +30,8 @@ QtObject { property var collectiblesModel: walletSectionSendInst.collectiblesModel property var nestedCollectiblesModel: walletSectionSendInst.nestedCollectiblesModel property bool areTestNetworksEnabled: networksModule.areTestNetworksEnabled - property var tmpActivityController: walletSection.tmpActivityController + property var tmpActivityController0: walletSection.tmpActivityController0 + property var tmpActivityController1: walletSection.tmpActivityController1 property var savedAddressesModel: SortFilterProxyModel { sourceModel: walletSectionSavedAddresses.model filters: [