diff --git a/src/app/wallet/view.nim b/src/app/wallet/view.nim index 84956bd988..a980ad26e3 100644 --- a/src/app/wallet/view.nim +++ b/src/app/wallet/view.nim @@ -10,7 +10,7 @@ QtObject: WalletView* = ref object of QAbstractListModel accounts*: AccountList currentAssetList*: AssetList - currentCollectiblesList*: CollectiblesList + currentCollectiblesLists*: CollectiblesList currentAccount: AccountItemView currentTransactions: TransactionList status: Status @@ -40,7 +40,7 @@ QtObject: result.currentAccount = newAccountItemView() result.currentAssetList = newAssetList() result.currentTransactions = newTransactionList() - result.currentCollectiblesList = newCollectiblesList() + result.currentCollectiblesLists = newCollectiblesList() result.totalFiatBalance = "" result.etherscanLink = "" result.safeLowGasPrice = "0" @@ -79,19 +79,19 @@ QtObject: proc setCurrentAssetList*(self: WalletView, assetList: seq[Asset]) - proc currentCollectiblesListChanged*(self: WalletView) {.signal.} + proc currentCollectiblesListsChanged*(self: WalletView) {.signal.} - proc getCurrentCollectiblesList(self: WalletView): QVariant {.slot.} = - return newQVariant(self.currentCollectiblesList) + proc getCurrentCollectiblesLists(self: WalletView): QVariant {.slot.} = + return newQVariant(self.currentCollectiblesLists) - proc setCurrentCollectiblesList*(self: WalletView, collectibles: seq[Collectible]) = - self.currentCollectiblesList.setNewData(collectibles) - self.currentCollectiblesListChanged() + proc setCurrentCollectiblesLists*(self: WalletView, collectiblesLists: seq[CollectibleList]) = + self.currentCollectiblesLists.setNewData(collectiblesLists) + self.currentCollectiblesListsChanged() - QtProperty[QVariant] collectibles: - read = getCurrentCollectiblesList - write = setCurrentCollectiblesList - notify = currentCollectiblesListChanged + QtProperty[QVariant] collectiblesLists: + read = getCurrentCollectiblesLists + write = setCurrentCollectiblesLists + notify = currentCollectiblesListsChanged proc currentTransactionsChanged*(self: WalletView) {.signal.} @@ -122,7 +122,8 @@ QtObject: self.setCurrentAssetList(selectedAccount.assetList) # Display currently known collectibles, and get latest from API/Contracts - self.setCurrentCollectiblesList(selectedAccount.collectibles) + self.setCurrentCollectiblesLists(selectedAccount.collectiblesLists) + # TODO only load if the list is empty self.loadCollectiblesForAccount(selectedAccount.address) # Display currently known transactions, and get latest transactions from status-go self.setCurrentTransactions(selectedAccount.transactions) @@ -267,26 +268,53 @@ QtObject: proc addCustomToken*(self: WalletView, address: string, name: string, symbol: string, decimals: string) {.slot.} = self.status.wallet.toggleAsset(symbol, true, address, name, parseInt(decimals), "") - proc loadingCollectibles*(self: WalletView, isLoading: bool) {.signal.} - proc loadCollectiblesForAccount*(self: WalletView, address: string) {.slot.} = - self.loadingCollectibles(true) - spawnAndSend(self, "setCollectiblesResult ") do: + # Add loading state if it is the current account + if address == self.currentAccount.address: + for collectibleType in status_collectibles.COLLECTIBLE_TYPES: + self.currentCollectiblesLists.addCollectibleListToList(CollectibleList( + collectibleType: collectibleType, + collectiblesJSON: "[]", + error: "", + loading: 1 + )) + + # TODO find a way to use a loop to streamline this code + # Spawn for each collectible. They can end in whichever order + spawnAndSend(self, "setCollectiblesResult") do: $(%*{ "address": address, - "collectibles": status_collectibles.getAllCollectibles(address) + "collectibleType": status_collectibles.CRYPTOKITTY, + "collectibles": status_collectibles.getCryptoKitties(address) + }) + spawnAndSend(self, "setCollectiblesResult") do: + $(%*{ + "address": address, + "collectibleType": status_collectibles.KUDO, + "collectibles": status_collectibles.getKudos(address) + }) + spawnAndSend(self, "setCollectiblesResult") do: + $(%*{ + "address": address, + "collectibleType": status_collectibles.ETHERMON, + "collectibles": status_collectibles.getEthermons(address) }) proc setCollectiblesResult(self: WalletView, collectiblesJSON: string) {.slot.} = let collectibleData = parseJson(collectiblesJSON) - let collectibles = collectibleData["collectibles"].to(seq[Collectible]); let address = collectibleData["address"].getStr - let index = self.accounts.getAccountindexByAddress(address) - if index == -1: return - self.accounts.getAccount(index).collectibles = collectibles + + # TODO Add the collectibleData to the Wallet account + # let index = self.accounts.getAccountindexByAddress(address) + # if index == -1: return + # self.accounts.getAccount(index).collectiblesLists = collectiblesList if address == self.currentAccount.address: - self.setCurrentCollectiblesList(collectibles) - self.loadingCollectibles(false) + # Add CollectibleListJSON to the right list + # TODO check if instead we need to set an error + self.currentCollectiblesLists.setCollectiblesJSONByType( + collectibleData["collectibleType"].getStr, + $collectibleData["collectibles"] + ) proc loadingTrxHistory*(self: WalletView, isLoading: bool) {.signal.} diff --git a/src/app/wallet/views/collectibles_list.nim b/src/app/wallet/views/collectibles_list.nim index 5996e52a95..138eeabc1c 100644 --- a/src/app/wallet/views/collectibles_list.nim +++ b/src/app/wallet/views/collectibles_list.nim @@ -1,66 +1,68 @@ import NimQml, tables -from ../../../status/wallet import Collectible +from ../../../status/wallet import CollectibleList type CollectiblesRoles {.pure.} = enum - Name = UserRole + 1, - Image = UserRole + 2 - CollectibleId = UserRole + 3 - CollectibleType = UserRole + 4 - Description = UserRole + 5 - ExternalUrl = UserRole + 6 + CollectibleType = UserRole + 1 + CollectiblesJSON = UserRole + 2 + Error = UserRole + 3 + Loading = UserRole + 4 QtObject: type CollectiblesList* = ref object of QAbstractListModel - collectibles*: seq[Collectible] + collectibleLists*: seq[CollectibleList] proc setup(self: CollectiblesList) = self.QAbstractListModel.setup - proc delete(self: CollectiblesList) = - self.QAbstractListModel.delete - self.collectibles = @[] - - proc newCollectiblesList*(): CollectiblesList = - new(result, delete) - result.collectibles = @[] - result.setup - - method rowCount(self: CollectiblesList, index: QModelIndex = nil): int = - return self.collectibles.len - - method data(self: CollectiblesList, index: QModelIndex, role: int): QVariant = - if not index.isValid: - return - if index.row < 0 or index.row >= self.collectibles.len: - return - let collectible = self.collectibles[index.row] - let collectibleRole = role.CollectiblesRoles - case collectibleRole: - of CollectiblesRoles.Name: result = newQVariant(collectible.name) - of CollectiblesRoles.Image: result = newQVariant(collectible.image) - of CollectiblesRoles.CollectibleId: result = newQVariant(collectible.id) - of CollectiblesRoles.CollectibleType: result = newQVariant(collectible.collectibleType) - of CollectiblesRoles.Description: result = newQVariant(collectible.description) - of CollectiblesRoles.ExternalUrl: result = newQVariant(collectible.externalUrl) - - method roleNames(self: CollectiblesList): Table[int, string] = - { CollectiblesRoles.Name.int:"name", - CollectiblesRoles.Image.int:"image", - CollectiblesRoles.CollectibleId.int:"collectibleId", - CollectiblesRoles.CollectibleType.int:"collectibleType", - CollectiblesRoles.Description.int:"description", - CollectiblesRoles.ExternalUrl.int:"externalUrl" }.toTable - - proc addCollectibleToList*(self: CollectiblesList, colelctible: Collectible) = - self.beginInsertRows(newQModelIndex(), self.collectibles.len, self.collectibles.len) - self.collectibles.add(colelctible) - self.endInsertRows() - - proc setNewData*(self: CollectiblesList, collectibles: seq[Collectible]) = - self.beginResetModel() - self.collectibles = collectibles - self.endResetModel() - proc forceUpdate*(self: CollectiblesList) = self.beginResetModel() self.endResetModel() + + proc delete(self: CollectiblesList) = + self.QAbstractListModel.delete + self.collectibleLists = @[] + + proc newCollectiblesList*(): CollectiblesList = + new(result, delete) + result.collectibleLists = @[] + result.setup + + proc setCollectiblesJSONByType*(self: CollectiblesList, collectibleType: string, collectiblesJSON: string) = + for collectibleList in self.collectibleLists: + if collectibleList.collectibleType == collectibleType: + collectibleList.collectiblesJSON = collectiblesJSON + collectibleList.loading = 0 + self.forceUpdate() + break + + method rowCount(self: CollectiblesList, index: QModelIndex = nil): int = + return self.collectibleLists.len + + method data(self: CollectiblesList, index: QModelIndex, role: int): QVariant = + if not index.isValid: + return + if index.row < 0 or index.row >= self.collectibleLists.len: + return + let collectibleList = self.collectibleLists[index.row] + let collectibleRole = role.CollectiblesRoles + case collectibleRole: + of CollectiblesRoles.CollectibleType: result = newQVariant(collectibleList.collectibleType) + of CollectiblesRoles.CollectiblesJSON: result = newQVariant(collectibleList.collectiblesJSON) + of CollectiblesRoles.Error: result = newQVariant(collectibleList.error) + of CollectiblesRoles.Loading: result = newQVariant(collectibleList.loading) + + method roleNames(self: CollectiblesList): Table[int, string] = + { CollectiblesRoles.CollectibleType.int:"collectibleType", + CollectiblesRoles.CollectiblesJSON.int:"collectiblesJSON", + CollectiblesRoles.Error.int:"error", + CollectiblesRoles.Loading.int:"loading" }.toTable + + proc addCollectibleListToList*(self: CollectiblesList, collectibleList: CollectibleList) = + self.beginInsertRows(newQModelIndex(), self.collectibleLists.len, self.collectibleLists.len) + self.collectibleLists.add(collectibleList) + self.endInsertRows() + + proc setNewData*(self: CollectiblesList, collectibleLists: seq[CollectibleList]) = + self.beginResetModel() + self.collectibleLists = collectibleLists + self.endResetModel() diff --git a/src/status/wallet.nim b/src/status/wallet.nim index 5aa1e32dd0..03fd15081f 100644 --- a/src/status/wallet.nim +++ b/src/status/wallet.nim @@ -186,8 +186,6 @@ proc getTransfersByAddress*(self: WalletModel, address: string): seq[Transaction proc validateMnemonic*(self: WalletModel, mnemonic: string): string = result = status_wallet.validateMnemonic(mnemonic).parseJSON()["error"].getStr -proc getAllCollectibles*(self: WalletModel, address: string): seq[Collectible] = getAllCollectibles(address) - proc getGasPricePredictions*(self: WalletModel): GasPricePrediction = try: let url: string = fmt"https://etherchain.org/api/gasPriceOracle" diff --git a/src/status/wallet/account.nim b/src/status/wallet/account.nim index 5e5969514e..8d96e1cb45 100644 --- a/src/status/wallet/account.nim +++ b/src/status/wallet/account.nim @@ -1,6 +1,10 @@ from eventemitter import Args import ../libstatus/types +type CollectibleList* = ref object + collectibleType*, collectiblesJSON*, error*: string + loading*: int + type Collectible* = ref object name*, image*, id*, collectibleType*, description*, externalUrl*: string @@ -15,7 +19,7 @@ type WalletAccount* = ref object realFiatBalance*: float assetList*: seq[Asset] wallet*, chat*: bool - collectibles*: seq[Collectible] + collectiblesLists*: seq[CollectibleList] transactions*: seq[Transaction] type AccountArgs* = ref object of Args diff --git a/src/status/wallet/collectibles.nim b/src/status/wallet/collectibles.nim index cee201b462..cd7d2eea7e 100644 --- a/src/status/wallet/collectibles.nim +++ b/src/status/wallet/collectibles.nim @@ -7,9 +7,11 @@ import eth/common/eth_types import ../libstatus/types import account -const CRYPTOKITTY = "cryptokitty" -const KUDO = "kudo" -const ETHERMON = "ethermon" +const CRYPTOKITTY* = "cryptokitty" +const KUDO* = "kudo" +const ETHERMON* = "ethermon" + +const COLLECTIBLE_TYPES* = [CRYPTOKITTY, KUDO, ETHERMON] proc getTokenUri(contract: Contract, tokenId: Stuint[256]): string = try: @@ -89,6 +91,10 @@ proc getCryptoKitties*(address: EthAddress): seq[Collectible] = except Exception as e: error "Error getting Cryptokitties", msg = e.msg +proc getCryptoKitties*(address: string): seq[Collectible] = + let eth_address = parseAddress(address) + result = getCryptoKitties(eth_address) + proc getEthermons*(address: EthAddress): seq[Collectible] = result = @[] try: @@ -120,6 +126,10 @@ proc getEthermons*(address: EthAddress): seq[Collectible] = except Exception as e: error "Error getting Ethermons", msg = e.msg +proc getEthermons*(address: string): seq[Collectible] = + let eth_address = parseAddress(address) + result = getEthermons(eth_address) + proc getKudos*(address: EthAddress): seq[Collectible] = result = @[] try: @@ -152,6 +162,6 @@ proc getKudos*(address: EthAddress): seq[Collectible] = except Exception as e: error "Error getting Kudos", msg = e.msg -proc getAllCollectibles*(address: string): seq[Collectible] = +proc getKudos*(address: string): seq[Collectible] = let eth_address = parseAddress(address) - result = concat(getCryptoKitties(eth_address), getEthermons(eth_address), getKudos(eth_address)) + result = getKudos(eth_address) diff --git a/ui/app/AppLayouts/Wallet/CollectiblesTab.qml b/ui/app/AppLayouts/Wallet/CollectiblesTab.qml index b8f6930695..84298a569d 100644 --- a/ui/app/AppLayouts/Wallet/CollectiblesTab.qml +++ b/ui/app/AppLayouts/Wallet/CollectiblesTab.qml @@ -3,86 +3,61 @@ import QtGraphicalEffects 1.13 import "../../../imports" import "../../../shared" import "./components/collectiblesComponents" +import "./components/collectiblesComponents/collectiblesData.js" as CollectiblesData Item { - property bool isLoading: true id: root - Loader { - active: true - sourceComponent: root.isLoading || walletModel.collectibles.rowCount() > 0 ? collectiblesListComponent - : noCollectiblesComponent - width: parent.width + StyledText { + id: noCollectiblesText + color: Style.current.secondaryText + text: qsTr("Collectibles will appear here") + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + font.pixelSize: 15 } - Component { - id: noCollectiblesComponent - - StyledText { - color: Style.current.secondaryText - text: qsTr("Collectibles will appear here") - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - font.pixelSize: 15 - visible: !root.isLoading && walletModel.collectibles.rowCount() === 0 - } - } - - CollectiblesModal { id: collectiblesModalComponent } - Component { - id: collectiblesListComponent - - Column { - spacing: Style.current.halfPadding - anchors.fill: parent - - CollectiblesContainer { - collectibleName: "CryptoKitties" - collectibleType: Constants.cryptokitty - collectibleIconSource: "../../img/collectibles/CryptoKitties.png" - isLoading: root.isLoading - collectiblesModal: collectiblesModalComponent - buttonText: qsTr("View in Cryptokitties") - getLink: function (id) { - return `https://www.cryptokitties.co/kitty/${id}` - } - } - - CollectiblesContainer { - collectibleName: "Ethermons" - collectibleType: Constants.ethermon - collectibleIconSource: "../../img/collectibles/ethermons.png" - isLoading: root.isLoading - collectiblesModal: collectiblesModalComponent - buttonText: qsTr("View in Ethermon") - getLink: function (id) { - // TODO find a more direct URL - return "https://ethermon.io/inventory" - } - } - - CollectiblesContainer { - collectibleName: "Kudos" - collectibleType: Constants.kudo - collectibleIconSource: "../../img/collectibles/kudos.png" - isLoading: root.isLoading - collectiblesModal: collectiblesModalComponent - buttonText: qsTr("View in Gitcoin") - getLink: function (id, externalUrl) { - return externalUrl - } + function checkCollectiblesVisibility() { + // Show the collectibles section only if at least one of the sub-items is visible + // Sub-items are visible only if they are loading or have more than zero collectible + let showCollectibles = false + let currentItem + for (let i = 0; i < collectiblesRepeater.count; i++) { + currentItem = collectiblesRepeater.itemAt(i) + if (currentItem && currentItem.active) { + showCollectibles = true + break } } + noCollectiblesText.visible = !showCollectibles + collectiblesSection.visible = showCollectibles } - Connections { - target: walletModel - onLoadingCollectibles: { - root.isLoading= isLoading + Column { + id: collectiblesSection + spacing: Style.current.halfPadding + anchors.fill: parent + + Repeater { + id: collectiblesRepeater + model: walletModel.collectiblesLists + + CollectiblesContainer { + property var collectibleData: CollectiblesData.collectiblesData[model.collectibleType] + + collectibleName: collectibleData.collectibleName + collectibleIconSource: "../../img/collectibles/" + collectibleData.collectibleIconSource + collectiblesModal: collectiblesModalComponent + buttonText: collectibleData.buttonText + getLink: collectibleData.getLink + onVisibleChanged: { + checkCollectiblesVisibility() + } + } } } } diff --git a/ui/app/AppLayouts/Wallet/WalletLayout.qml b/ui/app/AppLayouts/Wallet/WalletLayout.qml index 064b9e2ab7..8f98e8090f 100644 --- a/ui/app/AppLayouts/Wallet/WalletLayout.qml +++ b/ui/app/AppLayouts/Wallet/WalletLayout.qml @@ -80,9 +80,6 @@ SplitView { anchors.leftMargin: 32 //% "Collectibles" btnText: qsTrId("wallet-collectibles") - onClicked: { - walletModel.loadCollectiblesForAccount(walletModel.currentAccount.address) - } } StatusTabButton { id: historyBtn diff --git a/ui/app/AppLayouts/Wallet/components/collectiblesComponents/CollectiblesContainer.qml b/ui/app/AppLayouts/Wallet/components/collectiblesComponents/CollectiblesContainer.qml index 4df6ff09ab..7c3a004ec0 100644 --- a/ui/app/AppLayouts/Wallet/components/collectiblesComponents/CollectiblesContainer.qml +++ b/ui/app/AppLayouts/Wallet/components/collectiblesComponents/CollectiblesContainer.qml @@ -7,14 +7,25 @@ Item { property url collectibleIconSource: "../../../../img/collectibles/CryptoKitties.png" property string collectibleName: "CryptoKitties" property string collectibleType: "cryptokitty" - property bool isLoading: true property bool collectiblesOpened: false property var collectiblesModal property string buttonText: "View in Cryptokitties" property var getLink: function () {} + property var collectibles: { + try { + return JSON.parse(collectiblesJSON) + } catch (e) { + console.error('Error parsing collectibles for:', collectibleName) + console.error('JSON:', collectiblesJSON) + console.error('Error:', e) + return [] + } + } + // Adding active instead of just using visible, because visible counts as false when the parent is not visible + property bool active: !!loading || collectibles.length > 0 id: root - visible: isLoading || collectiblesContent.collectiblesQty > 0 + visible: active width: parent.width height: visible ? collectiblesHeader.height + collectiblesContent.height : 0 @@ -22,8 +33,8 @@ Item { id: collectiblesHeader collectibleName: root.collectibleName collectibleIconSource: root.collectibleIconSource - collectiblesQty: collectiblesContent.collectiblesQty - isLoading: root.isLoading + collectiblesQty: collectibles.length + isLoading: loading toggleCollectible: function () { root.collectiblesOpened = !root.collectiblesOpened } @@ -38,6 +49,7 @@ Item { getLink: root.getLink anchors.top: collectiblesHeader.bottom anchors.topMargin: Style.current.halfPadding + collectibles: root.collectibles } } diff --git a/ui/app/AppLayouts/Wallet/components/collectiblesComponents/CollectiblesContent.qml b/ui/app/AppLayouts/Wallet/components/collectiblesComponents/CollectiblesContent.qml index 01b05831b5..13d9c01b78 100644 --- a/ui/app/AppLayouts/Wallet/components/collectiblesComponents/CollectiblesContent.qml +++ b/ui/app/AppLayouts/Wallet/components/collectiblesComponents/CollectiblesContent.qml @@ -11,7 +11,7 @@ ScrollView { property var collectiblesModal property string buttonText: "View in Cryptokitties" property var getLink: function () {} - property alias collectiblesQty: collectibleModel.count + property var collectibles: [] id: root height: visible ? contentRow.height : 0 @@ -26,40 +26,9 @@ ScrollView { spacing: Style.current.padding Repeater { - model: collectibleModel - } + model: collectibles - DelegateModel { - id: collectibleModel - model: walletModel.collectibles - items.includeByDefault: false - - groups: [ - DelegateModelGroup { - id: uncheckedItems - name: "unchecked" - includeByDefault: true - onChanged: { - while (uncheckedItems.count > 0) { - var currentItem = uncheckedItems.get(0) - if (currentItem.model.collectibleType === root.collectibleType) { - currentItem.groups = "items" - } else { - currentItem.groups = "bad" - } - } - - } - }, - DelegateModelGroup { - id: badCollectibleGroup - name: "bad" - includeByDefault: true - } - - ] - - delegate: Rectangle { + Rectangle { radius: 16 border.width: 1 border.color: Style.current.border @@ -71,7 +40,7 @@ ScrollView { id: collectibleImage width: root.imageSize height: root.imageSize - source: image + source: modelData.image fillMode: Image.PreserveAspectCrop } @@ -80,12 +49,12 @@ ScrollView { anchors.fill: parent onClicked: { collectiblesModal.openModal({ - name: name, - id: collectibleId, - description: description, + name: modelData.name, + id: modelData.id, + description: modelData.description, buttonText: root.buttonText, - link: root.getLink(collectibleId, externalUrl), - image: image + link: root.getLink(modelData.id, modelData.externalUrl), + image: modelData.image }) } } diff --git a/ui/app/AppLayouts/Wallet/components/collectiblesComponents/collectiblesData.js b/ui/app/AppLayouts/Wallet/components/collectiblesComponents/collectiblesData.js new file mode 100644 index 0000000000..d9a2ca0fd9 --- /dev/null +++ b/ui/app/AppLayouts/Wallet/components/collectiblesComponents/collectiblesData.js @@ -0,0 +1,32 @@ + +var cryptokitty = "cryptokitty" +var kudo = "kudo" +var ethermon = "ethermon" + +var collectiblesData = { + [cryptokitty] :{ + collectibleName: "CryptoKitties", + collectibleIconSource: "CryptoKitties.png", + buttonText: qsTr("View in Cryptokitties"), + getLink: function (id) { + return `https://www.cryptokitties.co/kitty/${id}` + } + }, + [ethermon] :{ + collectibleName: "Ethermons", + collectibleIconSource: "ethermons.png", + buttonText: qsTr("View in Ethermon"), + getLink: function (id) { + // TODO find a more direct URL + return "https://ethermon.io/inventory" + } + }, + [kudo] :{ + collectibleName: "Kudos", + collectibleIconSource: "kudos.png", + buttonText: qsTr("View in Gitcoin"), + getLink: function (id, externalUrl) { + return externalUrl + } + }, +} diff --git a/ui/imports/Constants.qml b/ui/imports/Constants.qml index a81d88f693..7d6452b899 100644 --- a/ui/imports/Constants.qml +++ b/ui/imports/Constants.qml @@ -23,10 +23,6 @@ QtObject { readonly property string seedWalletType: "seed" readonly property string generatedWalletType: "generated" - readonly property string cryptokitty: "cryptokitty" - readonly property string kudo: "kudo" - readonly property string ethermon: "ethermon" - readonly property var accountColors: [ "#9B832F", "#D37EF4", diff --git a/ui/nim-status-client.pro b/ui/nim-status-client.pro index 773a3a7c09..ac98932625 100644 --- a/ui/nim-status-client.pro +++ b/ui/nim-status-client.pro @@ -157,6 +157,7 @@ DISTFILES += \ app/AppLayouts/Wallet/components/collectiblesComponents/CollectiblesHeader.qml \ app/AppLayouts/Wallet/components/collectiblesComponents/CollectiblesModal.qml \ app/AppLayouts/Wallet/components/collectiblesComponents/CollectiblesModalContent.qml \ + app/AppLayouts/Wallet/components/collectiblesComponents/collectiblesData.js \ fonts/InterStatus/InterStatus-Black.otf \ fonts/InterStatus/InterStatus-BlackItalic.otf \ fonts/InterStatus/InterStatus-Bold.otf \