feat(wallet): use new status-go collectibles backend

Fixes #11558
This commit is contained in:
Dario Gabriel Lipicar 2023-07-17 20:56:40 -03:00 committed by dlipicar
parent 43a5d7eeeb
commit dc75c120df
40 changed files with 1034 additions and 952 deletions

View File

@ -0,0 +1,116 @@
import NimQml, logging, std/json, sequtils, strutils
import stint
import app/modules/shared_models/collectible_details_entry
import events_handler
import app/core/eventemitter
import backend/collectibles as backend_collectibles
import app_service/service/network/service as network_service
QtObject:
type
Controller* = ref object of QObject
networkService: network_service.Service
isDetailedEntryLoading: bool
detailedEntry: CollectibleDetailsEntry
eventsHandler: EventsHandler
proc setup(self: Controller) =
self.QObject.setup
proc delete*(self: Controller) =
self.QObject.delete
proc getDetailedEntry*(self: Controller): QVariant {.slot.} =
return newQVariant(self.detailedEntry)
proc detailedEntryChanged(self: Controller) {.signal.}
QtProperty[QVariant] detailedEntry:
read = getDetailedEntry
notify = detailedEntryChanged
proc getIsDetailedEntryLoading*(self: Controller): QVariant {.slot.} =
return newQVariant(self.detailedEntry)
proc isDetailedEntryLoadingChanged(self: Controller) {.signal.}
proc setIsDetailedEntryLoading(self: Controller, value: bool) =
if self.isDetailedEntryLoading != value:
self.isDetailedEntryLoading = value
self.isDetailedEntryLoadingChanged()
QtProperty[bool] isDetailedEntryLoading:
read = getIsDetailedEntryLoading
notify = isDetailedEntryLoadingChanged
proc getExtraData(self: Controller, chainID: int): ExtraData =
let network = self.networkService.getNetwork(chainID)
return ExtraData(
networkShortName: network.shortName,
networkColor: network.chainColor,
networkIconUrl: network.iconURL
)
proc processGetCollectiblesDataResponse(self: Controller, response: JsonNode) =
defer: self.setIsDetailedEntryLoading(false)
let res = fromJson(response, backend_collectibles.GetCollectiblesDataResponse)
if res.errorCode != ErrorCodeSuccess:
error "error fetching collectible details: ", res.errorCode
return
if len(res.collectibles) != 1:
error "unexpected number of items fetching collectible details: ", len(res.collectibles)
return
let collectible = res.collectibles[0]
let extradata = self.getExtraData(collectible.id.chainID)
self.detailedEntry = newCollectibleDetailsFullEntry(collectible, extradata)
self.detailedEntryChanged()
proc getDetailedCollectible*(self: Controller, chainId: int, contractAddress: string, tokenId: string) {.slot.} =
self.setIsDetailedEntryLoading(true)
let id = backend_collectibles.CollectibleUniqueID(
chainID: chainId,
contractAddress: contractAddress,
tokenID: stint.u256(tokenId)
)
let extradata = self.getExtraData(chainId)
self.detailedEntry = newCollectibleDetailsBasicEntry(id, extradata)
self.detailedEntryChanged()
let response = backend_collectibles.getCollectiblesDataAsync(@[id])
if response.error != nil:
self.setIsDetailedEntryLoading(false)
error "error fetching collectible details: ", response.error
return
proc setupEventHandlers(self: Controller) =
self.eventsHandler.onGetCollectiblesDataDone(proc (jsonObj: JsonNode) =
self.processGetCollectiblesDataResponse(jsonObj)
)
proc newController*(networkService: network_service.Service,
events: EventEmitter
): Controller =
new(result, delete)
result.networkService = networkService
result.detailedEntry = newCollectibleDetailsEmptyEntry()
result.isDetailedEntryLoading = false
result.eventsHandler = newEventsHandler(events)
result.setup()
result.setupEventHandlers()

View File

@ -0,0 +1,54 @@
import NimQml, logging, std/json, sequtils, strutils
import tables, stint
import app/core/eventemitter
import app/core/signals/types
import backend/collectibles as backend_collectibles
type EventCallbackProc = proc (eventObject: JsonNode)
# EventsHandler responsible for catching collectibles related backend events and reporting them
QtObject:
type
EventsHandler* = ref object of QObject
events: EventEmitter
eventHandlers: Table[string, EventCallbackProc]
proc setup(self: EventsHandler) =
self.QObject.setup
proc delete*(self: EventsHandler) =
self.QObject.delete
proc onGetCollectiblesDataDone*(self: EventsHandler, handler: EventCallbackProc) =
self.eventHandlers[backend_collectibles.eventGetCollectiblesDataDone] = handler
proc handleApiEvents(self: EventsHandler, e: Args) =
var data = WalletSignal(e)
if self.eventHandlers.hasKey(data.eventType):
var responseJson: JsonNode
responseJson = parseJson(data.message)
if responseJson.kind != JObject:
error "unexpected json type", responseJson.kind
return
let callback = self.eventHandlers[data.eventType]
callback(responseJson)
else:
discard
proc newEventsHandler*(events: EventEmitter): EventsHandler =
new(result, delete)
result.events = events
result.eventHandlers = initTable[string, EventCallbackProc]()
result.setup()
# Register for wallet events
let eventsHandler = result
result.events.on(SignalType.Wallet.event, proc(e: Args) =
eventsHandler.handleApiEvents(e)
)

View File

@ -1,81 +1,108 @@
import sequtils, Tables, sugar
import io_interface
import ../../../../../app_service/service/collectible/service as collectible_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_connection/service as network_connection_service
import ../../../../../app_service/service/node/service as node_service
import ../../../../core/eventemitter
import NimQml, std/json, sequtils, sugar, strutils
import stint, logging
type
Controller* = ref object of RootObj
delegate: io_interface.AccessInterface
events: EventEmitter
collectibleService: collectible_service.Service
walletAccountService: wallet_account_service.Service
networkService: network_service.Service
nodeService: node_service.Service
networkConnectionService: network_connection_service.Service
import app/modules/shared_models/collectibles_model
import app/modules/shared_models/collectibles_utils
import events_handler
proc newController*(
delegate: io_interface.AccessInterface,
events: EventEmitter,
collectibleService: collectible_service.Service,
walletAccountService: wallet_account_service.Service,
networkService: network_service.Service,
nodeService: node_service.Service,
networkConnectionService: network_connection_service.Service
): Controller =
result = Controller()
result.delegate = delegate
result.events = events
result.collectibleService = collectibleService
result.walletAccountService = walletAccountService
result.networkService = networkService
result.nodeService = nodeService
result.networkConnectionService = networkConnectionService
import app/core/eventemitter
proc delete*(self: Controller) =
discard
import backend/collectibles as backend_collectibles
proc updateCollectibles(self: Controller, chainId: int, address: string) =
let data = self.collectibleService.getOwnedCollectibles(chainId, @[address])
self.delegate.appendCollectibles(chainId, address, data[0])
const FETCH_BATCH_COUNT_DEFAULT = 50
proc init*(self: Controller) =
self.events.on(SIGNAL_OWNED_COLLECTIBLES_REFETCH) do(e:Args):
let args = OwnedCollectiblesUpdateArgs(e)
self.delegate.resetCollectibles()
QtObject:
type
Controller* = ref object of QObject
model: Model
self.events.on(SIGNAL_OWNED_COLLECTIBLES_UPDATE_ERROR) do(e:Args):
let args = OwnedCollectiblesUpdateArgs(e)
self.updateCollectibles(args.chainId, args.address)
eventsHandler: EventsHandler
self.events.on(SIGNAL_OWNED_COLLECTIBLES_UPDATE_STARTED) do(e:Args):
let args = OwnedCollectiblesUpdateArgs(e)
self.delegate.onFetchStarted(args.chainId, args.address)
addresses: seq[string]
chainIds: seq[int]
self.events.on(SIGNAL_OWNED_COLLECTIBLES_UPDATE_FINISHED) do(e:Args):
let args = OwnedCollectiblesUpdateArgs(e)
self.updateCollectibles(args.chainId, args.address)
proc setup(self: Controller) =
self.QObject.setup
self.events.on(SIGNAL_REFRESH_COLLECTIBLES) do(e:Args):
self.collectibleService.refetchAllOwnedCollectibles()
proc delete*(self: Controller) =
self.QObject.delete
proc getNetwork*(self: Controller): network_service.NetworkDto =
return self.networkService.getNetworkForCollectibles()
proc getModel*(self: Controller): QVariant {.slot.} =
return newQVariant(self.model)
proc getOwnedCollectibles*(self: Controller, chainId: int, addresses: seq[string]): seq[CollectiblesData] =
return self.collectibleService.getOwnedCollectibles(chainId, addresses)
QtProperty[QVariant] model:
read = getModel
proc fetchOwnedCollectibles*(self: Controller, chainId: int, addresses: seq[string]) =
self.collectibleService.fetchOwnedCollectibles(chainId, addresses)
proc processFilterOwnedCollectiblesResponse(self: Controller, response: JsonNode) =
defer: self.model.setIsFetching(false)
proc getCollectible*(self: Controller, chainId: int, id: UniqueID) : CollectibleDto =
self.collectibleService.getCollectible(chainId, id)
let res = fromJson(response, backend_collectibles.FilterOwnedCollectiblesResponse)
proc getCollection*(self: Controller, chainId: int, slug: string) : CollectionDto =
self.collectibleService.getCollection(chainId, slug)
let isError = res.errorCode != ErrorCodeSuccess
defer: self.model.setIsError(isError)
proc getHasCollectiblesCache*(self: Controller, address: string): bool =
return self.collectibleService.areCollectionsLoaded(address)
if isError:
error "error fetching collectibles entries: ", res.errorCode
return
try:
let items = res.collectibles.map(header => collectibleToItem(header))
self.model.setItems(items, res.offset, res.hasMore)
except Exception as e:
error "Error converting activity entries: ", e.msg
proc updateFilter*(self: Controller) {.slot.} =
self.model.resetModel(@[])
let response = backend_collectibles.filterOwnedCollectiblesAsync(self.chainIds, self.addresses, 0, FETCH_BATCH_COUNT_DEFAULT)
if response.error != nil:
self.model.setIsFetching(false)
self.model.setIsError(true)
error "error fetching collectibles entries: ", response.error
return
self.model.setIsFetching(true)
self.model.setIsError(false)
proc loadMoreItems(self: Controller) {.slot.} =
if self.model.getIsFetching():
return
let response = backend_collectibles.filterOwnedCollectiblesAsync(self.chainIds, self.addresses, self.model.getCount(), FETCH_BATCH_COUNT_DEFAULT)
if response.error != nil:
self.model.setIsError(true)
error "error fetching collectibles entries: ", response.error
return
self.model.setIsFetching(true)
self.model.setIsError(false)
proc setupEventHandlers(self: Controller) =
self.eventsHandler.onOwnedCollectiblesFilteringDone(proc (jsonObj: JsonNode) =
self.processFilterOwnedCollectiblesResponse(jsonObj)
)
self.eventsHandler.onCollectiblesOwnershipUpdateFinished(proc () =
self.updateFilter()
)
proc newController*(events: EventEmitter): Controller =
new(result, delete)
result.model = newModel()
result.eventsHandler = newEventsHandler(events)
result.addresses = @[]
result.chainIds = @[]
result.setup()
result.setupEventHandlers()
signalConnect(result.model, "loadMoreItems()", result, "loadMoreItems()")
proc globalFilterChanged*(self: Controller, addresses: seq[string], chainIds: seq[int]) =
if chainIds == self.chainIds and addresses == self.addresses:
return
self.chainIds = chainIds
self.addresses = addresses
self.updateFilter()

View File

@ -1,33 +0,0 @@
import sequtils, Tables
import io_interface
import ../../../../../../app_service/service/collectible/service as collectible_service
import ../../../../../../app_service/service/network/dto as network_dto
type
Controller* = ref object of RootObj
delegate: io_interface.AccessInterface
collectibleService: collectible_service.Service
network: network_dto.NetworkDto
proc newController*(
delegate: io_interface.AccessInterface,
collectibleService: collectible_service.Service,
): Controller =
result = Controller()
result.delegate = delegate
result.collectibleService = collectibleService
proc delete*(self: Controller) =
discard
proc init*(self: Controller) =
discard
method setCurrentNetwork*(self: Controller, network: network_dto.NetworkDto) =
self.network = network
proc update*(self: Controller, id: collectible_service.UniqueID) =
let collectible = self.collectibleService.getCollectible(self.network.chainId, id)
let collection = self.collectibleService.getCollection(self.network.chainId, collectible.collectionSlug)
self.delegate.setData(collection, collectible, self.network)

View File

@ -1,31 +0,0 @@
import stint
import ../../../../../../app_service/service/network/dto as network_dto
import ../../../../../../app_service/service/collectible/dto as collectible_dto
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
## Abstract class for any input/interaction with this module.
method delete*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method load*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method isLoaded*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method setCurrentNetwork*(self: AccessInterface, network: network_dto.NetworkDto) {.base.} =
raise newException(ValueError, "No implementation available")
method update*(self: AccessInterface, address: string, tokenId: Uint256) {.base.} =
raise newException(ValueError, "No implementation available")
method setData*(self: AccessInterface, collection: collectible_dto.CollectionDto, collectible: collectible_dto.CollectibleDto, network: network_dto.NetworkDto) {.base.} =
raise newException(ValueError, "No implementation available")
# View Delegate Interface
# Delegate for the view must be declared here due to use of QtObject and multi
# inheritance, which is not well supported in Nim.
method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -1,60 +0,0 @@
import NimQml, sequtils, sugar, stint
import ../../../../../global/global_singleton
import ./io_interface, ./view, ./controller
import ../io_interface as delegate_interface
import ../../../../../../app_service/service/collectible/service as collectible_service
import ../../../../../../app_service/service/collectible/dto as collectible_dto
import ../../../../../../app_service/service/network/dto as network_dto
import ../models/collectibles_item as collectibles_item
import ../models/collectibles_utils
export io_interface
type
Module* = ref object of io_interface.AccessInterface
delegate: delegate_interface.AccessInterface
view: View
controller: Controller
moduleLoaded: bool
proc newModule*(
delegate: delegate_interface.AccessInterface,
collectibleService: collectible_service.Service
): Module =
result = Module()
result.delegate = delegate
result.view = newView(result)
result.controller = newController(result, collectibleService)
result.moduleLoaded = false
method delete*(self: Module) =
self.view.delete
method load*(self: Module) =
singletonInstance.engine.setRootContextProperty("walletSectionCurrentCollectible", newQVariant(self.view))
self.controller.init()
self.view.load()
method isLoaded*(self: Module): bool =
return self.moduleLoaded
method viewDidLoad*(self: Module) =
self.moduleLoaded = true
self.delegate.currentCollectibleModuleDidLoad()
method setCurrentNetwork*(self: Module, network: network_dto.NetworkDto) =
self.controller.setCurrentNetwork(network)
method update*(self: Module, address: string, tokenId: Uint256) =
let id = collectible_dto.UniqueID(
contractAddress: address,
tokenId: tokenId
)
self.controller.update(id)
method setData*(self: Module, collection: collectible_dto.CollectionDto, collectible: collectible_dto.CollectibleDto, network: network_dto.NetworkDto) =
let item = collectibleToItem(collectible, collection)
self.view.setData(item, network)

View File

@ -1,175 +0,0 @@
import NimQml, sequtils, sugar, stint
import ./io_interface
import ../../../../../../app_service/service/network/dto as network_dto
import ../models/collectibles_item
import ../models/collectible_trait_item
import ../models/collectible_trait_model
QtObject:
type
View* = ref object of QObject
delegate: io_interface.AccessInterface
networkShortName: string
networkColor: string
networkIconUrl: string
collectible: Item
propertiesModel: TraitModel
rankingsModel: TraitModel
statsModel: TraitModel
proc setup(self: View) =
self.QObject.setup
proc delete*(self: View) =
self.QObject.delete
proc newView*(delegate: io_interface.AccessInterface): View =
new(result, delete)
result.setup()
result.delegate = delegate
result.collectible = initItem()
result.propertiesModel = newTraitModel()
result.rankingsModel = newTraitModel()
result.statsModel = newTraitModel()
proc load*(self: View) =
self.delegate.viewDidLoad()
proc getNetworkShortName(self: View): QVariant {.slot.} =
return newQVariant(self.networkShortName)
proc networkShortNameChanged(self: View) {.signal.}
QtProperty[QVariant] networkShortName:
read = getNetworkShortName
notify = networkShortNameChanged
proc getNetworkColor(self: View): QVariant {.slot.} =
return newQVariant(self.networkColor)
proc networkColorChanged(self: View) {.signal.}
QtProperty[QVariant] networkColor:
read = getNetworkColor
notify = networkColorChanged
proc getNetworkIconUrl(self: View): QVariant {.slot.} =
return newQVariant(self.networkIconUrl)
proc networkIconUrlChanged(self: View) {.signal.}
QtProperty[QVariant] networkIconUrl:
read = getNetworkIconUrl
notify = networkIconUrlChanged
proc currentCollectibleChanged(self: View) {.signal.}
proc getName(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getName())
QtProperty[QVariant] name:
read = getName
notify = currentCollectibleChanged
proc getID(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getId())
QtProperty[QVariant] id:
read = getID
notify = currentCollectibleChanged
proc getTokenID(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getTokenId().toString())
QtProperty[QVariant] tokenId:
read = getTokenID
notify = currentCollectibleChanged
proc getDescription(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getDescription())
QtProperty[QVariant] description:
read = getDescription
notify = currentCollectibleChanged
proc getBackgroundColor(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getBackgroundColor())
QtProperty[QVariant] backgroundColor:
read = getBackgroundColor
notify = currentCollectibleChanged
proc getMediaUrl(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getMediaUrl())
QtProperty[QVariant] mediaUrl:
read = getMediaUrl
notify = currentCollectibleChanged
proc getMediaType(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getMediaType())
QtProperty[QVariant] mediaType:
read = getMediaType
notify = currentCollectibleChanged
proc getImageUrl(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getImageUrl())
QtProperty[QVariant] imageUrl:
read = getImageUrl
notify = currentCollectibleChanged
proc getCollectionName(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getCollectionName())
QtProperty[QVariant] collectionName:
read = getCollectionName
notify = currentCollectibleChanged
proc getCollectionImageUrl(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getCollectionImageUrl())
QtProperty[QVariant] collectionImageUrl:
read = getCollectionImageUrl
notify = currentCollectibleChanged
proc getPermalink(self: View): QVariant {.slot.} =
return newQVariant(self.collectible.getPermalink())
QtProperty[QVariant] permalink:
read = getPermalink
notify = currentCollectibleChanged
proc getProperties*(self: View): QVariant {.slot.} =
return newQVariant(self.propertiesModel)
QtProperty[QVariant] properties:
read = getProperties
notify = currentCollectibleChanged
proc getRankings*(self: View): QVariant {.slot.} =
return newQVariant(self.rankingsModel)
QtProperty[QVariant] rankings:
read = getRankings
notify = currentCollectibleChanged
proc getStats*(self: View): QVariant {.slot.} =
return newQVariant(self.statsModel)
QtProperty[QVariant] stats:
read = getStats
notify = currentCollectibleChanged
proc update*(self: View, address: string, tokenId: string) {.slot.} =
self.delegate.update(address, parse(tokenId, Uint256))
proc setData*(self: View, collectible: Item, network: network_dto.NetworkDto) =
if (self.networkShortName != network.shortName):
self.networkShortName = network.shortName
self.networkShortNameChanged()
if (self.networkColor != network.chainColor):
self.networkColor = network.chainColor
self.networkColorChanged()
if (self.networkIconUrl != network.iconURL):
self.networkIconUrl = network.iconURL
self.networkIconUrlChanged()
self.collectible = collectible
self.propertiesModel.setItems(collectible.getProperties())
self.rankingsModel.setItems(collectible.getRankings())
self.statsModel.setItems(collectible.getStats())
self.currentCollectibleChanged()

View File

@ -0,0 +1,72 @@
import NimQml, logging, std/json, sequtils, strutils
import tables, stint
import app/core/eventemitter
import app/core/signals/types
import backend/collectibles as backend_collectibles
type EventCallbackProc = proc (eventObject: JsonNode)
type WalletEventCallbackProc = proc (data: WalletSignal)
# EventsHandler responsible for catching collectibles related backend events and reporting them
QtObject:
type
EventsHandler* = ref object of QObject
events: EventEmitter
eventHandlers: Table[string, EventCallbackProc]
walletEventHandlers: Table[string, WalletEventCallbackProc]
collectiblesOwnershipUpdateFinishedFn: proc()
proc setup(self: EventsHandler) =
self.QObject.setup
proc delete*(self: EventsHandler) =
self.QObject.delete
proc onOwnedCollectiblesFilteringDone*(self: EventsHandler, handler: EventCallbackProc) =
self.eventHandlers[backend_collectibles.eventOwnedCollectiblesFilteringDone] = handler
proc onCollectiblesOwnershipUpdateFinished*(self: EventsHandler, handler: proc()) =
self.collectiblesOwnershipUpdateFinishedFn = handler
proc handleApiEvents(self: EventsHandler, e: Args) =
var data = WalletSignal(e)
if self.walletEventHandlers.hasKey(data.eventType):
let callback = self.walletEventHandlers[data.eventType]
callback(data)
elif self.eventHandlers.hasKey(data.eventType):
var responseJson: JsonNode
responseJson = parseJson(data.message)
if responseJson.kind != JObject:
error "unexpected json type", responseJson.kind
return
let callback = self.eventHandlers[data.eventType]
callback(responseJson)
else:
discard
proc setupWalletEventHandlers(self: EventsHandler) =
self.walletEventHandlers[backend_collectibles.eventCollectiblesOwnershipUpdateFinished] = proc (data: WalletSignal) =
if self.collectiblesOwnershipUpdateFinishedFn == nil:
return
self.collectiblesOwnershipUpdateFinishedFn()
proc newEventsHandler*(events: EventEmitter): EventsHandler =
new(result, delete)
result.events = events
result.eventHandlers = initTable[string, EventCallbackProc]()
result.setup()
result.setupWalletEventHandlers()
# Register for wallet events
let eventsHandler = result
result.events.on(SignalType.Wallet.event, proc(e: Args) =
eventsHandler.handleApiEvents(e)
)

View File

@ -1,45 +0,0 @@
import ../../../../../app_service/service/collectible/service
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
## Abstract class for any input/interaction with this module.
method delete*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method load*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method isLoaded*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method filterChanged*(self: AccessInterface, addresses: seq[string], chainIds: seq[int]) {.base.} =
raise newException(ValueError, "No implementation available")
method fetchOwnedCollectibles*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method onFetchStarted*(self: AccessInterface, chainId: int, address: string) {.base.} =
raise newException(ValueError, "No implementation available")
method setCollectibles*(self: AccessInterface, data: seq[CollectiblesData]) {.base.} =
raise newException(ValueError, "No implementation available")
method appendCollectibles*(self: AccessInterface, chainId: int, address: string, data: CollectiblesData) {.base.} =
raise newException(ValueError, "No implementation available")
method resetCollectibles*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method getHasCollectiblesCache*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
# Methods called by submodules of this module
method collectiblesModuleDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method currentCollectibleModuleDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -1,23 +0,0 @@
type
CollectibleTrait* = object
traitType, value, displayType, maxValue: string
proc getTraitType*(self: CollectibleTrait): string =
return self.traitType
proc getValue*(self: CollectibleTrait): string =
return self.value
proc getDisplayType*(self: CollectibleTrait): string =
return self.displayType
proc getMaxValue*(self: CollectibleTrait): string =
return self.maxValue
proc initTrait*(
traitType, value, displayType, maxValue: string
): CollectibleTrait =
result.traitType = traitType
result.value = value
result.displayType = displayType
result.maxValue = maxValue

View File

@ -1,30 +0,0 @@
import sequtils, sugar, times
import ../../../../../../app_service/service/collectible/dto
import collectibles_item, collectible_trait_item
proc collectibleToItem*(c: CollectibleDto, co: CollectionDto, isPinned: bool = false) : Item =
var mediaUrl = c.animationUrl
var mediaType = c.animationMediaType
if mediaUrl == "":
mediaUrl = c.imageUrl
mediaType = "image"
return initItem(
c.id,
c.address,
c.tokenId,
c.name,
mediaUrl,
mediaType,
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)),
co.name,
co.slug,
co.imageUrl,
isPinned
)

View File

@ -1,142 +0,0 @@
import NimQml, Tables, sequtils, sugar
import ../../../../global/global_singleton
import ../../../../core/eventemitter
import ./io_interface, ./view, ./controller
import ../io_interface as delegate_interface
import ../../../../../app_service/service/collectible/service as collectible_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/node/service as node_service
import ../../../../../app_service/service/network_connection/service as network_connection_service
import ./current_collectible/module as current_collectible_module
import ./models/collectibles_item as collectibles_item
import ./models/collectible_trait_item as collectible_trait_item
import ./models/collectibles_utils
import ./models/collectibles_model as collectibles_model
export io_interface
type
Module* = ref object of io_interface.AccessInterface
delegate: delegate_interface.AccessInterface
view: View
controller: Controller
moduleLoaded: bool
chainId: int
addresses: seq[string]
currentCollectibleModule: current_collectible_module.AccessInterface
proc newModule*(
delegate: delegate_interface.AccessInterface,
events: EventEmitter,
collectibleService: collectible_service.Service,
walletAccountService: wallet_account_service.Service,
networkService: network_service.Service,
nodeService: node_service.Service,
networkConnectionService: network_connection_service.Service
): Module =
result = Module()
result.delegate = delegate
result.view = newView(result)
result.controller = newController(result, events, collectibleService, walletAccountService, networkService, nodeService, networkConnectionService)
result.moduleLoaded = false
result.chainId = 0
result.addresses = @[]
result.currentCollectibleModule = currentCollectibleModule.newModule(result, collectibleService)
method delete*(self: Module) =
self.view.delete
self.currentCollectibleModule.delete
method load*(self: Module) =
singletonInstance.engine.setRootContextProperty("walletSectionCollectibles", newQVariant(self.view))
self.controller.init()
self.view.load()
self.currentCollectibleModule.load
method isLoaded*(self: Module): bool =
return self.moduleLoaded
proc checkIfModuleDidLoad(self: Module) =
if self.moduleLoaded:
return
if(not self.currentCollectibleModule.isLoaded()):
return
self.moduleLoaded = true
self.delegate.collectiblesModuleDidLoad()
method viewDidLoad*(self: Module) =
self.checkIfModuleDidLoad()
method currentCollectibleModuleDidLoad*(self: Module) =
self.checkIfModuleDidLoad()
method fetchOwnedCollectibles*(self: Module) =
self.controller.fetchOwnedCollectibles(self.chainId, self.addresses)
method filterChanged*(self: Module, addresses: seq[string], chainIds: seq[int]) =
let network = self.controller.getNetwork()
self.chainId = network.chainId
self.addresses = addresses
self.currentCollectibleModule.setCurrentNetwork(network)
self.view.setCollectibles(@[])
let data = self.controller.getOwnedCollectibles(self.chainId, self.addresses)
for i, addressData in data.pairs:
if not addressData.anyLoaded:
self.controller.fetchOwnedCollectibles(self.chainId, @[self.addresses[i]])
self.setCollectibles(data)
proc ownedCollectibleToItem(self: Module, oc: OwnedCollectible): Item =
let c = self.controller.getCollectible(self.chainId, oc.id)
let col = self.controller.getCollection(self.chainId, c.collectionSlug)
return collectibleToItem(c, col, oc.isFromWatchedContract)
method onFetchStarted*(self: Module, chainId: int, address: string) =
if self.chainId == chainId and address in self.addresses:
self.view.setIsFetching(true)
method resetCollectibles*(self: Module) =
let data = self.controller.getOwnedCollectibles(self.chainId, self.addresses)
self.setCollectibles(data)
method appendCollectibles*(self: Module, chainId: int, address: string, data: CollectiblesData) =
if not (self.chainId == chainId and address in self.addresses):
return
self.view.setIsError(data.isError)
if data.isError and not data.anyLoaded:
# If fetching failed before being able to get any collectibles info,
# show loading animation
self.view.setIsFetching(true)
else:
self.view.setIsFetching(data.isFetching)
if data.lastLoadCount > 0:
var ownedCollectiblesToAdd = newSeq[OwnedCollectible]()
for i in data.collectibles.len - data.lastLoadCount ..< data.collectibles.len:
ownedCollectiblesToAdd.add(data.collectibles[i])
let newCollectibles = ownedCollectiblesToAdd.map(oc => self.ownedCollectibleToItem(oc))
self.view.appendCollectibles(newCollectibles)
self.view.setAllLoaded(data.allLoaded)
method setCollectibles*(self: Module, data: seq[CollectiblesData]) =
for index, address in self.addresses:
self.appendCollectibles(self.chainId, address, data[index])
method getHasCollectiblesCache*(self: Module): bool =
return self.controller.getHasCollectiblesCache(self.addresses[0])

View File

@ -1,53 +0,0 @@
import NimQml
import ./models/collectibles_model
import ./models/collectibles_item
import ./io_interface
QtObject:
type
View* = ref object of QObject
delegate: io_interface.AccessInterface
model: Model
proc delete*(self: View) =
self.model.delete
self.QObject.delete
proc newView*(delegate: io_interface.AccessInterface): View =
new(result, delete)
result.QObject.setup
result.delegate = delegate
result.model = newModel()
signalConnect(result.model, "requestFetch()", result, "fetchMoreOwnedCollectibles()")
proc load*(self: View) =
self.delegate.viewDidLoad()
proc modelChanged*(self: View) {.signal.}
proc getModel(self: View): QVariant {.slot.} =
return newQVariant(self.model)
QtProperty[QVariant] model:
read = getModel
notify = modelChanged
proc fetchMoreOwnedCollectibles*(self: View) {.slot.} =
self.delegate.fetchOwnedCollectibles()
proc setIsError*(self: View, isError: bool) =
self.model.setIsError(isError)
proc setIsFetching*(self: View, isFetching: bool) =
self.model.setIsFetching(isFetching)
proc setAllLoaded*(self: View, allLoaded: bool) =
self.model.setAllCollectiblesLoaded(allLoaded)
proc setCollectibles*(self: View, collectibles: seq[Item]) =
self.model.setItems(collectibles)
proc appendCollectibles*(self: View, collectibles: seq[Item]) =
self.model.appendItems(collectibles)
proc getHasCollectiblesCache(self: View): bool {.slot.} =
return self.delegate.getHasCollectiblesCache()

View File

@ -6,7 +6,6 @@ import ../io_interface as delegate_interface
import ./accounts/module as accounts_module
import ./all_tokens/module as all_tokens_module
import ./collectibles/module as collectibles_module
import ./assets/module as assets_module
import ./transactions/module as transactions_module
import ./saved_addresses/module as saved_addresses_module
@ -17,6 +16,8 @@ import ./send/module as send_module
import ../../shared_modules/add_account/module as add_account_module
import ./activity/controller as activityc
import ./collectibles/controller as collectiblesc
import ./collectible_details/controller as collectible_detailsc
import ../../../global/global_singleton
import ../../../core/eventemitter
@ -49,7 +50,6 @@ type
accountsModule: accounts_module.AccessInterface
allTokensModule: all_tokens_module.AccessInterface
collectiblesModule: collectibles_module.AccessInterface
assetsModule: assets_module.AccessInterface
sendModule: send_module.AccessInterface
transactionsModule: transactions_module.AccessInterface
@ -64,6 +64,8 @@ type
walletAccountService: wallet_account_service.Service
activityController: activityc.Controller
collectiblesController: collectiblesc.Controller
collectibleDetailsController: collectible_detailsc.Controller
proc newModule*(
delegate: delegate_interface.AccessInterface,
@ -92,7 +94,6 @@ proc newModule*(
result.accountsModule = accounts_module.newModule(result, events, walletAccountService, networkService, currencyService)
result.allTokensModule = all_tokens_module.newModule(result, events, tokenService, walletAccountService)
result.collectiblesModule = collectibles_module.newModule(result, events, collectibleService, walletAccountService, networkService, nodeService, networkConnectionService)
result.assetsModule = assets_module.newModule(result, events, walletAccountService, networkService, tokenService, currencyService)
result.transactionsModule = transactions_module.newModule(result, events, transactionService, walletAccountService, networkService, currencyService)
result.sendModule = send_module.newModule(result, events, walletAccountService, networkService, currencyService, transactionService)
@ -102,15 +103,16 @@ proc newModule*(
result.networksModule = networks_module.newModule(result, events, networkService, walletAccountService, settingsService)
result.networksService = networkService
result.activityController = activityc.newController(result.transactionsModule, currencyService, tokenService, events)
result.collectiblesController = collectiblesc.newController(events)
result.collectibleDetailsController = collectible_detailsc.newController(networkService, events)
result.filter = initFilter(result.controller)
result.view = newView(result, result.activityController)
result.view = newView(result, result.activityController, result.collectiblesController, result.collectibleDetailsController)
method delete*(self: Module) =
self.accountsModule.delete
self.allTokensModule.delete
self.collectiblesModule.delete
self.assetsModule.delete
self.transactionsModule.delete
self.savedAddressesModule.delete
@ -119,6 +121,8 @@ method delete*(self: Module) =
self.controller.delete
self.view.delete
self.activityController.delete
self.collectiblesController.delete
self.collectibleDetailsController.delete
if not self.addAccountModule.isNil:
self.addAccountModule.delete
@ -143,11 +147,11 @@ method notifyFilterChanged(self: Module) =
let includeWatchOnly = self.controller.isIncludeWatchOnlyAccount()
self.overviewModule.filterChanged(self.filter.addresses, self.filter.chainIds, includeWatchOnly, self.filter.allAddresses)
self.assetsModule.filterChanged(self.filter.addresses, self.filter.chainIds)
self.collectiblesModule.filterChanged(self.filter.addresses, self.filter.chainIds)
self.transactionsModule.filterChanged(self.filter.addresses, self.filter.chainIds)
self.accountsModule.filterChanged(self.filter.addresses, self.filter.chainIds)
self.sendModule.filterChanged(self.filter.addresses, self.filter.chainIds)
self.activityController.globalFilterChanged(self.filter.addresses, self.filter.chainIds)
self.collectiblesController.globalFilterChanged(self.filter.addresses, self.filter.chainIds)
if self.filter.addresses.len > 0:
self.view.filterChanged(self.filter.addresses[0], includeWatchOnly, self.filter.allAddresses)
@ -214,7 +218,6 @@ method load*(self: Module) =
self.view.load()
self.accountsModule.load()
self.allTokensModule.load()
self.collectiblesModule.load()
self.assetsModule.load()
self.transactionsModule.load()
self.savedAddressesModule.load()
@ -233,9 +236,6 @@ proc checkIfModuleDidLoad(self: Module) =
if(not self.allTokensModule.isLoaded()):
return
if(not self.collectiblesModule.isLoaded()):
return
if(not self.assetsModule.isLoaded()):
return

View File

@ -1,6 +1,8 @@
import NimQml, json
import ./activity/controller as activityc
import ./collectibles/controller as collectiblesc
import ./collectible_details/controller as collectible_detailsc
import ./io_interface
import ../../shared_models/currency_amount
@ -14,6 +16,8 @@ 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
collectiblesController: collectiblesc.Controller
collectibleDetailsController: collectible_detailsc.Controller
proc setup(self: View) =
self.QObject.setup
@ -21,10 +25,12 @@ QtObject:
proc delete*(self: View) =
self.QObject.delete
proc newView*(delegate: io_interface.AccessInterface, activityController: activityc.Controller): View =
proc newView*(delegate: io_interface.AccessInterface, activityController: activityc.Controller, collectiblesController: collectiblesc.Controller, collectibleDetailsController: collectible_detailsc.Controller): View =
new(result, delete)
result.delegate = delegate
result.activityController = activityController
result.collectiblesController = collectiblesController
result.collectibleDetailsController = collectibleDetailsController
result.setup()
proc load*(self: View) =
@ -119,3 +125,13 @@ QtObject:
return newQVariant(self.activityController)
QtProperty[QVariant] activityController:
read = getActivityController
proc getCollectiblesController(self: View): QVariant {.slot.} =
return newQVariant(self.collectiblesController)
QtProperty[QVariant] collectiblesController:
read = getCollectiblesController
proc getCollectibleDetailsController(self: View): QVariant {.slot.} =
return newQVariant(self.collectibleDetailsController)
QtProperty[QVariant] collectibleDetailsController:
read = getCollectibleDetailsController

View File

@ -0,0 +1,165 @@
import NimQml, json, strformat, sequtils, strutils, stint, strutils
import backend/collectibles as backend
import collectible_trait_model
# Additional data needed to build an Entry, which is
# not included in the backend data and needs to be
# fetched from a different source.
type
ExtraData* = object
networkShortName*: string
networkColor*: string
networkIconURL*: string
# It is used to display a detailed collectibles entry in the QML UI
QtObject:
type
CollectibleDetailsEntry* = ref object of QObject
id: backend.CollectibleUniqueID
data: backend.CollectibleData
extradata: ExtraData
traits: TraitModel
proc setup(self: CollectibleDetailsEntry) =
self.QObject.setup
proc delete*(self: CollectibleDetailsEntry) =
self.QObject.delete
proc newCollectibleDetailsFullEntry*(data: backend.CollectibleData, extradata: ExtraData): CollectibleDetailsEntry =
new(result, delete)
result.id = data.id
result.data = data
result.extradata = extradata
result.traits = newTraitModel()
result.traits.setItems(data.traits)
result.setup()
proc newCollectibleDetailsBasicEntry*(id: backend.CollectibleUniqueID, extradata: ExtraData): CollectibleDetailsEntry =
new(result, delete)
result.id = id
result.extradata = extradata
result.traits = newTraitModel()
result.setup()
proc newCollectibleDetailsEmptyEntry*(): CollectibleDetailsEntry =
let id = backend.CollectibleUniqueID(
tokenID: stint.u256(0)
)
let extradata = ExtraData()
return newCollectibleDetailsBasicEntry(id, extradata)
proc `$`*(self: CollectibleDetailsEntry): string =
return fmt"""CollectibleDetailsEntry(
id:{self.id},
data:{self.data},
extradata:{self.extradata},
traits:{self.traits}
)"""
proc getChainID*(self: CollectibleDetailsEntry): int {.slot.} =
return self.id.chainID
QtProperty[int] chainId:
read = getChainID
proc getContractAddress*(self: CollectibleDetailsEntry): string {.slot.} =
return self.id.contractAddress
QtProperty[string] contractAddress:
read = getContractAddress
proc getTokenID*(self: CollectibleDetailsEntry): string {.slot.} =
return self.id.tokenID.toString()
QtProperty[string] tokenId:
read = getTokenID
proc getName*(self: CollectibleDetailsEntry): string {.slot.} =
if self.data == nil:
return ""
return self.data.name
QtProperty[string] name:
read = getName
proc getImageURL*(self: CollectibleDetailsEntry): string {.slot.} =
if self.data == nil:
return ""
return self.data.imageUrl
QtProperty[string] imageUrl:
read = getImageURL
proc getMediaURL*(self: CollectibleDetailsEntry): string {.slot.} =
if self.data == nil:
return ""
return self.data.animationUrl
QtProperty[string] mediaUrl:
read = getMediaURL
proc getMediaType*(self: CollectibleDetailsEntry): string {.slot.} =
if self.data == nil:
return ""
return self.data.animationMediaType
QtProperty[string] mediaType:
read = getMediaType
proc getBackgroundColor*(self: CollectibleDetailsEntry): string {.slot.} =
if self.data == nil:
return ""
return self.data.backgroundColor
QtProperty[string] backgroundColor:
read = getBackgroundColor
proc getCollectionName*(self: CollectibleDetailsEntry): string {.slot.} =
if self.data == nil:
return ""
return self.data.collectionData.name
QtProperty[string] collectionName:
read = getCollectionName
proc getDescription*(self: CollectibleDetailsEntry): string {.slot.} =
if self.data == nil:
return ""
return self.data.description
QtProperty[string] description:
read = getDescription
proc getCollectionImageURL*(self: CollectibleDetailsEntry): string {.slot.} =
if self.data == nil:
return ""
return self.data.collectionData.imageUrl
QtProperty[string] collectionImageUrl:
read = getCollectionImageURL
proc getTraits*(self: CollectibleDetailsEntry): QVariant {.slot.} =
return newQVariant(self.traits)
QtProperty[QVariant] traits:
read = getTraits
proc getNetworkShortName*(self: CollectibleDetailsEntry): string {.slot.} =
return self.extradata.networkShortName
QtProperty[string] networkShortName:
read = getNetworkShortName
proc getNetworkColor*(self: CollectibleDetailsEntry): string {.slot.} =
return self.extradata.networkColor
QtProperty[string] networkColor:
read = getNetworkColor
proc getNetworkIconURL*(self: CollectibleDetailsEntry): string {.slot.} =
return self.extradata.networkIconURL
QtProperty[string] networkIconUrl:
read = getNetworkIconURL

View File

@ -1,6 +1,6 @@
import NimQml, Tables, strutils, strformat
import ./collectible_trait_item
import backend/collectibles as backend
type
ModelRole {.pure.} = enum
@ -12,7 +12,7 @@ type
QtObject:
type
TraitModel* = ref object of QAbstractListModel
items: seq[CollectibleTrait]
items: seq[backend.CollectibleTrait]
proc delete(self: TraitModel) =
self.items = @[]
@ -61,13 +61,13 @@ QtObject:
case enumRole:
of ModelRole.TraitType:
result = newQVariant(item.getTraitType())
result = newQVariant(item.trait_type)
of ModelRole.Value:
result = newQVariant(item.getValue())
result = newQVariant(item.value)
of ModelRole.DisplayType:
result = newQVariant(item.getDisplayType())
result = newQVariant(item.display_type)
of ModelRole.MaxValue:
result = newQVariant(item.getMaxValue())
result = newQVariant(item.max_value)
proc setItems*(self: TraitModel, items: seq[CollectibleTrait]) =
self.beginResetModel()

View File

@ -1,67 +1,45 @@
import strformat, stint
import ./collectible_trait_item
type
Item* = object
id: int
address: string
chainId: int
contractAddress: string
tokenId: UInt256
name: string
mediaUrl: string
mediaType: string
imageUrl: string
backgroundColor: string
description: string
permalink: string
properties: seq[CollectibleTrait]
rankings: seq[CollectibleTrait]
stats: seq[CollectibleTrait]
collectionName: string
collectionSlug: string
collectionImageUrl: string
isLoading: bool
isPinned: bool
proc initItem*(
id: int,
address: string,
chainId: int,
contractAddress: string,
tokenId: UInt256,
name: string,
mediaUrl: string,
mediaType: string,
imageUrl: string,
backgroundColor: string,
description: string,
permalink: string,
properties: seq[CollectibleTrait],
rankings: seq[CollectibleTrait],
stats: seq[CollectibleTrait],
collectionName: string,
collectionSlug: string,
collectionImageUrl: string,
isPinned: bool
): Item =
result.id = id
result.address = address
result.chainId = chainId
result.contractAddress = contractAddress
result.tokenId = tokenId
result.name = if (name != ""): name else: ("#" & tokenId.toString())
result.mediaUrl = mediaUrl
result.mediaType = mediaType
result.imageUrl = imageUrl
result.backgroundColor = if (backgroundColor == ""): "transparent" else: ("#" & backgroundColor)
result.description = description
result.permalink = permalink
result.properties = properties
result.rankings = rankings
result.stats = stats
result.collectionName = collectionName
result.collectionSlug = collectionSlug
result.collectionImageUrl = collectionImageUrl
result.isLoading = false
result.isPinned = isPinned
proc initItem*: Item =
result = initItem(-1, "", u256(0), "", "", "", "", "transparent", "Collectibles", "", @[], @[], @[], "", "", "", false)
result = initItem(0, "", u256(0), "", "", "", "", "transparent", "Collectibles", false)
proc initLoadingItem*: Item =
result = initItem()
@ -69,32 +47,32 @@ proc initLoadingItem*: Item =
proc `$`*(self: Item): string =
result = fmt"""Collectibles(
id: {self.id},
address: {self.address},
chainId: {self.chainId},
contractAddress: {self.contractAddress},
tokenId: {self.tokenId},
name: {self.name},
mediaUrl: {self.mediaUrl},
mediaType: {self.mediaType},
imageUrl: {self.imageUrl},
backgroundColor: {self.backgroundColor},
description: {self.description},
permalink: {self.permalink},
collectionName: {self.collectionName},
collectionSlug: {self.collectionSlug},
collectionImageUrl: {self.collectionImageUrl},
isLoading: {self.isLoading},
isPinned: {self.isPinned},
]"""
proc getId*(self: Item): int =
return self.id
proc getChainId*(self: Item): int =
return self.chainId
proc getAddress*(self: Item): string =
return self.address
proc getContractAddress*(self: Item): string =
return self.contractAddress
proc getTokenId*(self: Item): UInt256 =
return self.tokenId
# Unique ID to identify collectible, generated by us
proc getId*(self: Item): string =
return fmt"{self.getChainId}+{self.getContractAddress}+{self.getTokenID}"
proc getName*(self: Item): string =
return self.name
@ -110,30 +88,9 @@ proc getImageUrl*(self: Item): string =
proc getBackgroundColor*(self: Item): string =
return self.backgroundColor
proc getDescription*(self: Item): string =
return self.description
proc getPermalink*(self: Item): string =
return self.permalink
proc getProperties*(self: Item): seq[CollectibleTrait] =
return self.properties
proc getRankings*(self: Item): seq[CollectibleTrait] =
return self.rankings
proc getStats*(self: Item): seq[CollectibleTrait] =
return self.stats
proc getCollectionName*(self: Item): string =
return self.collectionName
proc getCollectionSlug*(self: Item): string =
return self.collectionSlug
proc getCollectionImageUrl*(self: Item): string =
return self.collectionImageUrl
proc getIsLoading*(self: Item): bool =
return self.isLoading

View File

@ -1,25 +1,20 @@
import NimQml, Tables, strutils, strformat, sequtils, stint
import logging
import ./collectibles_item, ./collectible_trait_model
type
CollectibleRole* {.pure.} = enum
Id = UserRole + 1,
Address
Uid = UserRole + 1,
ChainId
ContractAddress
TokenId
Name
ImageUrl
MediaUrl
MediaType
ImageUrl
BackgroundColor
Description
Permalink
Properties
Rankings
Stats
CollectionName
CollectionSlug
CollectionImageUrl
IsLoading
IsPinned
@ -29,7 +24,7 @@ QtObject:
type
Model* = ref object of QAbstractListModel
items: seq[Item]
allCollectiblesLoaded: bool
hasMore: bool
isFetching: bool
isError: bool
loadingItemsStartIdx: int
@ -48,7 +43,7 @@ QtObject:
new(result, delete)
result.setup
result.items = @[]
result.allCollectiblesLoaded = false
result.hasMore = true
result.isFetching = false
result.isError = false
result.loadingItemsStartIdx = -1
@ -92,46 +87,41 @@ QtObject:
self.isError = value
self.isErrorChanged()
proc allCollectiblesLoadedChanged(self: Model) {.signal.}
proc getAllCollectiblesLoaded*(self: Model): bool {.slot.} =
self.allCollectiblesLoaded
QtProperty[bool] allCollectiblesLoaded:
read = getAllCollectiblesLoaded
notify = allCollectiblesLoadedChanged
proc setAllCollectiblesLoaded*(self: Model, value: bool) =
if value == self.allCollectiblesLoaded:
proc hasMoreChanged*(self: Model) {.signal.}
proc getHasMore*(self: Model): bool {.slot.} =
self.hasMore
QtProperty[bool] hasMore:
read = getHasMore
notify = hasMoreChanged
proc setHasMore(self: Model, hasMore: bool) {.slot.} =
if hasMore == self.hasMore:
return
self.allCollectiblesLoaded = value
self.allCollectiblesLoadedChanged()
self.hasMore = hasMore
self.hasMoreChanged()
method canFetchMore*(self: Model, parent: QModelIndex): bool =
return not self.allCollectiblesLoaded and not self.isFetching and not self.isError
return self.hasMore
proc loadMoreItems(self: Model) {.signal.}
proc requestFetch(self: Model) {.signal.}
method fetchMore*(self: Model, parent: QModelIndex) =
self.requestFetch()
self.loadMoreItems()
method rowCount*(self: Model, index: QModelIndex = nil): int =
return self.items.len
method roleNames(self: Model): Table[int, string] =
{
CollectibleRole.Id.int:"id",
CollectibleRole.Address.int:"address",
CollectibleRole.Uid.int:"uid",
CollectibleRole.ChainId.int:"chainId",
CollectibleRole.ContractAddress.int:"contractAddress",
CollectibleRole.TokenId.int:"tokenId",
CollectibleRole.Name.int:"name",
CollectibleRole.MediaUrl.int:"mediaUrl",
CollectibleRole.MediaType.int:"mediaType",
CollectibleRole.ImageUrl.int:"imageUrl",
CollectibleRole.BackgroundColor.int:"backgroundColor",
CollectibleRole.Description.int:"description",
CollectibleRole.Permalink.int:"permalink",
CollectibleRole.Properties.int:"properties",
CollectibleRole.Rankings.int:"rankings",
CollectibleRole.Stats.int:"stats",
CollectibleRole.CollectionName.int:"collectionName",
CollectibleRole.CollectionSlug.int:"collectionSlug",
CollectibleRole.CollectionImageUrl.int:"collectionImageUrl",
CollectibleRole.IsLoading.int:"isLoading",
CollectibleRole.IsPinned.int:"isPinned",
}.toTable
@ -147,10 +137,12 @@ QtObject:
let enumRole = role.CollectibleRole
case enumRole:
of CollectibleRole.Id:
of CollectibleRole.Uid:
result = newQVariant(item.getId())
of CollectibleRole.Address:
result = newQVariant(item.getAddress())
of CollectibleRole.ChainId:
result = newQVariant(item.getChainId())
of CollectibleRole.ContractAddress:
result = newQVariant(item.getContractAddress())
of CollectibleRole.TokenId:
result = newQVariant(item.getTokenId().toString())
of CollectibleRole.Name:
@ -163,28 +155,8 @@ QtObject:
result = newQVariant(item.getImageUrl())
of CollectibleRole.BackgroundColor:
result = newQVariant(item.getBackgroundColor())
of CollectibleRole.Description:
result = newQVariant(item.getDescription())
of CollectibleRole.Permalink:
result = newQVariant(item.getPermalink())
of CollectibleRole.Properties:
let traits = newTraitModel()
traits.setItems(item.getProperties())
result = newQVariant(traits)
of CollectibleRole.Rankings:
let traits = newTraitModel()
traits.setItems(item.getRankings())
result = newQVariant(traits)
of CollectibleRole.Stats:
let traits = newTraitModel()
traits.setItems(item.getStats())
result = newQVariant(traits)
of CollectibleRole.CollectionName:
result = newQVariant(item.getCollectionName())
of CollectibleRole.CollectionSlug:
result = newQVariant(item.getCollectionSlug())
of CollectibleRole.CollectionImageUrl:
result = newQVariant(item.getCollectionImageUrl())
of CollectibleRole.IsLoading:
result = newQVariant(item.getIsLoading())
of CollectibleRole.IsPinned:
@ -218,24 +190,29 @@ QtObject:
self.endRemoveRows()
self.countChanged()
proc setItems*(self: Model, items: seq[Item]) =
if self.isFetching:
self.removeLoadingItems()
proc resetModel*(self: Model, newItems: seq[Item]) =
self.beginResetModel()
self.items = items
self.items = newItems
self.endResetModel()
self.countChanged()
if self.isFetching:
self.appendLoadingItems()
proc appendItems*(self: Model, items: seq[Item]) =
proc setItems*(self: Model, newItems: seq[Item], offset: int, hasMore: bool) =
if self.isFetching:
self.removeLoadingItems()
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
self.beginInsertRows(parentModelIndex, self.items.len, self.items.len + items.len - 1)
self.items = concat(self.items, items)
self.endInsertRows()
if offset == 0:
self.resetModel(newItems)
else:
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
if offset != self.items.len:
error "offset != self.items.len"
return
self.beginInsertRows(parentModelIndex, self.items.len, self.items.len + newItems.len - 1)
self.items.add(newItems)
self.endInsertRows()
self.countChanged()
self.setHasMore(hasMore)
if self.isFetching:
self.appendLoadingItems()

View File

@ -0,0 +1,23 @@
import sequtils, sugar, times
import backend/collectibles as backend
import collectibles_item
proc collectibleToItem*(c: backend.CollectibleHeader, isPinned: bool = false) : Item =
var mediaUrl = c.animationUrl
var mediaType = c.animationMediaType
if mediaUrl == "":
mediaUrl = c.imageUrl
mediaType = "image"
return initItem(
c.id.chainID,
c.id.contractAddress,
c.id.tokenID,
c.name,
mediaUrl,
mediaType,
c.imageUrl,
c.backgroundColor,
c.collectionName,
isPinned
)

View File

@ -64,31 +64,3 @@ const fetchOwnedCollectiblesFromContractAddressesTaskArg: Task = proc(argEncoded
"error": e.msg
}
arg.finish(output)
type
FetchCollectiblesTaskArg = ref object of QObjectTaskArg
chainId*: int
ids*: seq[collectibles.NFTUniqueID]
limit: int
const fetchCollectiblesTaskArg: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[FetchCollectiblesTaskArg](argEncoded)
try:
let response = collectibles.getOpenseaAssetsByNFTUniqueID(arg.chainId, arg.ids, arg.limit)
if not response.error.isNil:
raise newException(ValueError, "Error getOpenseaAssetsByNFTUniqueID" & response.error.message)
let output = %* {
"chainId": arg.chainId,
"collectibles": response.result,
"error": ""
}
arg.finish(output)
except Exception as e:
let output = %* {
"chainId": arg.chainId,
"collectibles": "",
"error": e.msg
}
arg.finish(output)

View File

@ -355,36 +355,6 @@ QtObject:
return
result.success = true
proc onRxCollectibles(self: Service, response: string) {.slot.} =
let responseObj = response.parseJson
let chainIdJson = responseObj["chainId"]
let chainId = chainIdJson.getInt()
let errorStr = responseObj["error"].getStr()
if errorStr != "":
error "error onRxCollectibles: ", errorStr
else:
let (success, collectibles, collections, _, _) = processCollectiblesResult(responseObj)
if success:
self.updateCollectiblesCache(chainId, collectibles, collections)
else:
let errDesription = "Could not get data from response"
error "error onRxCollectibles: ", errDesription
proc fetchCollectibles*(self: Service, chainId: int, ids: seq[UniqueID]) =
let arg = FetchCollectiblesTaskArg(
tptr: cast[ByteAddress](fetchCollectiblesTaskArg),
vptr: cast[ByteAddress](self.vptr),
slot: "onRxCollectibles",
chainId: chainId,
ids: ids.map(id => collectibles.NFTUniqueID(
contractAddress: id.contractAddress,
tokenID: id.tokenId.toString()
)),
limit: len(ids)
)
self.threadpool.start(arg)
proc onRxOwnedCollectibles(self: Service, response: string) {.slot.} =
let responseObj = response.parseJson
let chainIdJson = responseObj["chainId"]

View File

@ -39,24 +39,25 @@ const loadTransactionsTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.}
output["allTxLoaded"] = %(response.getElems().len < arg.limit)
# Fetch collectibles for transactions
var uniqueIds: seq[collectibles.NFTUniqueID] = @[]
var uniqueIds: seq[collectibles.CollectibleUniqueID] = @[]
for txJson in response.getElems():
let tx = txJson.toTransactionDto()
if tx.typeValue == ERC721_TRANSACTION_TYPE:
let nftId = collectibles.NFTUniqueID(
let nftId = collectibles.CollectibleUniqueID(
chainID: arg.chainId,
contractAddress: tx.contract,
tokenID: tx.tokenId.toString(10)
tokenID: tx.tokenId
)
if not uniqueIds.any(x => (x == nftId)):
uniqueIds.add(nftId)
if len(uniqueIds) > 0:
let collectiblesResponse = collectibles.getOpenseaAssetsByNFTUniqueID(arg.chainId, uniqueIds, arg.collectiblesLimit)
let collectiblesResponse = collectibles.getOpenseaAssetsByCollectibleUniqueID(uniqueIds)
if not collectiblesResponse.error.isNil:
# We don't want to prevent getting the list of transactions if we cannot get
# NFT metadata. Just don't return the metadata.
let errDesription = "Error getOpenseaAssetsByNFTUniqueID" & collectiblesResponse.error.message
let errDesription = "Error getOpenseaAssetsByCollectibleUniqueID" & collectiblesResponse.error.message
error "error loadTransactionsTask: ", errDesription
else:
output["collectibles"] = collectiblesResponse.result

View File

@ -1,41 +1,95 @@
import json, json_serialization, strformat
import stint, Tables
import core
import response_type, collectibles_types
import ./core, ./response_type
#import ./core, ./response_type
from ./gen import rpc
export response_type, collectibles_types
# Declared in services/wallet/collectibles/service.go
const eventCollectiblesOwnershipUpdateStarted*: string = "wallet-collectibles-ownership-update-started"
const eventCollectiblesOwnershipUpdateFinished*: string = "wallet-collectibles-ownership-update-finished"
const eventCollectiblesOwnershipUpdateFinishedWithError*: string = "wallet-collectibles-ownership-update-finished-with-error"
const eventOwnedCollectiblesFilteringDone*: string = "wallet-owned-collectibles-filtering-done"
const eventGetCollectiblesDataDone*: string = "wallet-get-collectibles-data-done"
type
NFTUniqueID* = ref object of RootObj
contractAddress* {.serializedFieldName("contract_address").}: string
tokenID* {.serializedFieldName("token_id").}: string
# Mirrors services/wallet/collectibles/service.go ErrorCode
ErrorCode* = enum
ErrorCodeSuccess = 1,
ErrorCodeTaskCanceled,
ErrorCodeFailed
proc `$`*(self: NFTUniqueID): string =
return fmt"""NFTUniqueID(
contractAddress:{self.contractAddress},
tokenID:{self.tokenID}
)"""
# Mirrors services/wallet/collectibles/service.go FilterOwnedCollectiblesResponse
FilterOwnedCollectiblesResponse* = object
collectibles*: seq[CollectibleHeader]
offset*: int
hasMore*: bool
errorCode*: ErrorCode
proc `==`*(a, b: NFTUniqueID): bool =
result = a.contractAddress == b.contractAddress and
a.tokenID == b.tokenID
# Mirrors services/wallet/collectibles/service.go GetCollectiblesDataResponse
GetCollectiblesDataResponse* = object
collectibles*: seq[CollectibleData]
errorCode*: ErrorCode
rpc(getOpenseaAssetsByOwnerWithCursor, "wallet"):
# Responses
proc fromJson*(e: JsonNode, T: typedesc[FilterOwnedCollectiblesResponse]): FilterOwnedCollectiblesResponse {.inline.} =
var collectibles: seq[CollectibleHeader]
if e.hasKey("collectibles"):
let jsonCollectibles = e["collectibles"]
collectibles = newSeq[CollectibleHeader](jsonCollectibles.len)
for i in 0 ..< jsonCollectibles.len:
collectibles[i] = fromJson(jsonCollectibles[i], CollectibleHeader)
result = T(
collectibles: collectibles,
offset: e["offset"].getInt(),
hasMore: if e.hasKey("hasMore"): e["hasMore"].getBool()
else: false,
errorCode: ErrorCode(e["errorCode"].getInt())
)
proc fromJson*(e: JsonNode, T: typedesc[GetCollectiblesDataResponse]): GetCollectiblesDataResponse {.inline.} =
var collectibles: seq[CollectibleData] = @[]
if e.hasKey("collectibles"):
let jsonCollectibles = e["collectibles"]
for item in jsonCollectibles.getElems():
collectibles.add(fromJson(item, CollectibleData))
result = T(
collectibles: collectibles,
errorCode: ErrorCode(e["errorCode"].getInt())
)
rpc(getCollectiblesByOwnerWithCursor, "wallet"):
chainId: int
address: string
cursor: string
limit: int
rpc(getOpenseaAssetsByOwnerAndContractAddressWithCursor, "wallet"):
rpc(getCollectiblesByOwnerAndContractAddressWithCursor, "wallet"):
chainId: int
address: string
contractAddresses: seq[string]
cursor: string
limit: int
rpc(getOpenseaAssetsByNFTUniqueID, "wallet"):
chainId: int
uniqueIds: seq[NFTUniqueID]
limit: int
rpc(getCollectiblesByUniqueID, "wallet"):
uniqueIds: seq[CollectibleUniqueID]
rpc(getCollectibleOwnersByContractAddress, "wallet"):
chainId: int
contractAddress: string
rpc(filterOwnedCollectiblesAsync, "wallet"):
chainIDs: seq[int]
addresses: seq[string]
offset: int
limit: int
rpc(getCollectiblesDataAsync, "wallet"):
uniqueIds: seq[CollectibleUniqueID]

View File

@ -0,0 +1,268 @@
import json, strformat
import stint, Tables
type
# Mirrors services/wallet/thirdparty/collectible_types.go CollectibleUniqueID
CollectibleUniqueID* = ref object of RootObj
chainID*: int
contractAddress*: string
tokenID*: UInt256
# Mirrors services/wallet/thirdparty/collectible_types.go CollectibleHeader
CollectibleHeader* = ref object of RootObj
id* : CollectibleUniqueID
name*: string
imageUrl*: string
animationUrl*: string
animationMediaType*: string
backgroundColor*: string
collectionName*: string
# Mirrors services/wallet/thirdparty/collectible_types.go CollectibleTrait
CollectibleTrait* = ref object of RootObj
trait_type*: string
value*: string
display_type*: string
max_value*: string
# Mirrors services/wallet/thirdparty/collectible_types.go CollectionTrait
CollectionTrait* = ref object of RootObj
min*: float
max*: float
# Mirrors services/wallet/thirdparty/collectible_types.go CollectionData
CollectionData* = ref object of RootObj
name*: string
slug*: string
imageUrl*: string
traits*: Table[string, CollectionTrait]
# Mirrors services/wallet/thirdparty/collectible_types.go CollectibleData
CollectibleData* = ref object of RootObj
id* : CollectibleUniqueID
name*: string
description*: string
permalink*: string
imageUrl*: string
animationUrl*: string
animationMediaType*: string
traits*: seq[CollectibleTrait]
backgroundColor*: string
tokenUri*: string
collectionData*: CollectionData
# Mirrors services/wallet/thirdparty/collectible_types.go TokenBalance
CollectibleBalance* = ref object
tokenId*: UInt256
balance*: UInt256
# Mirrors services/wallet/thirdparty/collectible_types.go CollectibleOwner
CollectibleOwner* = ref object
address*: string
balances*: seq[CollectibleBalance]
# Mirrors services/wallet/thirdparty/collectible_types.go CollectibleContractOwnership
CollectibleContractOwnership* = ref object
contractAddress*: string
owners*: seq[CollectibleOwner]
# CollectibleUniqueID
proc `$`*(self: CollectibleUniqueID): string =
return fmt"""CollectibleUniqueID(
chainID:{self.chainID},
contractAddress:{self.contractAddress},
tokenID:{self.tokenID}
)"""
proc `==`*(a, b: CollectibleUniqueID): bool =
result = a.chainID == b.chainID and
a.contractAddress == b.contractAddress and
a.tokenID == b.tokenID
proc `%`*(t: CollectibleUniqueID): JsonNode {.inline.} =
result = newJObject()
result["chainID"] = %(t.chainID)
result["contractAddress"] = %(t.contractAddress)
result["tokenID"] = %(t.tokenID.toString())
proc `%`*(t: ref CollectibleUniqueID): JsonNode {.inline.} =
return %(t[])
proc fromJson*(t: JsonNode, T: typedesc[CollectibleUniqueID]): CollectibleUniqueID {.inline.} =
result = CollectibleUniqueID()
result.chainID = t["chainID"].getInt()
result.contractAddress = t["contractAddress"].getStr()
result.tokenID = stint.parse(t["tokenID"].getStr(), UInt256)
proc fromJson*(t: JsonNode, T: typedesc[ref CollectibleUniqueID]): ref CollectibleUniqueID {.inline.} =
result = new(CollectibleUniqueID)
result[] = fromJson(t, CollectibleUniqueID)
# CollectibleHeader
proc `$`*(self: CollectibleHeader): string =
return fmt"""CollectibleHeader(
id:{self.id},
name:{self.name},
imageUrl:{self.imageUrl},
animationUrl:{self.animationUrl},
animationMediaType:{self.animationMediaType},
backgroundColor:{self.backgroundColor},
collectionName:{self.collectionName}
)"""
proc fromJson*(t: JsonNode, T: typedesc[CollectibleHeader]): CollectibleHeader {.inline.} =
result = CollectibleHeader()
result.id = fromJson(t["id"], CollectibleUniqueID)
result.name = t["name"].getStr()
result.imageUrl = t["image_url"].getStr()
result.animationUrl = t["animation_url"].getStr()
result.animationMediaType = t["animation_media_type"].getStr()
result.backgroundColor = t["background_color"].getStr()
result.collectionName = t["collection_name"].getStr()
# CollectibleTrait
proc `$`*(self: CollectibleTrait): string =
return fmt"""CollectibleTrait(
trait_type:{self.trait_type},
value:{self.value},
display_type:{self.display_type},
max_value:{self.max_value}
)"""
proc fromJson*(t: JsonNode, T: typedesc[CollectibleTrait]): CollectibleTrait {.inline.} =
result = CollectibleTrait()
result.trait_type = t["trait_type"].getStr()
result.value = t["value"].getStr()
result.display_type = t["display_type"].getStr()
result.max_value = t["max_value"].getStr()
proc fromJson*(t: JsonNode, T: typedesc[ref CollectibleTrait]): ref CollectibleTrait {.inline.} =
result = new(CollectibleTrait)
result[] = fromJson(t, CollectibleTrait)
# CollectionTrait
proc `$`*(self: CollectionTrait): string =
return fmt"""CollectionTrait(
min:{self.min},
max:{self.max}
)"""
proc fromJson*(t: JsonNode, T: typedesc[CollectionTrait]): CollectionTrait {.inline.} =
result = CollectionTrait()
result.min = t["min"].getFloat()
result.max = t["max"].getFloat()
proc fromJson*(t: JsonNode, T: typedesc[ref CollectionTrait]): ref CollectionTrait {.inline.} =
result = new(CollectionTrait)
result[] = fromJson(t, CollectionTrait)
# CollectionData
proc `$`*(self: CollectionData): string =
return fmt"""CollectionData(
name:{self.name},
slug:{self.slug},
imageUrl:{self.imageUrl},
traits:{self.traits}
)"""
proc getCollectionTraits*(t: JsonNode): Table[string, CollectionTrait] =
var traitList: Table[string, CollectionTrait] = initTable[string, CollectionTrait]()
for key, value in t{"traits"}.getFields():
traitList[key] = fromJson(value, CollectionTrait)
return traitList
proc fromJson*(t: JsonNode, T: typedesc[CollectionData]): CollectionData {.inline.} =
result = CollectionData()
result.name = t["name"].getStr()
result.slug = t["slug"].getStr()
result.imageUrl = t["image_url"].getStr()
result.traits = getCollectionTraits(t["traits"])
proc fromJson*(t: JsonNode, T: typedesc[ref CollectionData]): ref CollectionData {.inline.} =
result = new(CollectionData)
result[] = fromJson(t, CollectionData)
# CollectibleData
proc `$`*(self: CollectibleData): string =
return fmt"""CollectibleData(
id:{self.id},
name:{self.name},
description:{self.description},
permalink:{self.permalink},
imageUrl:{self.imageUrl},
animationUrl:{self.animationUrl},
animationMediaType:{self.animationMediaType},
traits:{self.traits},
backgroundColor:{self.backgroundColor},
tokenUri:{self.tokenUri},
collectionData:{self.collectionData}
)"""
proc getCollectibleTraits*(t: JsonNode): seq[CollectibleTrait] =
var traitList: seq[CollectibleTrait] = @[]
for item in t.getElems():
traitList.add(fromJson(item, CollectibleTrait))
return traitList
proc fromJson*(t: JsonNode, T: typedesc[CollectibleData]): CollectibleData {.inline.} =
result = CollectibleData()
result.id = fromJson(t["id"], CollectibleUniqueID)
result.name = t["name"].getStr()
result.description = t["description"].getStr()
result.permalink = t["permalink"].getStr()
result.imageUrl = t["image_url"].getStr()
result.animationUrl = t["animation_url"].getStr()
result.animationMediaType = t["animation_media_type"].getStr()
result.traits = getCollectibleTraits(t["traits"])
result.backgroundColor = t["background_color"].getStr()
result.tokenUri = t["token_uri"].getStr()
result.collectionData = fromJson(t["collection_data"], CollectionData)
proc fromJson*(t: JsonNode, T: typedesc[ref CollectibleData]): ref CollectibleData {.inline.} =
result = new(CollectibleData)
result[] = fromJson(t, CollectibleData)
# CollectibleBalance
proc `$`*(self: CollectibleBalance): string =
return fmt"""CollectibleBalance(
tokenId:{self.tokenId},
balance:{self.balance}
"""
proc getCollectibleBalances(jsonAsset: JsonNode): seq[CollectibleBalance] =
var balanceList: seq[CollectibleBalance] = @[]
for item in jsonAsset.items:
balanceList.add(CollectibleBalance(
tokenId: stint.parse(item{"tokenId"}.getStr, Uint256),
balance: stint.parse(item{"balance"}.getStr, Uint256)
))
return balanceList
# CollectibleOwner
proc `$`*(self: CollectibleOwner): string =
return fmt"""CollectibleOwner(
address:{self.address},
balances:{self.balances}
"""
proc getCollectibleOwners(jsonAsset: JsonNode): seq[CollectibleOwner] =
var ownerList: seq[CollectibleOwner] = @[]
for item in jsonAsset.items:
ownerList.add(CollectibleOwner(
address: item{"ownerAddress"}.getStr,
balances: getCollectibleBalances(item{"tokenBalances"})
))
return ownerList
# CollectibleContractOwnership
proc `$`*(self: CollectibleContractOwnership): string =
return fmt"""CollectibleContractOwnership(
contractAddress:{self.contractAddress},
owners:{self.owners}
"""
proc fromJson*(t: JsonNode, T: typedesc[CollectibleContractOwnership]): CollectibleContractOwnership {.inline.} =
return CollectibleContractOwnership(
contractAddress: t{"contractAddress"}.getStr,
owners: getCollectibleOwners(t{"owners"})
)

View File

@ -228,10 +228,6 @@ SplitView {
logs.logEvent("walletStore::setFilterAddress", ["address"], arguments)
}
function selectCollectible(slug, id) {
logs.logEvent("walletStore::selectCollectible", ["slug", "id"], arguments)
}
readonly property var accounts: ListModel {
ListElement {
name: "My Status Account"
@ -338,7 +334,7 @@ SplitView {
Component.onCompleted: append(data)
}
readonly property var flatCollectibles: ListModel {
readonly property var collectibles: ListModel {
readonly property var data: [
{
//id: 123,

View File

@ -28,63 +28,52 @@ SplitView {
id: collectiblesModel
readonly property var data: [
{
id: 123,
uid: "123",
name: "SNT",
description: "",
collectionName: "Super Nitro Toluen (with pink bg)",
backgroundColor: "pink",
imageUrl: ModelsData.collectibles.custom,
permalink: "green",
isLoading: false
},
{
id: 34545656768,
uid: "34545656768",
name: "Kitty 1",
description: "",
collectionName: "Kitties",
backgroundColor: "",
imageUrl: ModelsData.collectibles.kitty1Big,
permalink: "",
isLoading: false
},
{
id: 123456,
uid: "123456",
name: "Kitty 2",
description: "",
collectionName: "",
backgroundColor: "",
imageUrl: ModelsData.collectibles.kitty2Big,
permalink: "",
isLoading: false
},
{
id: 12345645459537432,
uid: "12345645459537432",
name: "",
description: "Kitty 3 description",
collectionName: "Super Kitties",
backgroundColor: "oink",
imageUrl: ModelsData.collectibles.kitty3Big,
permalink: "",
isLoading: false
},
{
id: 691,
uid: "691",
name: "KILLABEAR",
description: "Please note that weapons are not yet reflected in the rarity stats.",
collectionName: "KILLABEARS",
backgroundColor: "#807c56",
imageUrl: "https://assets.killabears.com/content/killabears/img/691-e81f892696a8ae700e0dbc62eb072060679a2046d1ef5eb2671bdb1fad1f68e3.png",
permalink: "https://opensea.io/assets/ethereum/0xc99c679c50033bbc5321eb88752e89a93e9e83c5/691",
isLoading: true
},
{
id: 8876,
uid: "8876",
name: "AIORBIT",
description: "",
collectionName: "AIORBIT (Animated SVG)",
backgroundColor: "",
imageUrl: "https://dl.openseauserdata.com/cache/originImage/files/8b14ef530b28853445c27d6693c4e805.svg",
permalink: "https://opensea.io/assets/ethereum/0xba66a7c5e1f89a542e3108e3df155a9bf41ac824/8876",
isLoading: false
}
]

View File

@ -8,9 +8,9 @@ ProfileShowcasePanel {
id: root
settingsKey: "collectibles"
keyRole: "id"
roleNames: ["id", "name", "description", "collectionName", "backgroundColor", "imageUrl"]
filterFunc: (modelData) => !showcaseModel.hasItem(modelData.id)
keyRole: "uid"
roleNames: ["uid", "name", "collectionName", "backgroundColor", "imageUrl"]
filterFunc: (modelData) => !showcaseModel.hasItem(modelData.uid)
hiddenPlaceholderBanner: qsTr("Collectibles here will show on your profile")
showcasePlaceholderBanner: qsTr("Collectibles here will be hidden from your profile")

View File

@ -20,8 +20,8 @@ QtObject {
// TODO(alaibe): there should be no access to wallet section, create collectible in profile
property var overview: walletSectionOverview
property var flatCollectibles: Global.appIsReady ? walletSectionCollectibles.model : null
property var assets: walletSectionAssets.assets
property var collectibles: Global.appIsReady ? walletSection.collectiblesController.model : null // To-do: Fetch profile collectibles separately
property var accounts: Global.appIsReady? accountsModule.accounts : null
property var originModel: accountsModule.keyPairModel
property bool includeWatchOnlyAccount: accountsModule.includeWatchOnlyAccount

View File

@ -211,7 +211,7 @@ ColumnLayout {
ProfileShowcaseCollectiblesPanel {
Layout.minimumHeight: implicitHeight
Layout.maximumHeight: implicitHeight
baseModel: root.walletStore.flatCollectibles
baseModel: root.walletStore.collectibles
}
ProfileShowcaseAssetsPanel {

View File

@ -156,7 +156,8 @@ QtObject {
}
// Collectibles Filters
property var collectiblesList: walletSectionCollectibles.model
// To-do: Get list of collectibles with activity from backend
property var collectiblesList: walletSection.collectiblesController.model
property var collectiblesFilter: []
function toggleCollectibles(id) {
// update filters

View File

@ -0,0 +1,23 @@
import QtQuick 2.12
import utils 1.0
QtObject {
id: root
readonly property var ownedCollectibles: Global.appIsReady ? walletSection.collectiblesController.model : null
readonly property var detailedCollectible: Global.appIsReady ? walletSection.collectibleDetailsController.detailedEntry : null
readonly property var detailedCollectibleStatus: Global.appIsReady ? walletSection.collectibleDetailsController.status : null
readonly property bool isDetailedCollectibleLoading: Global.appIsReady ? walletSection.collectibleDetailsController.isDetailedEntryLoading : true
function fetchMoreCollectibles() {
if (!root.ownedCollectibles.hasMore
|| root.ownedCollectibes.isFetching)
return
walletSection.collectiblesController.loadMoreItems()
}
function getDetailedCollectible(chainId, contractAddress, tokenId) {
walletSection.collectibleDetailsController.getDetailedCollectible(chainId, contractAddress, tokenId)
}
}

View File

@ -29,8 +29,7 @@ QtObject {
property string signingPhrase: walletSection.signingPhrase
property string mnemonicBackedUp: walletSection.isMnemonicBackedUp
property var flatCollectibles: walletSectionCollectibles.model
property var currentCollectible: walletSectionCurrentCollectible
property CollectiblesStore collectiblesStore: CollectiblesStore {}
property var areTestNetworksEnabled: networksModule.areTestNetworksEnabled
@ -175,19 +174,6 @@ QtObject {
return globalUtils.hex2Dec(value)
}
function getCollectionMaxValue(traitType, value, maxValue, collectionIndex) {
// Not Refactored Yet
// if(maxValue !== "")
// return parseInt(value) + qsTr(" of ") + maxValue;
// else
// return parseInt(value) + qsTr(" of ") +
// walletModelV2Inst.collectiblesView.collections.getCollectionTraitMaxValue(collectionIndex, traitType).toString();
}
function selectCollectible(address, tokenId) {
walletSectionCurrentCollectible.update(address, tokenId)
}
function getNameForSavedWalletAddress(address) {
return walletSectionSavedAddresses.getNameByAddress(address)
}

View File

@ -1,2 +1,3 @@
singleton RootStore 1.0 RootStore.qml
ActivityFiltersStore 1.0 ActivityFiltersStore.qml
CollectiblesStore 1.0 CollectiblesStore.qml

View File

@ -16,7 +16,7 @@ Item {
property var collectiblesModel
width: parent.width
signal collectibleClicked(string address, string tokenId)
signal collectibleClicked(int chainId, string contractAddress, string tokenId)
Loader {
id: contentLoader
@ -24,8 +24,10 @@ Item {
height: parent.height
sourceComponent: {
if (root.collectiblesModel.allCollectiblesLoaded && root.collectiblesModel.count === 0)
/* TODO: Issue #11635
if (!root.collectiblesModel.hasMore && root.collectiblesModel.count === 0)
return empty;
*/
return loaded;
}
}
@ -62,7 +64,7 @@ Item {
backgroundColor: model.backgroundColor ? model.backgroundColor : "transparent"
isLoading: model.isLoading
onClicked: root.collectibleClicked(model.address, model.tokenId)
onClicked: root.collectibleClicked(model.chainId, model.contractAddress, model.tokenId)
}
ScrollBar.vertical: StatusScrollBar {}

View File

@ -115,9 +115,9 @@ Item {
}
}
CollectiblesView {
collectiblesModel: RootStore.flatCollectibles
collectiblesModel: RootStore.collectiblesStore.ownedCollectibles
onCollectibleClicked: {
RootStore.selectCollectible(address, tokenId)
RootStore.collectiblesStore.getDetailedCollectible(chainId, contractAddress, tokenId)
stack.currentIndex = 1
}
}
@ -134,6 +134,8 @@ Item {
CollectibleDetailView {
Layout.fillWidth: true
Layout.fillHeight: true
collectible: RootStore.collectiblesStore.detailedCollectible
isCollectibleLoading: RootStore.collectiblesStore.isDetailedCollectibleLoading
}
AssetsDetailView {
id: assetDetailView

View File

@ -16,7 +16,8 @@ import "../../controls"
Item {
id: root
property var currentCollectible: RootStore.currentCollectible
property var collectible
property bool isCollectibleLoading
readonly property int isNarrowMode : width < 700
CollectibleDetailsHeader {
@ -24,14 +25,14 @@ Item {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
asset.name: currentCollectible.collectionImageUrl
asset.name: collectible.collectionImageUrl
asset.isImage: true
primaryText: currentCollectible.collectionName
secondaryText: "#" + currentCollectible.tokenId
primaryText: collectible.collectionName
secondaryText: "#" + collectible.tokenId
isNarrowMode: root.isNarrowMode
networkShortName: currentCollectible.networkShortName
networkColor: currentCollectible.networkColor
networkIconURL: currentCollectible.networkIconUrl
networkShortName: collectible.networkShortName
networkColor: collectible.networkColor
networkIconURL: collectible.networkIconUrl
}
ColumnLayout {
@ -57,12 +58,12 @@ Item {
width: size
height: size
radius: 2
color: currentCollectible.backgroundColor
color: collectible.backgroundColor
border.color: Theme.palette.directColor8
border.width: 1
mediaUrl: currentCollectible.mediaUrl
mediaType: currentCollectible.mediaType
fallbackImageUrl: currentCollectible.imageUrl
mediaUrl: collectible.mediaUrl
mediaType: collectible.mediaType
fallbackImageUrl: collectible.imageUrl
}
Column {
@ -76,7 +77,7 @@ Item {
width: parent.width
height: 24
text: currentCollectible.name
text: collectible.name
color: Theme.palette.directColor1
font.pixelSize: 17
lineHeight: 24
@ -97,7 +98,7 @@ Item {
id: descriptionText
width: descriptionScrollView.availableWidth
text: currentCollectible.description
text: collectible.description
textFormat: Text.MarkdownText
color: Theme.palette.directColor4
font.pixelSize: 15
@ -114,7 +115,7 @@ Item {
id: collectiblesDetailsTab
Layout.fillWidth: true
Layout.topMargin: root.isNarrowMode ? 0 : Style.current.xlPadding
visible: currentCollectible.properties.count > 0
visible: collectible.traits.count > 0
StatusTabButton {
leftPadding: 0
@ -132,7 +133,7 @@ Item {
width: scrollView.availableWidth
spacing: 10
Repeater {
model: currentCollectible.properties
model: collectible.traits
InformationTile {
maxWidth: parent.width
primaryText: model.traitType

View File

@ -13,7 +13,7 @@ QtObject {
readonly property bool balanceCache: walletSectionAssets.hasBalanceCache
readonly property bool marketValuesCache: walletSectionAssets.hasMarketValuesCache
readonly property bool collectiblesCache: walletSectionCollectibles.getHasCollectiblesCache()
readonly property bool collectiblesCache: false // TODO: Issue #11636
readonly property var blockchainNetworksDown: !!networkConnectionModule.blockchainNetworkConnection.chainIds ? networkConnectionModule.blockchainNetworkConnection.chainIds.split(";") : []
readonly property bool atleastOneBlockchainNetworkAvailable: blockchainNetworksDown.length < networksModule.all.count

View File

@ -247,7 +247,8 @@ Control {
cellWidth: (width-rightMargin)/4
cellHeight: cellWidth
visible: count
model: root.isCurrentUser ? root.walletStore.flatCollectibles : null // TODO show other users too
// TODO Issue #11637: Dedicated controller for user's list of collectibles (no watch-only entries)
model: root.isCurrentUser ? root.walletStore.ownedCollectibles : null
ScrollBar.vertical: StatusScrollBar { }
delegate: StatusRoundedImage {
width: GridView.view.cellWidth - Style.current.smallPadding

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit 3d1b1bab572eeedb7028abf2486dec3d9fe04858
Subproject commit 10a42e639d6c455e2a14a89006c8d653e9d1d441