feat(@desktop/wallet): implement new collectibles view

Fixes #8810
This commit is contained in:
Dario Gabriel Lipicar 2023-01-19 21:44:35 -03:00 committed by Anthony Laibe
parent 45adf0f411
commit 20733272f2
15 changed files with 513 additions and 278 deletions

View File

@ -42,6 +42,7 @@ proc init*(self: Controller) =
self.events.on(SIGNAL_COLLECTIONS_UPDATED) do(e:Args): self.events.on(SIGNAL_COLLECTIONS_UPDATED) do(e:Args):
let args = CollectionsUpdateArgs(e) let args = CollectionsUpdateArgs(e)
self.refreshCollections(args.chainId, args.address) self.refreshCollections(args.chainId, args.address)
self.collectibleService.fetchAllCollectibles(args.chainId, args.address)
self.events.on(SIGNAL_COLLECTIBLES_UPDATED) do(e:Args): self.events.on(SIGNAL_COLLECTIBLES_UPDATED) do(e:Args):
let args = CollectiblesUpdateArgs(e) let args = CollectiblesUpdateArgs(e)

View File

@ -0,0 +1,221 @@
import NimQml, Tables, strutils
import ./collections_model as collections_model
import ./collectibles_model as collectibles_model
type
ModelRole* {.pure.} = enum
CollectionName = UserRole + 1,
CollectionSlug
CollectionImageUrl
CollectionOwnedAssetCount
CollectionCollectiblesCount
CollectionCollectiblesLoaded
Id
Name
ImageUrl
BackgroundColor
Description
Permalink
Properties
Rankings
Stats
const COLLECTION_ROLE_TO_PROXY_ROLE = {
CollectionRole.Name: ModelRole.CollectionName,
CollectionRole.Slug: ModelRole.CollectionSlug,
CollectionRole.ImageUrl: ModelRole.CollectionImageUrl,
CollectionRole.OwnedAssetCount: ModelRole.CollectionOwnedAssetCount,
CollectionRole.CollectiblesLoaded: ModelRole.CollectionCollectiblesLoaded,
}.toTable()
const COLLECTIBLE_ROLE_TO_PROXY_ROLE = {
CollectibleRole.Id: ModelRole.Id,
CollectibleRole.Name: ModelRole.Name,
CollectibleRole.ImageUrl: ModelRole.ImageUrl,
CollectibleRole.BackgroundColor: ModelRole.BackgroundColor,
CollectibleRole.Description: ModelRole.Description,
CollectibleRole.Permalink: ModelRole.Permalink,
CollectibleRole.Properties: ModelRole.Properties,
CollectibleRole.Rankings: ModelRole.Rankings,
CollectibleRole.Stats: ModelRole.Stats,
}.toTable()
type
Index = tuple
collectionIdx: int
collectibleIdx: int
QtObject:
type
Model* = ref object of QAbstractListModel
collectionsModel: collections_model.Model
sourceIndexToRow: Table[Index, int]
collectionToRows: Table[int, (int, int)]
rowToSourceIndex: Table[int, Index]
proc delete(self: Model) =
self.collectionsModel = nil
self.QAbstractListModel.delete
proc setup(self: Model) =
self.QAbstractListModel.setup
proc countChanged(self: Model) {.signal.}
proc getCount(self: Model): int {.slot.} =
self.sourceIndexToRow.len
QtProperty[int] count:
read = getCount
notify = countChanged
proc collectionsLoadedChanged(self: Model) {.signal.}
proc getCollectionsLoaded*(self: Model): bool {.slot.} =
self.collectionsModel.getCollectionsLoaded()
QtProperty[bool] collectionsLoaded:
read = getCollectionsLoaded
notify = collectionsLoadedChanged
proc collectionCountChanged(self: Model) {.signal.}
proc getCollectionCount*(self: Model): int {.slot.} =
self.collectionsModel.getCount()
QtProperty[int] collectionCount:
read = getCollectionCount
notify = collectionCountChanged
proc rebuildMap(self: Model) =
self.beginResetModel()
self.sourceIndexToRow.clear()
self.collectionToRows.clear()
self.rowToSourceIndex.clear()
var proxy_row = 0
for i in 0 ..< self.collectionsModel.getCount():
let collectiblesModel = self.collectionsModel.getCollectiblesModel(i)
let collectionIndexStart = proxy_row
for j in 0 ..< collectiblesModel.getCount():
let idx = (collectionIdx: i, collectibleIdx: j)
self.sourceIndexToRow[idx] = proxy_row
self.rowToSourceIndex[proxy_row] = idx
proxy_row += 1
self.collectionToRows[i] = (collectionIndexStart, proxy_row - 1)
self.endResetModel()
self.countChanged()
proc newModel*(collectionsModel: collections_model.Model): Model =
new(result, delete)
result.collectionsModel = collectionsModel
result.setup
result.rebuildMap()
signalConnect(result.collectionsModel, "collectionsLoadedChanged()", result, "onCollectionsLoadedChanged()")
signalConnect(result.collectionsModel, "countChanged()", result, "onCollectionCountChanged()")
signalConnect(result.collectionsModel, "signalDataChanged(int, int, int)", result, "onDataChanged(int, int, int)")
proc onCollectionsLoadedChanged(self: Model) {.slot.} =
self.collectionsLoadedChanged()
proc onCollectionCountChanged(self: Model) {.slot.} =
self.collectionCountChanged()
self.rebuildMap()
proc onDataChanged(self: Model,
top: int,
bottom: int,
role: int) {.slot.} =
var topRow = self.collectionToRows[top][0]
var bottomRow = self.collectionToRows[bottom][1]
let topIndex = self.createIndex(topRow, 0, nil)
let bottomIndex = self.createIndex(bottomRow, 0, nil)
if (COLLECTION_ROLE_TO_PROXY_ROLE.hasKey(role.CollectionRole)):
self.dataChanged(topIndex, bottomIndex, @[COLLECTION_ROLE_TO_PROXY_ROLE[role.CollectionRole].int])
elif role == CollectionRole.CollectiblesModel.int:
self.rebuildMap()
method rowCount*(self: Model, index: QModelIndex = nil): int =
return self.getCount()
method roleNames(self: Model): Table[int, string] =
{
ModelRole.CollectionName.int:"collectionName",
ModelRole.CollectionSlug.int:"collectionSlug",
ModelRole.CollectionImageUrl.int:"collectionImageUrl",
ModelRole.CollectionOwnedAssetCount.int:"collectionOwnedAssetCount",
ModelRole.CollectionCollectiblesCount.int:"collectionCollectiblesCount",
ModelRole.CollectionCollectiblesLoaded.int:"collectionCollectiblesLoaded",
ModelRole.Id.int:"id",
ModelRole.Name.int:"name",
ModelRole.ImageUrl.int:"imageUrl",
ModelRole.BackgroundColor.int:"backgroundColor",
ModelRole.Description.int:"description",
ModelRole.Permalink.int:"permalink",
ModelRole.Properties.int:"properties",
ModelRole.Rankings.int:"rankings",
ModelRole.Stats.int:"stats",
}.toTable
proc mapFromSource(self: Model, index: Index): QModelIndex =
if not self.sourceIndexToRow.hasKey(index):
return QModelIndex()
let proxyIndex = self.sourceIndexToRow[index]
return self.createIndex(proxyIndex, 0, nil)
proc mapToSource(self: Model, index: QModelIndex): Index =
if not self.rowToSourceIndex.hasKey(index.row):
return (collectionIdx: -1, collectibleIdx: -1)
return self.rowToSourceIndex[index.row]
method data(self: Model, index: QModelIndex, role: int): QVariant =
if (not index.isValid):
return
if (index.row < 0 or index.row >= self.getCount()):
return
let sourceIndex = self.mapToSource(index)
let collectionIndex = self.collectionsModel.createIndex(sourceIndex.collectionIdx, 0, nil)
let enumRole = role.ModelRole
case enumRole:
of ModelRole.CollectionName:
result = self.collectionsModel.data(collectionIndex, CollectionRole.Name.int)
of ModelRole.CollectionSlug:
result = self.collectionsModel.data(collectionIndex, CollectionRole.Slug.int)
of ModelRole.CollectionImageUrl:
result = self.collectionsModel.data(collectionIndex, CollectionRole.ImageUrl.int)
of ModelRole.CollectionOwnedAssetCount:
result = self.collectionsModel.data(collectionIndex, CollectionRole.OwnedAssetCount.int)
of ModelRole.CollectionCollectiblesLoaded:
result = self.collectionsModel.data(collectionIndex, CollectionRole.CollectiblesLoaded.int)
else:
let collectiblesModel = self.collectionsModel.getCollectiblesModel(sourceIndex.collectionIdx)
let collectibleIndex = collectiblesModel.createIndex(sourceIndex.collectibleIdx, 0, nil)
case enumRole:
of ModelRole.CollectionCollectiblesCount:
result = newQVariant(collectiblesModel.getCount())
of ModelRole.Id:
result = collectiblesModel.data(collectibleIndex, CollectibleRole.Id.int)
of ModelRole.Name:
result = collectiblesModel.data(collectibleIndex, CollectibleRole.Name.int)
of ModelRole.ImageUrl:
result = collectiblesModel.data(collectibleIndex, CollectibleRole.ImageUrl.int)
of ModelRole.BackgroundColor:
result = collectiblesModel.data(collectibleIndex, CollectibleRole.BackgroundColor.int)
of ModelRole.Description:
result = collectiblesModel.data(collectibleIndex, CollectibleRole.Description.int)
of ModelRole.Permalink:
result = collectiblesModel.data(collectibleIndex, CollectibleRole.Permalink.int)
of ModelRole.Properties:
result = collectiblesModel.data(collectibleIndex, CollectibleRole.Properties.int)
of ModelRole.Rankings:
result = collectiblesModel.data(collectibleIndex, CollectibleRole.Rankings.int)
of ModelRole.Stats:
result = collectiblesModel.data(collectibleIndex, CollectibleRole.Stats.int)
else:
return
proc data*(self: Model, row: int, role: ModelRole): QVariant =
return self.data(self.createIndex(row, 0, nil), role.int)

View File

@ -1,9 +1,9 @@
import NimQml, Tables, strutils, strformat import NimQml, Tables, strutils, strformat, sequtils
import ./collectibles_item, ./collectible_trait_model import ./collectibles_item, ./collectible_trait_model
type type
ModelRole {.pure.} = enum CollectibleRole* {.pure.} = enum
Id = UserRole + 1, Id = UserRole + 1,
Name Name
ImageUrl ImageUrl
@ -26,37 +26,39 @@ QtObject:
proc setup(self: Model) = proc setup(self: Model) =
self.QAbstractListModel.setup self.QAbstractListModel.setup
proc newModel*(): Model = proc newModel*(items: seq[Item]): Model =
new(result, delete) new(result, delete)
result.setup result.setup
result.items = items
proc newModel*(): Model =
return newModel(@[])
proc `$`*(self: Model): string = proc `$`*(self: Model): string =
for i in 0 ..< self.items.len: for i in 0 ..< self.items.len:
result &= fmt"""[{i}]:({$self.items[i]})""" result &= fmt"""[{i}]:({$self.items[i]})"""
proc countChanged(self: Model) {.signal.} proc countChanged(self: Model) {.signal.}
proc getCount*(self: Model): int {.slot.} =
proc getCount(self: Model): int {.slot.} =
self.items.len self.items.len
QtProperty[int] count: QtProperty[int] count:
read = getCount read = getCount
notify = countChanged notify = countChanged
method rowCount(self: Model, index: QModelIndex = nil): int = method rowCount*(self: Model, index: QModelIndex = nil): int =
return self.items.len return self.items.len
method roleNames(self: Model): Table[int, string] = method roleNames(self: Model): Table[int, string] =
{ {
ModelRole.Id.int:"id", CollectibleRole.Id.int:"id",
ModelRole.Name.int:"name", CollectibleRole.Name.int:"name",
ModelRole.ImageUrl.int:"imageUrl", CollectibleRole.ImageUrl.int:"imageUrl",
ModelRole.BackgroundColor.int:"backgroundColor", CollectibleRole.BackgroundColor.int:"backgroundColor",
ModelRole.Description.int:"description", CollectibleRole.Description.int:"description",
ModelRole.Permalink.int:"permalink", CollectibleRole.Permalink.int:"permalink",
ModelRole.Properties.int:"properties", CollectibleRole.Properties.int:"properties",
ModelRole.Rankings.int:"rankings", CollectibleRole.Rankings.int:"rankings",
ModelRole.Stats.int:"stats", CollectibleRole.Stats.int:"stats",
}.toTable }.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant = method data(self: Model, index: QModelIndex, role: int): QVariant =
@ -67,36 +69,42 @@ QtObject:
return return
let item = self.items[index.row] let item = self.items[index.row]
let enumRole = role.ModelRole let enumRole = role.CollectibleRole
case enumRole: case enumRole:
of ModelRole.Id: of CollectibleRole.Id:
result = newQVariant(item.getId()) result = newQVariant(item.getId())
of ModelRole.Name: of CollectibleRole.Name:
result = newQVariant(item.getName()) result = newQVariant(item.getName())
of ModelRole.ImageUrl: of CollectibleRole.ImageUrl:
result = newQVariant(item.getImageUrl()) result = newQVariant(item.getImageUrl())
of ModelRole.BackgroundColor: of CollectibleRole.BackgroundColor:
result = newQVariant(item.getBackgroundColor()) result = newQVariant(item.getBackgroundColor())
of ModelRole.Description: of CollectibleRole.Description:
result = newQVariant(item.getDescription()) result = newQVariant(item.getDescription())
of ModelRole.Permalink: of CollectibleRole.Permalink:
result = newQVariant(item.getPermalink()) result = newQVariant(item.getPermalink())
of ModelRole.Properties: of CollectibleRole.Properties:
let traits = newTraitModel() let traits = newTraitModel()
traits.setItems(item.getProperties()) traits.setItems(item.getProperties())
result = newQVariant(traits) result = newQVariant(traits)
of ModelRole.Rankings: of CollectibleRole.Rankings:
let traits = newTraitModel() let traits = newTraitModel()
traits.setItems(item.getRankings()) traits.setItems(item.getRankings())
result = newQVariant(traits) result = newQVariant(traits)
of ModelRole.Stats: of CollectibleRole.Stats:
let traits = newTraitModel() let traits = newTraitModel()
traits.setItems(item.getStats()) traits.setItems(item.getStats())
result = newQVariant(traits) result = newQVariant(traits)
proc getItem*(self: Model, index: int): Item =
return self.items[index]
proc setItems*(self: Model, items: seq[Item]) = proc setItems*(self: Model, items: seq[Item]) =
self.beginResetModel() self.beginResetModel()
self.items = items self.items = items
self.endResetModel() self.endResetModel()
self.countChanged() self.countChanged()
proc appendItems*(self: Model, items: seq[Item]) =
self.setItems(concat(self.items, items))

View File

@ -0,0 +1,16 @@
import sequtils, sugar
import ../../../../../../app_service/service/collectible/dto
import collectibles_item, collectible_trait_item
proc collectibleToItem*(c: CollectibleDto) : Item =
return initItem(
c.id,
c.name,
c.imageUrl,
c.backgroundColor,
c.description,
c.permalink,
c.properties.map(t => initTrait(t.traitType, t.value, t.displayType, t.maxValue)),
c.rankings.map(t => initTrait(t.traitType, t.value, t.displayType, t.maxValue)),
c.statistics.map(t => initTrait(t.traitType, t.value, t.displayType, t.maxValue))
)

View File

@ -17,8 +17,7 @@ proc initItem*(name, slug, imageUrl: string, ownedAssetCount: int, collectiblesL
result.imageUrl = imageUrl result.imageUrl = imageUrl
result.ownedAssetCount = ownedAssetCount result.ownedAssetCount = ownedAssetCount
result.collectiblesLoaded = collectiblesLoaded result.collectiblesLoaded = collectiblesLoaded
result.collectiblesModel = collectibles_model.newModel() result.collectiblesModel = collectibles_model.newModel(collectibles)
result.collectiblesModel.setItems(collectibles)
proc initItem*(): Item = proc initItem*(): Item =
result = initItem("", "", "", 0, false, @[]) result = initItem("", "", "", 0, false, @[])

View File

@ -4,8 +4,9 @@ import ./collections_item as collections_item
import ./collectibles_model as collectibles_model import ./collectibles_model as collectibles_model
import ./collectibles_item as collectibles_item import ./collectibles_item as collectibles_item
type type
ModelRole {.pure.} = enum CollectionRole* {.pure.} = enum
Name = UserRole + 1, Name = UserRole + 1,
Slug Slug
ImageUrl ImageUrl
@ -36,34 +37,30 @@ QtObject:
result &= fmt"""[{i}]:({$self.items[i]})""" result &= fmt"""[{i}]:({$self.items[i]})"""
proc countChanged(self: Model) {.signal.} proc countChanged(self: Model) {.signal.}
proc getCount*(self: Model): int {.slot.} =
proc getCount(self: Model): int {.slot.} =
self.items.len self.items.len
QtProperty[int] collectionsLoaded:
read = getCollectionsLoaded
notify = collectionsLoadedChanged
proc collectionsLoadedChanged(self: Model) {.signal.}
proc getCollectionsLoaded(self: Model): int {.slot.} =
self.items.len
QtProperty[int] count: QtProperty[int] count:
read = getCount read = getCount
notify = countChanged notify = countChanged
proc collectionsLoadedChanged(self: Model) {.signal.}
proc getCollectionsLoaded*(self: Model): bool {.slot.} =
self.collectionsLoaded
QtProperty[bool] collectionsLoaded:
read = getCollectionsLoaded
notify = collectionsLoadedChanged
method rowCount(self: Model, index: QModelIndex = nil): int = method rowCount(self: Model, index: QModelIndex = nil): int =
return self.items.len return self.items.len
method roleNames(self: Model): Table[int, string] = method roleNames(self: Model): Table[int, string] =
{ {
ModelRole.Name.int:"name", CollectionRole.Name.int:"name",
ModelRole.Slug.int:"slug", CollectionRole.Slug.int:"slug",
ModelRole.ImageUrl.int:"imageUrl", CollectionRole.ImageUrl.int:"imageUrl",
ModelRole.OwnedAssetCount.int:"ownedAssetCount", CollectionRole.OwnedAssetCount.int:"ownedAssetCount",
ModelRole.CollectiblesLoaded.int:"collectiblesLoaded", CollectionRole.CollectiblesLoaded.int:"collectiblesLoaded",
ModelRole.CollectiblesModel.int:"collectiblesModel" CollectionRole.CollectiblesModel.int:"collectiblesModel"
}.toTable }.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant = method data(self: Model, index: QModelIndex, role: int): QVariant =
@ -74,29 +71,39 @@ QtObject:
return return
let item = self.items[index.row] let item = self.items[index.row]
let enumRole = role.ModelRole let enumRole = role.CollectionRole
case enumRole: case enumRole:
of ModelRole.Name: of CollectionRole.Name:
result = newQVariant(item.getName()) result = newQVariant(item.getName())
of ModelRole.Slug: of CollectionRole.Slug:
result = newQVariant(item.getSlug()) result = newQVariant(item.getSlug())
of ModelRole.ImageUrl: of CollectionRole.ImageUrl:
result = newQVariant(item.getImageUrl()) result = newQVariant(item.getImageUrl())
of ModelRole.OwnedAssetCount: of CollectionRole.OwnedAssetCount:
result = newQVariant(item.getOwnedAssetCount()) result = newQVariant(item.getOwnedAssetCount())
of ModelRole.CollectiblesLoaded: of CollectionRole.CollectiblesLoaded:
result = newQVariant(item.getCollectiblesLoaded()) result = newQVariant(item.getCollectiblesLoaded())
of ModelRole.CollectiblesModel: of CollectionRole.CollectiblesModel:
result = newQVariant(item.getCollectiblesModel()) result = newQVariant(item.getCollectiblesModel())
proc setItems*(self: Model, items: seq[collections_item.Item]) = proc setCollections*(self: Model, items: seq[collections_item.Item], collectionsLoaded: bool) =
self.beginResetModel() self.beginResetModel()
self.items = items self.items = items
self.endResetModel() self.endResetModel()
self.countChanged() self.countChanged()
self.collectionsLoaded = true if self.collectionsLoaded != collectionsLoaded:
self.collectionsLoadedChanged() self.collectionsLoaded = collectionsLoaded
self.collectionsLoadedChanged()
proc getCollectionItem*(self: Model, index: int) : collections_item.Item =
return self.items[index]
proc getCollectiblesModel*(self: Model, index: int) : collectibles_model.Model =
if index < self.items.len:
return self.items[index].getCollectiblesModel()
echo "getCollectiblesModel: Invalid index ", index, " with len ", self.items.len
return collectibles_model.newModel()
proc findIndexBySlug(self: Model, slug: string): int = proc findIndexBySlug(self: Model, slug: string): int =
for i in 0 ..< self.items.len: for i in 0 ..< self.items.len:
@ -104,15 +111,21 @@ QtObject:
return i return i
return -1 return -1
proc updateCollectionCollectibles*(self: Model, slug: string, collectibles: seq[collectibles_item.Item]) = proc signalDataChanged(self: Model, top: int, bottom: int, roles: int) {.signal.}
proc emitDataChanged(self: Model, top: int, bottom: int, role: int) =
let topIndex = self.createIndex(top, 0, nil)
let bottomIndex = self.createIndex(bottom, 0, nil)
self.dataChanged(topIndex, bottomIndex, @[role])
self.signalDataChanged(top, bottom, role)
proc updateCollectionCollectibles*(self: Model, slug: string, collectibles: seq[collectibles_item.Item], collectiblesLoaded: bool) =
let idx = self.findIndexBySlug(slug) let idx = self.findIndexBySlug(slug)
if idx > -1: if idx > -1:
let index = self.createIndex(idx, 0, nil)
let collectiblesModel = self.items[idx].getCollectiblesModel() let collectiblesModel = self.items[idx].getCollectiblesModel()
collectiblesModel.setItems(collectibles) collectiblesModel.setItems(collectibles)
self.dataChanged(index, index, @[ModelRole.CollectiblesModel.int]) self.emitDataChanged(idx, idx, CollectionRole.CollectiblesModel.int)
if not self.items[idx].getCollectiblesLoaded(): if self.items[idx].getCollectiblesLoaded() != collectiblesLoaded:
self.items[idx].collectiblesLoaded = true self.items[idx].collectiblesLoaded = collectiblesLoaded
self.dataChanged(index, index, @[ModelRole.CollectiblesLoaded.int]) self.emitDataChanged(idx, idx, CollectionRole.CollectiblesLoaded.int)

View File

@ -0,0 +1,13 @@
import sequtils, sugar, Tables
import ../../../../../../app_service/service/collectible/service
import collections_item, collectibles_utils
proc collectionToItem*(collection: CollectionData) : Item =
return initItem(
collection.collection.name,
collection.collection.slug,
collection.collection.imageUrl,
collection.collection.ownedAssetCount,
collection.collectiblesLoaded,
toSeq(collection.collectibles.values).map(c => collectibleToItem(c))
)

View File

@ -6,15 +6,17 @@ import ../../../../core/eventemitter
import ./io_interface, ./view, ./controller import ./io_interface, ./view, ./controller
import ../io_interface as delegate_interface import ../io_interface as delegate_interface
import ../../../../../app_service/service/collectible/service as collectible_service import ../../../../../app_service/service/collectible/service as collectible_service
import ../../../../../app_service/service/collectible/service as collectible_dto
import ../../../../../app_service/service/wallet_account/service as wallet_account_service import ../../../../../app_service/service/wallet_account/service as wallet_account_service
import ../../../../../app_service/service/network/service as network_service import ../../../../../app_service/service/network/service as network_service
import ./current_collectible/module as current_collectible_module import ./current_collectible/module as current_collectible_module
import ./models/collections_item as collections_item
import ./models/collections_utils
import ./models/collectibles_item as collectibles_item import ./models/collectibles_item as collectibles_item
import ./models/collectible_trait_item as collectible_trait_item import ./models/collectible_trait_item as collectible_trait_item
import ./models/collections_item as collections_item import ./models/collectibles_utils
import ./models/collectibles_model as collectibles_model
export io_interface export io_interface
@ -86,37 +88,31 @@ method switchAccount*(self: Module, accountIndex: int) =
self.currentCollectibleModule.setCurrentAddress(network, self.address) self.currentCollectibleModule.setCurrentAddress(network, self.address)
proc collectibleToItem(c: collectible_dto.CollectibleDto) : collectibles_item.Item = proc collectionToItem(self: Module, collection: CollectionData) : collections_item.Item =
return collectibles_item.initItem( var item = collectionToItem(collection)
c.id, #[ Skeleton items are disabled until problem with OpenSea API is researched.
c.name, OpenSea is telling us the address owns a certain amount of NFTs from a certain collection, but
c.imageUrl, it doesn't give us the NFTs from that collection when trying to fetch them.
c.backgroundColor, # Append skeleton collectibles if not yet fetched
c.description, let model = item.getCollectiblesModel()
c.permalink, let unfetchedCollectiblesCount = item.getOwnedAssetCount() - model.getCount()
c.properties.map(t => initTrait(t.traitType, t.value, t.displayType, t.maxValue)), if unfetchedCollectiblesCount > 0:
c.rankings.map(t => initTrait(t.traitType, t.value, t.displayType, t.maxValue)), echo "unfetchedCollectiblesCount = ", unfetchedCollectiblesCount, " ", item.getSlug()
c.statistics.map(t => initTrait(t.traitType, t.value, t.displayType, t.maxValue)) let skeletonItems = newSeqWith(unfetchedCollectiblesCount, collectibles_item.initItem())
) model.appendItems(skeletonItems)
]#
proc collectionToItem(c: CollectionData) : collections_item.Item = return item
return collections_item.initItem(
c.collection.name,
c.collection.slug,
c.collection.imageUrl,
c.collection.ownedAssetCount,
c.collectiblesLoaded,
toSeq(c.collectibles.values).map(c => collectibleToItem(c))
)
method setCollections*(self: Module, collections: CollectionsData) = method setCollections*(self: Module, collections: CollectionsData) =
self.view.setCollections( self.view.setCollections(
toSeq(collections.collections.values).map(c => collectionToItem(c)) toSeq(collections.collections.values).map(c => self.collectionToItem(c)),
collections.collectionsLoaded
) )
method updateCollection*(self: Module, collection: CollectionData) = method updateCollection*(self: Module, collection: CollectionData) =
self.view.setCollectibles(collection.collection.slug, self.view.setCollectibles(collection.collection.slug,
toSeq(collection.collectibles.values).map(c => collectibleToItem(c)) toSeq(collection.collectibles.values).map(c => collectibleToItem(c)),
collection.collectiblesLoaded
) )
method fetchCollections*(self: Module) = method fetchCollections*(self: Module) =

View File

@ -1,6 +1,7 @@
import NimQml import NimQml
import ./models/collections_model import ./models/collections_model as collections_model
import ./models/collectibles_flat_proxy_model as flat_model
import ./models/collections_item as collections_item import ./models/collections_item as collections_item
import ./models/collectibles_item as collectibles_item import ./models/collectibles_item as collectibles_item
import ./io_interface import ./io_interface
@ -9,12 +10,12 @@ QtObject:
type type
View* = ref object of QObject View* = ref object of QObject
delegate: io_interface.AccessInterface delegate: io_interface.AccessInterface
model: Model model: collections_model.Model
modelVariant: QVariant flatModel: flat_model.Model
proc delete*(self: View) = proc delete*(self: View) =
self.flatModel.delete
self.model.delete self.model.delete
self.modelVariant.delete
self.QObject.delete self.QObject.delete
proc newView*(delegate: io_interface.AccessInterface): View = proc newView*(delegate: io_interface.AccessInterface): View =
@ -22,28 +23,33 @@ QtObject:
result.QObject.setup result.QObject.setup
result.delegate = delegate result.delegate = delegate
result.model = newModel() result.model = newModel()
result.modelVariant = newQVariant(result.model) result.flatModel = flat_model.newModel(result.model)
proc load*(self: View) = proc load*(self: View) =
self.delegate.viewDidLoad() self.delegate.viewDidLoad()
proc modelChanged*(self: View) {.signal.} proc modelChanged*(self: View) {.signal.}
proc getModel(self: View): QVariant {.slot.} = proc getModel(self: View): QVariant {.slot.} =
return self.modelVariant return newQVariant(self.model)
QtProperty[QVariant] model: QtProperty[QVariant] model:
read = getModel read = getModel
notify = modelChanged notify = modelChanged
proc setCollections*(self: View, collections: seq[collections_item.Item]) = proc flatModelChanged*(self: View) {.signal.}
self.model.setItems(collections) proc getFlatModel(self: View): QVariant {.slot.} =
return newQVariant(self.flatModel)
proc setCollectibles*(self: View, collectionsSlug: string, collectibles: seq[collectibles_item.Item]) = QtProperty[QVariant] flatModel:
self.model.updateCollectionCollectibles(collectionsSlug, collectibles) read = getFlatModel
notify = flatModelChanged
proc fetchCollections*(self: View) {.slot.} = proc fetchCollections*(self: View) {.slot.} =
self.delegate.fetchCollections() self.delegate.fetchCollections()
proc fetchCollectibles*(self: View, collectionSlug: string) {.slot.} = proc fetchCollectibles*(self: View, collectionSlug: string) {.slot.} =
self.delegate.fetchCollectibles(collectionSlug) self.delegate.fetchCollectibles(collectionSlug)
proc setCollections*(self: View, collections: seq[collections_item.Item], collectionsLoaded: bool) =
self.model.setCollections(collections, collectionsLoaded)
proc setCollectibles*(self: View, collectionsSlug: string, collectibles: seq[collectibles_item.Item], collectiblesLoaded: bool) =
self.model.updateCollectionCollectibles(collectionsSlug, collectibles, collectiblesLoaded)

View File

@ -165,11 +165,12 @@ QtObject:
data.chainId = chainIdJson.getInt() data.chainId = chainIdJson.getInt()
data.address = addressJson.getStr() data.address = addressJson.getStr()
var collections: seq[CollectionDto]
let collectionsJson = responseObj["collections"] let collectionsJson = responseObj["collections"]
if (collectionsJson.kind == JArray): if (collectionsJson.kind == JArray):
let collections = map(collectionsJson.getElems(), proc(x: JsonNode): CollectionDto = x.toCollectionDto()) collections = map(collectionsJson.getElems(), proc(x: JsonNode): CollectionDto = x.toCollectionDto())
self.setCollections(data.chainId, data.address, collections) self.setCollections(data.chainId, data.address, collections)
self.events.emit(SIGNAL_COLLECTIONS_UPDATED, data) self.events.emit(SIGNAL_COLLECTIONS_UPDATED, data)
except Exception as e: except Exception as e:
let errDescription = e.msg let errDescription = e.msg
error "error onRxCollections: ", errDescription error "error onRxCollections: ", errDescription
@ -208,11 +209,12 @@ QtObject:
data.address = addressJson.getStr() data.address = addressJson.getStr()
data.collectionSlug = collectionSlugJson.getStr() data.collectionSlug = collectionSlugJson.getStr()
var collectibles: seq[CollectibleDto]
let collectiblesJson = responseObj["collectibles"] let collectiblesJson = responseObj["collectibles"]
if (collectiblesJson.kind == JArray): if (collectiblesJson.kind == JArray):
let collectibles = map(collectiblesJson.getElems(), proc(x: JsonNode): CollectibleDto = x.toCollectibleDto()) collectibles = map(collectiblesJson.getElems(), proc(x: JsonNode): CollectibleDto = x.toCollectibleDto())
self.setCollectibles(data.chainId, data.address, data.collectionSlug, collectibles) self.setCollectibles(data.chainId, data.address, data.collectionSlug, collectibles)
self.events.emit(SIGNAL_COLLECTIBLES_UPDATED, data) self.events.emit(SIGNAL_COLLECTIBLES_UPDATED, data)
except Exception as e: except Exception as e:
let errDescription = e.msg let errDescription = e.msg
error "error onRxCollectibles: ", errDescription error "error onRxCollectibles: ", errDescription
@ -241,3 +243,11 @@ QtObject:
limit: limit limit: limit
) )
self.threadpool.start(arg) self.threadpool.start(arg)
proc fetchAllCollectibles*(self: Service, chainId: int, address: string) =
try:
for collectionSlug, _ in self.data[chainId][address].collections:
self.fetchCollectibles(chainId, address, collectionSlug)
except Exception as e:
let errDescription = e.msg
error "error fetchAllCollectibles: ", errDescription

View File

@ -31,6 +31,7 @@ QtObject {
property string mnemonicBackedUp: walletSection.isMnemonicBackedUp property string mnemonicBackedUp: walletSection.isMnemonicBackedUp
property var collections: walletSectionCollectibles.model property var collections: walletSectionCollectibles.model
property var flatCollectibles: walletSectionCollectibles.flatModel
property var currentCollectible: walletSectionCurrentCollectible property var currentCollectible: walletSectionCurrentCollectible
property var savedAddresses: walletSectionSavedAddresses.model property var savedAddresses: walletSectionSavedAddresses.model

View File

@ -1,34 +1,35 @@
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Controls 2.13 import QtQuick.Controls 2.13
import QtGraphicalEffects 1.13
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1 import StatusQ.Components 0.1
import utils 1.0
import shared 1.0
import shared.panels 1.0 import shared.panels 1.0
import "../stores"
import "../popups"
import "collectibles" import "collectibles"
Item { Item {
id: root id: root
property var collectiblesModel
width: parent.width width: parent.width
signal collectibleClicked(string collectionSlug, int collectibleId) signal collectibleClicked(string collectionSlug, int collectibleId)
readonly property bool areCollectionsLoaded: root.collectiblesModel.collectionsLoaded
Loader { Loader {
id: contentLoader id: contentLoader
width: parent.width width: parent.width
height: parent.height height: parent.height
sourceComponent: { sourceComponent: {
if (!RootStore.collections.collectionsLoaded) if (!root.areCollectionsLoaded)
{ {
return loading return loading
} else if (RootStore.collections.count === 0) { } else if (root.collectiblesModel.collectionCount === 0) {
return empty; return empty;
} else if (root.collectiblesModel.count === 0) {
return loading
} }
return loaded; return loaded;
} }
@ -64,42 +65,21 @@ Item {
Component { Component {
id: loaded id: loaded
StatusGridView {
StatusScrollView { id: gridView
id: scrollView anchors.fill: parent
model: root.collectiblesModel
Column { cellHeight: 229
id: collectiblesSection cellWidth: 176
width: root.width delegate: Item {
height: gridView.cellHeight
Repeater { width: gridView.cellWidth
objectName: "collectionsRepeater" CollectibleView {
id: collectionsRepeater collectibleModel: model
model: RootStore.collections anchors.fill: parent
delegate: StatusExpandableItem { anchors.bottomMargin: 4
id: collectionDelegate onCollectibleClicked: {
anchors.left: parent.left root.collectibleClicked(slug, collectibleId);
anchors.right: parent.right
primaryText: model.name
asset.name: model.imageUrl
asset.isImage: true
type: StatusExpandableItem.Type.Secondary
expandableComponent: CollectibleCollectionView {
collectionImageUrl: model.imageUrl
collectiblesLoaded: model.collectiblesLoaded
collectiblesModel: model.collectiblesModel
anchors.left: parent.left
anchors.right: parent.right
onCollectibleClicked: {
RootStore.selectCollectible(model.slug, collectibleId)
root.collectibleClicked(model.slug, collectibleId);
}
}
onExpandedChanged: {
if(expanded) {
RootStore.fetchCollectibles(model.slug)
}
}
} }
} }
} }

View File

@ -97,7 +97,9 @@ Item {
} }
} }
CollectiblesView { CollectiblesView {
collectiblesModel: RootStore.flatCollectibles
onCollectibleClicked: { onCollectibleClicked: {
RootStore.selectCollectible(collectionSlug, collectibleId)
stack.currentIndex = 1 stack.currentIndex = 1
} }
} }

View File

@ -1,121 +0,0 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtGraphicalEffects 1.13
import StatusQ.Components 0.1
import StatusQ.Core.Theme 0.1
import shared.panels 1.0
import "../../stores"
import utils 1.0
Item {
id: root
property string collectionImageUrl: ""
property bool collectiblesLoaded: false
property var collectiblesModel
width: parent.width
height: contentLoader.height
signal collectibleClicked(int collectibleId)
Loader {
id: contentLoader
width: parent.width
anchors.top: parent.top
anchors.topMargin: 16
anchors.horizontalCenter: parent.horizontalCenter
sourceComponent: {
if (!root.collectiblesLoaded) {
return loading
} else if (root.collectiblesModel.count === 0) {
return empty
}
return loaded
}
}
Component {
id: loading
Item {
id: loadingIndicator
height: 164
StatusLoadingIndicator {
width: 20
height: 20
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
Component {
id: empty
Item {
id: emptyContainer
height: 164
StyledText {
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
color: Style.current.secondaryText
text: qsTr("No collectibles available")
font.pixelSize: 15
}
}
}
Component {
id: loaded
Flow {
width: parent.width
bottomPadding: 16
spacing: 24
Component {
id: collectibleDelegate
StatusRoundedImage {
id: image
width: 146
height: 146
radius: 16
image.source: model.imageUrl
border.color: Theme.palette.baseColor2
border.width: 1
showLoadingIndicator: true
color: model.backgroundColor
Rectangle {
anchors.centerIn: parent
width: image.width
height: image.height
radius: image.radius
border.width: 1
border.color: Theme.palette.primaryColor1
color: Theme.palette.indirectColor3
visible: mouse.containsMouse
}
MouseArea {
id: mouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
root.collectibleClicked(model.id);
}
}
}
}
Repeater {
objectName: "collectiblesRepeater"
model: root.collectiblesModel
delegate: collectibleDelegate
}
}
}
}

View File

@ -0,0 +1,90 @@
import QtQuick 2.13
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.13
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import shared.panels 1.0
Item {
id: root
property var collectibleModel
implicitHeight: 225
implicitWidth: 176
signal collectibleClicked(string slug, int collectibleId)
readonly property bool isLoaded: root.collectibleModel.collectionCollectiblesLoaded
ColumnLayout {
//Layout.fillHeight: true
//Layout.fillWidth: true
width: parent.width
anchors.horizontalCenter: parent.horizontalCenter
spacing: 0
StatusRoundedImage {
id: image
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
Layout.topMargin: 8
Layout.bottomMargin: 0
implicitWidth: 160
implicitHeight: 160
radius: 12
image.source: root.collectibleModel.imageUrl
border.color: Theme.palette.baseColor2
border.width: 1
showLoadingIndicator: true
color: root.collectibleModel.backgroundColor
}
StatusBaseText {
id: collectibleLabel
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
Layout.topMargin: 9
Layout.preferredWidth: 144
Layout.preferredHeight: 21
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
font.pixelSize: 15
color: Theme.palette.directColor1
font.weight: Font.DemiBold
elide: Text.ElideRight
text: isLoaded ? root.collectibleModel.name : "..."
}
StatusBaseText {
id: collectionLabel
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
Layout.topMargin: 0
Layout.preferredWidth: 144
Layout.preferredHeight: 18
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
font.pixelSize: 13
color: Theme.palette.baseColor1
elide: Text.ElideRight
text: root.collectibleModel.collectionName
}
}
Rectangle {
anchors.fill: parent
radius: 18
border.width: 1
border.color: Theme.palette.primaryColor1
color: Theme.palette.indirectColor3
visible: root.isLoaded && mouse.containsMouse
}
MouseArea {
id: mouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
if (root.isLoaded) {
root.collectibleClicked(root.collectibleModel.collectionSlug, root.collectibleModel.id);
}
}
}
}