parent
43a5d7eeeb
commit
dc75c120df
|
@ -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()
|
|
@ -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)
|
||||||
|
)
|
||||||
|
|
|
@ -1,81 +1,108 @@
|
||||||
import sequtils, Tables, sugar
|
import NimQml, std/json, sequtils, sugar, strutils
|
||||||
import io_interface
|
import stint, logging
|
||||||
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
|
|
||||||
|
|
||||||
type
|
import app/modules/shared_models/collectibles_model
|
||||||
Controller* = ref object of RootObj
|
import app/modules/shared_models/collectibles_utils
|
||||||
delegate: io_interface.AccessInterface
|
import events_handler
|
||||||
events: EventEmitter
|
|
||||||
collectibleService: collectible_service.Service
|
|
||||||
walletAccountService: wallet_account_service.Service
|
|
||||||
networkService: network_service.Service
|
|
||||||
nodeService: node_service.Service
|
|
||||||
networkConnectionService: network_connection_service.Service
|
|
||||||
|
|
||||||
proc newController*(
|
import app/core/eventemitter
|
||||||
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
|
|
||||||
|
|
||||||
proc delete*(self: Controller) =
|
import backend/collectibles as backend_collectibles
|
||||||
discard
|
|
||||||
|
|
||||||
proc updateCollectibles(self: Controller, chainId: int, address: string) =
|
const FETCH_BATCH_COUNT_DEFAULT = 50
|
||||||
let data = self.collectibleService.getOwnedCollectibles(chainId, @[address])
|
|
||||||
self.delegate.appendCollectibles(chainId, address, data[0])
|
|
||||||
|
|
||||||
proc init*(self: Controller) =
|
QtObject:
|
||||||
self.events.on(SIGNAL_OWNED_COLLECTIBLES_REFETCH) do(e:Args):
|
type
|
||||||
let args = OwnedCollectiblesUpdateArgs(e)
|
Controller* = ref object of QObject
|
||||||
self.delegate.resetCollectibles()
|
model: Model
|
||||||
|
|
||||||
self.events.on(SIGNAL_OWNED_COLLECTIBLES_UPDATE_ERROR) do(e:Args):
|
eventsHandler: EventsHandler
|
||||||
let args = OwnedCollectiblesUpdateArgs(e)
|
|
||||||
self.updateCollectibles(args.chainId, args.address)
|
|
||||||
|
|
||||||
self.events.on(SIGNAL_OWNED_COLLECTIBLES_UPDATE_STARTED) do(e:Args):
|
addresses: seq[string]
|
||||||
let args = OwnedCollectiblesUpdateArgs(e)
|
chainIds: seq[int]
|
||||||
self.delegate.onFetchStarted(args.chainId, args.address)
|
|
||||||
|
|
||||||
self.events.on(SIGNAL_OWNED_COLLECTIBLES_UPDATE_FINISHED) do(e:Args):
|
proc setup(self: Controller) =
|
||||||
let args = OwnedCollectiblesUpdateArgs(e)
|
self.QObject.setup
|
||||||
self.updateCollectibles(args.chainId, args.address)
|
|
||||||
|
|
||||||
self.events.on(SIGNAL_REFRESH_COLLECTIBLES) do(e:Args):
|
proc delete*(self: Controller) =
|
||||||
self.collectibleService.refetchAllOwnedCollectibles()
|
self.QObject.delete
|
||||||
|
|
||||||
proc getNetwork*(self: Controller): network_service.NetworkDto =
|
proc getModel*(self: Controller): QVariant {.slot.} =
|
||||||
return self.networkService.getNetworkForCollectibles()
|
return newQVariant(self.model)
|
||||||
|
|
||||||
proc getOwnedCollectibles*(self: Controller, chainId: int, addresses: seq[string]): seq[CollectiblesData] =
|
QtProperty[QVariant] model:
|
||||||
return self.collectibleService.getOwnedCollectibles(chainId, addresses)
|
read = getModel
|
||||||
|
|
||||||
proc fetchOwnedCollectibles*(self: Controller, chainId: int, addresses: seq[string]) =
|
proc processFilterOwnedCollectiblesResponse(self: Controller, response: JsonNode) =
|
||||||
self.collectibleService.fetchOwnedCollectibles(chainId, addresses)
|
defer: self.model.setIsFetching(false)
|
||||||
|
|
||||||
proc getCollectible*(self: Controller, chainId: int, id: UniqueID) : CollectibleDto =
|
let res = fromJson(response, backend_collectibles.FilterOwnedCollectiblesResponse)
|
||||||
self.collectibleService.getCollectible(chainId, id)
|
|
||||||
|
|
||||||
proc getCollection*(self: Controller, chainId: int, slug: string) : CollectionDto =
|
let isError = res.errorCode != ErrorCodeSuccess
|
||||||
self.collectibleService.getCollection(chainId, slug)
|
defer: self.model.setIsError(isError)
|
||||||
|
|
||||||
proc getHasCollectiblesCache*(self: Controller, address: string): bool =
|
if isError:
|
||||||
return self.collectibleService.areCollectionsLoaded(address)
|
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()
|
|
@ -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)
|
|
|
@ -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")
|
|
|
@ -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)
|
|
|
@ -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()
|
|
|
@ -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)
|
||||||
|
)
|
||||||
|
|
|
@ -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")
|
|
|
@ -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
|
|
|
@ -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
|
|
||||||
)
|
|
|
@ -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])
|
|
|
@ -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()
|
|
|
@ -6,7 +6,6 @@ import ../io_interface as delegate_interface
|
||||||
|
|
||||||
import ./accounts/module as accounts_module
|
import ./accounts/module as accounts_module
|
||||||
import ./all_tokens/module as all_tokens_module
|
import ./all_tokens/module as all_tokens_module
|
||||||
import ./collectibles/module as collectibles_module
|
|
||||||
import ./assets/module as assets_module
|
import ./assets/module as assets_module
|
||||||
import ./transactions/module as transactions_module
|
import ./transactions/module as transactions_module
|
||||||
import ./saved_addresses/module as saved_addresses_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 ../../shared_modules/add_account/module as add_account_module
|
||||||
|
|
||||||
import ./activity/controller as activityc
|
import ./activity/controller as activityc
|
||||||
|
import ./collectibles/controller as collectiblesc
|
||||||
|
import ./collectible_details/controller as collectible_detailsc
|
||||||
|
|
||||||
import ../../../global/global_singleton
|
import ../../../global/global_singleton
|
||||||
import ../../../core/eventemitter
|
import ../../../core/eventemitter
|
||||||
|
@ -49,7 +50,6 @@ type
|
||||||
|
|
||||||
accountsModule: accounts_module.AccessInterface
|
accountsModule: accounts_module.AccessInterface
|
||||||
allTokensModule: all_tokens_module.AccessInterface
|
allTokensModule: all_tokens_module.AccessInterface
|
||||||
collectiblesModule: collectibles_module.AccessInterface
|
|
||||||
assetsModule: assets_module.AccessInterface
|
assetsModule: assets_module.AccessInterface
|
||||||
sendModule: send_module.AccessInterface
|
sendModule: send_module.AccessInterface
|
||||||
transactionsModule: transactions_module.AccessInterface
|
transactionsModule: transactions_module.AccessInterface
|
||||||
|
@ -64,6 +64,8 @@ type
|
||||||
walletAccountService: wallet_account_service.Service
|
walletAccountService: wallet_account_service.Service
|
||||||
|
|
||||||
activityController: activityc.Controller
|
activityController: activityc.Controller
|
||||||
|
collectiblesController: collectiblesc.Controller
|
||||||
|
collectibleDetailsController: collectible_detailsc.Controller
|
||||||
|
|
||||||
proc newModule*(
|
proc newModule*(
|
||||||
delegate: delegate_interface.AccessInterface,
|
delegate: delegate_interface.AccessInterface,
|
||||||
|
@ -92,7 +94,6 @@ proc newModule*(
|
||||||
|
|
||||||
result.accountsModule = accounts_module.newModule(result, events, walletAccountService, networkService, currencyService)
|
result.accountsModule = accounts_module.newModule(result, events, walletAccountService, networkService, currencyService)
|
||||||
result.allTokensModule = all_tokens_module.newModule(result, events, tokenService, walletAccountService)
|
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.assetsModule = assets_module.newModule(result, events, walletAccountService, networkService, tokenService, currencyService)
|
||||||
result.transactionsModule = transactions_module.newModule(result, events, transactionService, walletAccountService, networkService, currencyService)
|
result.transactionsModule = transactions_module.newModule(result, events, transactionService, walletAccountService, networkService, currencyService)
|
||||||
result.sendModule = send_module.newModule(result, events, walletAccountService, networkService, currencyService, transactionService)
|
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.networksModule = networks_module.newModule(result, events, networkService, walletAccountService, settingsService)
|
||||||
result.networksService = networkService
|
result.networksService = networkService
|
||||||
result.activityController = activityc.newController(result.transactionsModule, currencyService, tokenService, events)
|
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.filter = initFilter(result.controller)
|
||||||
|
|
||||||
result.view = newView(result, result.activityController)
|
result.view = newView(result, result.activityController, result.collectiblesController, result.collectibleDetailsController)
|
||||||
|
|
||||||
|
|
||||||
method delete*(self: Module) =
|
method delete*(self: Module) =
|
||||||
self.accountsModule.delete
|
self.accountsModule.delete
|
||||||
self.allTokensModule.delete
|
self.allTokensModule.delete
|
||||||
self.collectiblesModule.delete
|
|
||||||
self.assetsModule.delete
|
self.assetsModule.delete
|
||||||
self.transactionsModule.delete
|
self.transactionsModule.delete
|
||||||
self.savedAddressesModule.delete
|
self.savedAddressesModule.delete
|
||||||
|
@ -119,6 +121,8 @@ method delete*(self: Module) =
|
||||||
self.controller.delete
|
self.controller.delete
|
||||||
self.view.delete
|
self.view.delete
|
||||||
self.activityController.delete
|
self.activityController.delete
|
||||||
|
self.collectiblesController.delete
|
||||||
|
self.collectibleDetailsController.delete
|
||||||
|
|
||||||
if not self.addAccountModule.isNil:
|
if not self.addAccountModule.isNil:
|
||||||
self.addAccountModule.delete
|
self.addAccountModule.delete
|
||||||
|
@ -143,11 +147,11 @@ method notifyFilterChanged(self: Module) =
|
||||||
let includeWatchOnly = self.controller.isIncludeWatchOnlyAccount()
|
let includeWatchOnly = self.controller.isIncludeWatchOnlyAccount()
|
||||||
self.overviewModule.filterChanged(self.filter.addresses, self.filter.chainIds, includeWatchOnly, self.filter.allAddresses)
|
self.overviewModule.filterChanged(self.filter.addresses, self.filter.chainIds, includeWatchOnly, self.filter.allAddresses)
|
||||||
self.assetsModule.filterChanged(self.filter.addresses, self.filter.chainIds)
|
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.transactionsModule.filterChanged(self.filter.addresses, self.filter.chainIds)
|
||||||
self.accountsModule.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.sendModule.filterChanged(self.filter.addresses, self.filter.chainIds)
|
||||||
self.activityController.globalFilterChanged(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:
|
if self.filter.addresses.len > 0:
|
||||||
self.view.filterChanged(self.filter.addresses[0], includeWatchOnly, self.filter.allAddresses)
|
self.view.filterChanged(self.filter.addresses[0], includeWatchOnly, self.filter.allAddresses)
|
||||||
|
|
||||||
|
@ -214,7 +218,6 @@ method load*(self: Module) =
|
||||||
self.view.load()
|
self.view.load()
|
||||||
self.accountsModule.load()
|
self.accountsModule.load()
|
||||||
self.allTokensModule.load()
|
self.allTokensModule.load()
|
||||||
self.collectiblesModule.load()
|
|
||||||
self.assetsModule.load()
|
self.assetsModule.load()
|
||||||
self.transactionsModule.load()
|
self.transactionsModule.load()
|
||||||
self.savedAddressesModule.load()
|
self.savedAddressesModule.load()
|
||||||
|
@ -233,9 +236,6 @@ proc checkIfModuleDidLoad(self: Module) =
|
||||||
if(not self.allTokensModule.isLoaded()):
|
if(not self.allTokensModule.isLoaded()):
|
||||||
return
|
return
|
||||||
|
|
||||||
if(not self.collectiblesModule.isLoaded()):
|
|
||||||
return
|
|
||||||
|
|
||||||
if(not self.assetsModule.isLoaded()):
|
if(not self.assetsModule.isLoaded()):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import NimQml, json
|
import NimQml, json
|
||||||
|
|
||||||
import ./activity/controller as activityc
|
import ./activity/controller as activityc
|
||||||
|
import ./collectibles/controller as collectiblesc
|
||||||
|
import ./collectible_details/controller as collectible_detailsc
|
||||||
import ./io_interface
|
import ./io_interface
|
||||||
import ../../shared_models/currency_amount
|
import ../../shared_models/currency_amount
|
||||||
|
|
||||||
|
@ -14,6 +16,8 @@ QtObject:
|
||||||
tmpAmount: float # shouldn't be used anywhere except in prepare*/getPrepared* procs
|
tmpAmount: float # shouldn't be used anywhere except in prepare*/getPrepared* procs
|
||||||
tmpSymbol: string # 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
|
activityController: activityc.Controller
|
||||||
|
collectiblesController: collectiblesc.Controller
|
||||||
|
collectibleDetailsController: collectible_detailsc.Controller
|
||||||
|
|
||||||
proc setup(self: View) =
|
proc setup(self: View) =
|
||||||
self.QObject.setup
|
self.QObject.setup
|
||||||
|
@ -21,10 +25,12 @@ QtObject:
|
||||||
proc delete*(self: View) =
|
proc delete*(self: View) =
|
||||||
self.QObject.delete
|
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)
|
new(result, delete)
|
||||||
result.delegate = delegate
|
result.delegate = delegate
|
||||||
result.activityController = activityController
|
result.activityController = activityController
|
||||||
|
result.collectiblesController = collectiblesController
|
||||||
|
result.collectibleDetailsController = collectibleDetailsController
|
||||||
result.setup()
|
result.setup()
|
||||||
|
|
||||||
proc load*(self: View) =
|
proc load*(self: View) =
|
||||||
|
@ -119,3 +125,13 @@ QtObject:
|
||||||
return newQVariant(self.activityController)
|
return newQVariant(self.activityController)
|
||||||
QtProperty[QVariant] activityController:
|
QtProperty[QVariant] activityController:
|
||||||
read = getActivityController
|
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
|
||||||
|
|
|
@ -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
|
|
@ -1,6 +1,6 @@
|
||||||
import NimQml, Tables, strutils, strformat
|
import NimQml, Tables, strutils, strformat
|
||||||
|
|
||||||
import ./collectible_trait_item
|
import backend/collectibles as backend
|
||||||
|
|
||||||
type
|
type
|
||||||
ModelRole {.pure.} = enum
|
ModelRole {.pure.} = enum
|
||||||
|
@ -12,7 +12,7 @@ type
|
||||||
QtObject:
|
QtObject:
|
||||||
type
|
type
|
||||||
TraitModel* = ref object of QAbstractListModel
|
TraitModel* = ref object of QAbstractListModel
|
||||||
items: seq[CollectibleTrait]
|
items: seq[backend.CollectibleTrait]
|
||||||
|
|
||||||
proc delete(self: TraitModel) =
|
proc delete(self: TraitModel) =
|
||||||
self.items = @[]
|
self.items = @[]
|
||||||
|
@ -61,13 +61,13 @@ QtObject:
|
||||||
|
|
||||||
case enumRole:
|
case enumRole:
|
||||||
of ModelRole.TraitType:
|
of ModelRole.TraitType:
|
||||||
result = newQVariant(item.getTraitType())
|
result = newQVariant(item.trait_type)
|
||||||
of ModelRole.Value:
|
of ModelRole.Value:
|
||||||
result = newQVariant(item.getValue())
|
result = newQVariant(item.value)
|
||||||
of ModelRole.DisplayType:
|
of ModelRole.DisplayType:
|
||||||
result = newQVariant(item.getDisplayType())
|
result = newQVariant(item.display_type)
|
||||||
of ModelRole.MaxValue:
|
of ModelRole.MaxValue:
|
||||||
result = newQVariant(item.getMaxValue())
|
result = newQVariant(item.max_value)
|
||||||
|
|
||||||
proc setItems*(self: TraitModel, items: seq[CollectibleTrait]) =
|
proc setItems*(self: TraitModel, items: seq[CollectibleTrait]) =
|
||||||
self.beginResetModel()
|
self.beginResetModel()
|
|
@ -1,67 +1,45 @@
|
||||||
import strformat, stint
|
import strformat, stint
|
||||||
import ./collectible_trait_item
|
|
||||||
|
|
||||||
type
|
type
|
||||||
Item* = object
|
Item* = object
|
||||||
id: int
|
chainId: int
|
||||||
address: string
|
contractAddress: string
|
||||||
tokenId: UInt256
|
tokenId: UInt256
|
||||||
name: string
|
name: string
|
||||||
mediaUrl: string
|
mediaUrl: string
|
||||||
mediaType: string
|
mediaType: string
|
||||||
imageUrl: string
|
imageUrl: string
|
||||||
backgroundColor: string
|
backgroundColor: string
|
||||||
description: string
|
|
||||||
permalink: string
|
|
||||||
properties: seq[CollectibleTrait]
|
|
||||||
rankings: seq[CollectibleTrait]
|
|
||||||
stats: seq[CollectibleTrait]
|
|
||||||
collectionName: string
|
collectionName: string
|
||||||
collectionSlug: string
|
|
||||||
collectionImageUrl: string
|
|
||||||
isLoading: bool
|
isLoading: bool
|
||||||
isPinned: bool
|
isPinned: bool
|
||||||
|
|
||||||
proc initItem*(
|
proc initItem*(
|
||||||
id: int,
|
chainId: int,
|
||||||
address: string,
|
contractAddress: string,
|
||||||
tokenId: UInt256,
|
tokenId: UInt256,
|
||||||
name: string,
|
name: string,
|
||||||
mediaUrl: string,
|
mediaUrl: string,
|
||||||
mediaType: string,
|
mediaType: string,
|
||||||
imageUrl: string,
|
imageUrl: string,
|
||||||
backgroundColor: string,
|
backgroundColor: string,
|
||||||
description: string,
|
|
||||||
permalink: string,
|
|
||||||
properties: seq[CollectibleTrait],
|
|
||||||
rankings: seq[CollectibleTrait],
|
|
||||||
stats: seq[CollectibleTrait],
|
|
||||||
collectionName: string,
|
collectionName: string,
|
||||||
collectionSlug: string,
|
|
||||||
collectionImageUrl: string,
|
|
||||||
isPinned: bool
|
isPinned: bool
|
||||||
): Item =
|
): Item =
|
||||||
result.id = id
|
result.chainId = chainId
|
||||||
result.address = address
|
result.contractAddress = contractAddress
|
||||||
result.tokenId = tokenId
|
result.tokenId = tokenId
|
||||||
result.name = if (name != ""): name else: ("#" & tokenId.toString())
|
result.name = if (name != ""): name else: ("#" & tokenId.toString())
|
||||||
result.mediaUrl = mediaUrl
|
result.mediaUrl = mediaUrl
|
||||||
result.mediaType = mediaType
|
result.mediaType = mediaType
|
||||||
result.imageUrl = imageUrl
|
result.imageUrl = imageUrl
|
||||||
result.backgroundColor = if (backgroundColor == ""): "transparent" else: ("#" & backgroundColor)
|
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.collectionName = collectionName
|
||||||
result.collectionSlug = collectionSlug
|
|
||||||
result.collectionImageUrl = collectionImageUrl
|
|
||||||
result.isLoading = false
|
result.isLoading = false
|
||||||
result.isPinned = isPinned
|
result.isPinned = isPinned
|
||||||
|
|
||||||
proc initItem*: Item =
|
proc initItem*: Item =
|
||||||
result = initItem(-1, "", u256(0), "", "", "", "", "transparent", "Collectibles", "", @[], @[], @[], "", "", "", false)
|
result = initItem(0, "", u256(0), "", "", "", "", "transparent", "Collectibles", false)
|
||||||
|
|
||||||
proc initLoadingItem*: Item =
|
proc initLoadingItem*: Item =
|
||||||
result = initItem()
|
result = initItem()
|
||||||
|
@ -69,32 +47,32 @@ proc initLoadingItem*: Item =
|
||||||
|
|
||||||
proc `$`*(self: Item): string =
|
proc `$`*(self: Item): string =
|
||||||
result = fmt"""Collectibles(
|
result = fmt"""Collectibles(
|
||||||
id: {self.id},
|
chainId: {self.chainId},
|
||||||
address: {self.address},
|
contractAddress: {self.contractAddress},
|
||||||
tokenId: {self.tokenId},
|
tokenId: {self.tokenId},
|
||||||
name: {self.name},
|
name: {self.name},
|
||||||
mediaUrl: {self.mediaUrl},
|
mediaUrl: {self.mediaUrl},
|
||||||
mediaType: {self.mediaType},
|
mediaType: {self.mediaType},
|
||||||
imageUrl: {self.imageUrl},
|
imageUrl: {self.imageUrl},
|
||||||
backgroundColor: {self.backgroundColor},
|
backgroundColor: {self.backgroundColor},
|
||||||
description: {self.description},
|
|
||||||
permalink: {self.permalink},
|
|
||||||
collectionName: {self.collectionName},
|
collectionName: {self.collectionName},
|
||||||
collectionSlug: {self.collectionSlug},
|
|
||||||
collectionImageUrl: {self.collectionImageUrl},
|
|
||||||
isLoading: {self.isLoading},
|
isLoading: {self.isLoading},
|
||||||
isPinned: {self.isPinned},
|
isPinned: {self.isPinned},
|
||||||
]"""
|
]"""
|
||||||
|
|
||||||
proc getId*(self: Item): int =
|
proc getChainId*(self: Item): int =
|
||||||
return self.id
|
return self.chainId
|
||||||
|
|
||||||
proc getAddress*(self: Item): string =
|
proc getContractAddress*(self: Item): string =
|
||||||
return self.address
|
return self.contractAddress
|
||||||
|
|
||||||
proc getTokenId*(self: Item): UInt256 =
|
proc getTokenId*(self: Item): UInt256 =
|
||||||
return self.tokenId
|
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 =
|
proc getName*(self: Item): string =
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
@ -110,30 +88,9 @@ proc getImageUrl*(self: Item): string =
|
||||||
proc getBackgroundColor*(self: Item): string =
|
proc getBackgroundColor*(self: Item): string =
|
||||||
return self.backgroundColor
|
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 =
|
proc getCollectionName*(self: Item): string =
|
||||||
return self.collectionName
|
return self.collectionName
|
||||||
|
|
||||||
proc getCollectionSlug*(self: Item): string =
|
|
||||||
return self.collectionSlug
|
|
||||||
|
|
||||||
proc getCollectionImageUrl*(self: Item): string =
|
|
||||||
return self.collectionImageUrl
|
|
||||||
|
|
||||||
proc getIsLoading*(self: Item): bool =
|
proc getIsLoading*(self: Item): bool =
|
||||||
return self.isLoading
|
return self.isLoading
|
||||||
|
|
|
@ -1,25 +1,20 @@
|
||||||
import NimQml, Tables, strutils, strformat, sequtils, stint
|
import NimQml, Tables, strutils, strformat, sequtils, stint
|
||||||
|
import logging
|
||||||
|
|
||||||
import ./collectibles_item, ./collectible_trait_model
|
import ./collectibles_item, ./collectible_trait_model
|
||||||
|
|
||||||
type
|
type
|
||||||
CollectibleRole* {.pure.} = enum
|
CollectibleRole* {.pure.} = enum
|
||||||
Id = UserRole + 1,
|
Uid = UserRole + 1,
|
||||||
Address
|
ChainId
|
||||||
|
ContractAddress
|
||||||
TokenId
|
TokenId
|
||||||
Name
|
Name
|
||||||
|
ImageUrl
|
||||||
MediaUrl
|
MediaUrl
|
||||||
MediaType
|
MediaType
|
||||||
ImageUrl
|
|
||||||
BackgroundColor
|
BackgroundColor
|
||||||
Description
|
|
||||||
Permalink
|
|
||||||
Properties
|
|
||||||
Rankings
|
|
||||||
Stats
|
|
||||||
CollectionName
|
CollectionName
|
||||||
CollectionSlug
|
|
||||||
CollectionImageUrl
|
|
||||||
IsLoading
|
IsLoading
|
||||||
IsPinned
|
IsPinned
|
||||||
|
|
||||||
|
@ -29,7 +24,7 @@ QtObject:
|
||||||
type
|
type
|
||||||
Model* = ref object of QAbstractListModel
|
Model* = ref object of QAbstractListModel
|
||||||
items: seq[Item]
|
items: seq[Item]
|
||||||
allCollectiblesLoaded: bool
|
hasMore: bool
|
||||||
isFetching: bool
|
isFetching: bool
|
||||||
isError: bool
|
isError: bool
|
||||||
loadingItemsStartIdx: int
|
loadingItemsStartIdx: int
|
||||||
|
@ -48,7 +43,7 @@ QtObject:
|
||||||
new(result, delete)
|
new(result, delete)
|
||||||
result.setup
|
result.setup
|
||||||
result.items = @[]
|
result.items = @[]
|
||||||
result.allCollectiblesLoaded = false
|
result.hasMore = true
|
||||||
result.isFetching = false
|
result.isFetching = false
|
||||||
result.isError = false
|
result.isError = false
|
||||||
result.loadingItemsStartIdx = -1
|
result.loadingItemsStartIdx = -1
|
||||||
|
@ -92,46 +87,41 @@ QtObject:
|
||||||
self.isError = value
|
self.isError = value
|
||||||
self.isErrorChanged()
|
self.isErrorChanged()
|
||||||
|
|
||||||
proc allCollectiblesLoadedChanged(self: Model) {.signal.}
|
proc hasMoreChanged*(self: Model) {.signal.}
|
||||||
proc getAllCollectiblesLoaded*(self: Model): bool {.slot.} =
|
proc getHasMore*(self: Model): bool {.slot.} =
|
||||||
self.allCollectiblesLoaded
|
self.hasMore
|
||||||
QtProperty[bool] allCollectiblesLoaded:
|
QtProperty[bool] hasMore:
|
||||||
read = getAllCollectiblesLoaded
|
read = getHasMore
|
||||||
notify = allCollectiblesLoadedChanged
|
notify = hasMoreChanged
|
||||||
proc setAllCollectiblesLoaded*(self: Model, value: bool) =
|
proc setHasMore(self: Model, hasMore: bool) {.slot.} =
|
||||||
if value == self.allCollectiblesLoaded:
|
if hasMore == self.hasMore:
|
||||||
return
|
return
|
||||||
self.allCollectiblesLoaded = value
|
self.hasMore = hasMore
|
||||||
self.allCollectiblesLoadedChanged()
|
self.hasMoreChanged()
|
||||||
|
|
||||||
method canFetchMore*(self: Model, parent: QModelIndex): bool =
|
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) =
|
method fetchMore*(self: Model, parent: QModelIndex) =
|
||||||
self.requestFetch()
|
self.loadMoreItems()
|
||||||
|
|
||||||
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] =
|
||||||
{
|
{
|
||||||
CollectibleRole.Id.int:"id",
|
CollectibleRole.Uid.int:"uid",
|
||||||
CollectibleRole.Address.int:"address",
|
CollectibleRole.ChainId.int:"chainId",
|
||||||
|
CollectibleRole.ContractAddress.int:"contractAddress",
|
||||||
CollectibleRole.TokenId.int:"tokenId",
|
CollectibleRole.TokenId.int:"tokenId",
|
||||||
CollectibleRole.Name.int:"name",
|
CollectibleRole.Name.int:"name",
|
||||||
CollectibleRole.MediaUrl.int:"mediaUrl",
|
CollectibleRole.MediaUrl.int:"mediaUrl",
|
||||||
CollectibleRole.MediaType.int:"mediaType",
|
CollectibleRole.MediaType.int:"mediaType",
|
||||||
CollectibleRole.ImageUrl.int:"imageUrl",
|
CollectibleRole.ImageUrl.int:"imageUrl",
|
||||||
CollectibleRole.BackgroundColor.int:"backgroundColor",
|
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.CollectionName.int:"collectionName",
|
||||||
CollectibleRole.CollectionSlug.int:"collectionSlug",
|
|
||||||
CollectibleRole.CollectionImageUrl.int:"collectionImageUrl",
|
|
||||||
CollectibleRole.IsLoading.int:"isLoading",
|
CollectibleRole.IsLoading.int:"isLoading",
|
||||||
CollectibleRole.IsPinned.int:"isPinned",
|
CollectibleRole.IsPinned.int:"isPinned",
|
||||||
}.toTable
|
}.toTable
|
||||||
|
@ -147,10 +137,12 @@ QtObject:
|
||||||
let enumRole = role.CollectibleRole
|
let enumRole = role.CollectibleRole
|
||||||
|
|
||||||
case enumRole:
|
case enumRole:
|
||||||
of CollectibleRole.Id:
|
of CollectibleRole.Uid:
|
||||||
result = newQVariant(item.getId())
|
result = newQVariant(item.getId())
|
||||||
of CollectibleRole.Address:
|
of CollectibleRole.ChainId:
|
||||||
result = newQVariant(item.getAddress())
|
result = newQVariant(item.getChainId())
|
||||||
|
of CollectibleRole.ContractAddress:
|
||||||
|
result = newQVariant(item.getContractAddress())
|
||||||
of CollectibleRole.TokenId:
|
of CollectibleRole.TokenId:
|
||||||
result = newQVariant(item.getTokenId().toString())
|
result = newQVariant(item.getTokenId().toString())
|
||||||
of CollectibleRole.Name:
|
of CollectibleRole.Name:
|
||||||
|
@ -163,28 +155,8 @@ QtObject:
|
||||||
result = newQVariant(item.getImageUrl())
|
result = newQVariant(item.getImageUrl())
|
||||||
of CollectibleRole.BackgroundColor:
|
of CollectibleRole.BackgroundColor:
|
||||||
result = newQVariant(item.getBackgroundColor())
|
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:
|
of CollectibleRole.CollectionName:
|
||||||
result = newQVariant(item.getCollectionName())
|
result = newQVariant(item.getCollectionName())
|
||||||
of CollectibleRole.CollectionSlug:
|
|
||||||
result = newQVariant(item.getCollectionSlug())
|
|
||||||
of CollectibleRole.CollectionImageUrl:
|
|
||||||
result = newQVariant(item.getCollectionImageUrl())
|
|
||||||
of CollectibleRole.IsLoading:
|
of CollectibleRole.IsLoading:
|
||||||
result = newQVariant(item.getIsLoading())
|
result = newQVariant(item.getIsLoading())
|
||||||
of CollectibleRole.IsPinned:
|
of CollectibleRole.IsPinned:
|
||||||
|
@ -218,24 +190,29 @@ QtObject:
|
||||||
self.endRemoveRows()
|
self.endRemoveRows()
|
||||||
self.countChanged()
|
self.countChanged()
|
||||||
|
|
||||||
proc setItems*(self: Model, items: seq[Item]) =
|
proc resetModel*(self: Model, newItems: seq[Item]) =
|
||||||
if self.isFetching:
|
|
||||||
self.removeLoadingItems()
|
|
||||||
self.beginResetModel()
|
self.beginResetModel()
|
||||||
self.items = items
|
self.items = newItems
|
||||||
self.endResetModel()
|
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:
|
if self.isFetching:
|
||||||
self.removeLoadingItems()
|
self.removeLoadingItems()
|
||||||
|
|
||||||
|
if offset == 0:
|
||||||
|
self.resetModel(newItems)
|
||||||
|
else:
|
||||||
let parentModelIndex = newQModelIndex()
|
let parentModelIndex = newQModelIndex()
|
||||||
defer: parentModelIndex.delete
|
defer: parentModelIndex.delete
|
||||||
self.beginInsertRows(parentModelIndex, self.items.len, self.items.len + items.len - 1)
|
|
||||||
self.items = concat(self.items, items)
|
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.endInsertRows()
|
||||||
self.countChanged()
|
self.countChanged()
|
||||||
|
self.setHasMore(hasMore)
|
||||||
|
|
||||||
if self.isFetching:
|
if self.isFetching:
|
||||||
self.appendLoadingItems()
|
self.appendLoadingItems()
|
|
@ -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
|
||||||
|
)
|
|
@ -64,31 +64,3 @@ const fetchOwnedCollectiblesFromContractAddressesTaskArg: Task = proc(argEncoded
|
||||||
"error": e.msg
|
"error": e.msg
|
||||||
}
|
}
|
||||||
arg.finish(output)
|
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)
|
|
||||||
|
|
|
@ -355,36 +355,6 @@ QtObject:
|
||||||
return
|
return
|
||||||
result.success = true
|
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.} =
|
proc onRxOwnedCollectibles(self: Service, response: string) {.slot.} =
|
||||||
let responseObj = response.parseJson
|
let responseObj = response.parseJson
|
||||||
let chainIdJson = responseObj["chainId"]
|
let chainIdJson = responseObj["chainId"]
|
||||||
|
|
|
@ -39,24 +39,25 @@ const loadTransactionsTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.}
|
||||||
output["allTxLoaded"] = %(response.getElems().len < arg.limit)
|
output["allTxLoaded"] = %(response.getElems().len < arg.limit)
|
||||||
|
|
||||||
# Fetch collectibles for transactions
|
# Fetch collectibles for transactions
|
||||||
var uniqueIds: seq[collectibles.NFTUniqueID] = @[]
|
var uniqueIds: seq[collectibles.CollectibleUniqueID] = @[]
|
||||||
for txJson in response.getElems():
|
for txJson in response.getElems():
|
||||||
let tx = txJson.toTransactionDto()
|
let tx = txJson.toTransactionDto()
|
||||||
if tx.typeValue == ERC721_TRANSACTION_TYPE:
|
if tx.typeValue == ERC721_TRANSACTION_TYPE:
|
||||||
let nftId = collectibles.NFTUniqueID(
|
let nftId = collectibles.CollectibleUniqueID(
|
||||||
|
chainID: arg.chainId,
|
||||||
contractAddress: tx.contract,
|
contractAddress: tx.contract,
|
||||||
tokenID: tx.tokenId.toString(10)
|
tokenID: tx.tokenId
|
||||||
)
|
)
|
||||||
if not uniqueIds.any(x => (x == nftId)):
|
if not uniqueIds.any(x => (x == nftId)):
|
||||||
uniqueIds.add(nftId)
|
uniqueIds.add(nftId)
|
||||||
|
|
||||||
if len(uniqueIds) > 0:
|
if len(uniqueIds) > 0:
|
||||||
let collectiblesResponse = collectibles.getOpenseaAssetsByNFTUniqueID(arg.chainId, uniqueIds, arg.collectiblesLimit)
|
let collectiblesResponse = collectibles.getOpenseaAssetsByCollectibleUniqueID(uniqueIds)
|
||||||
|
|
||||||
if not collectiblesResponse.error.isNil:
|
if not collectiblesResponse.error.isNil:
|
||||||
# We don't want to prevent getting the list of transactions if we cannot get
|
# We don't want to prevent getting the list of transactions if we cannot get
|
||||||
# NFT metadata. Just don't return the metadata.
|
# 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
|
error "error loadTransactionsTask: ", errDesription
|
||||||
else:
|
else:
|
||||||
output["collectibles"] = collectiblesResponse.result
|
output["collectibles"] = collectiblesResponse.result
|
||||||
|
|
|
@ -1,41 +1,95 @@
|
||||||
import json, json_serialization, strformat
|
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
|
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
|
type
|
||||||
NFTUniqueID* = ref object of RootObj
|
# Mirrors services/wallet/collectibles/service.go ErrorCode
|
||||||
contractAddress* {.serializedFieldName("contract_address").}: string
|
ErrorCode* = enum
|
||||||
tokenID* {.serializedFieldName("token_id").}: string
|
ErrorCodeSuccess = 1,
|
||||||
|
ErrorCodeTaskCanceled,
|
||||||
|
ErrorCodeFailed
|
||||||
|
|
||||||
proc `$`*(self: NFTUniqueID): string =
|
# Mirrors services/wallet/collectibles/service.go FilterOwnedCollectiblesResponse
|
||||||
return fmt"""NFTUniqueID(
|
FilterOwnedCollectiblesResponse* = object
|
||||||
contractAddress:{self.contractAddress},
|
collectibles*: seq[CollectibleHeader]
|
||||||
tokenID:{self.tokenID}
|
offset*: int
|
||||||
)"""
|
hasMore*: bool
|
||||||
|
errorCode*: ErrorCode
|
||||||
|
|
||||||
proc `==`*(a, b: NFTUniqueID): bool =
|
# Mirrors services/wallet/collectibles/service.go GetCollectiblesDataResponse
|
||||||
result = a.contractAddress == b.contractAddress and
|
GetCollectiblesDataResponse* = object
|
||||||
a.tokenID == b.tokenID
|
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
|
chainId: int
|
||||||
address: string
|
address: string
|
||||||
cursor: string
|
cursor: string
|
||||||
limit: int
|
limit: int
|
||||||
|
|
||||||
rpc(getOpenseaAssetsByOwnerAndContractAddressWithCursor, "wallet"):
|
rpc(getCollectiblesByOwnerAndContractAddressWithCursor, "wallet"):
|
||||||
chainId: int
|
chainId: int
|
||||||
address: string
|
address: string
|
||||||
contractAddresses: seq[string]
|
contractAddresses: seq[string]
|
||||||
cursor: string
|
cursor: string
|
||||||
limit: int
|
limit: int
|
||||||
|
|
||||||
rpc(getOpenseaAssetsByNFTUniqueID, "wallet"):
|
rpc(getCollectiblesByUniqueID, "wallet"):
|
||||||
chainId: int
|
uniqueIds: seq[CollectibleUniqueID]
|
||||||
uniqueIds: seq[NFTUniqueID]
|
|
||||||
limit: int
|
|
||||||
|
|
||||||
rpc(getCollectibleOwnersByContractAddress, "wallet"):
|
rpc(getCollectibleOwnersByContractAddress, "wallet"):
|
||||||
chainId: int
|
chainId: int
|
||||||
contractAddress: string
|
contractAddress: string
|
||||||
|
|
||||||
|
rpc(filterOwnedCollectiblesAsync, "wallet"):
|
||||||
|
chainIDs: seq[int]
|
||||||
|
addresses: seq[string]
|
||||||
|
offset: int
|
||||||
|
limit: int
|
||||||
|
|
||||||
|
rpc(getCollectiblesDataAsync, "wallet"):
|
||||||
|
uniqueIds: seq[CollectibleUniqueID]
|
||||||
|
|
|
@ -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"})
|
||||||
|
)
|
|
@ -228,10 +228,6 @@ SplitView {
|
||||||
logs.logEvent("walletStore::setFilterAddress", ["address"], arguments)
|
logs.logEvent("walletStore::setFilterAddress", ["address"], arguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectCollectible(slug, id) {
|
|
||||||
logs.logEvent("walletStore::selectCollectible", ["slug", "id"], arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property var accounts: ListModel {
|
readonly property var accounts: ListModel {
|
||||||
ListElement {
|
ListElement {
|
||||||
name: "My Status Account"
|
name: "My Status Account"
|
||||||
|
@ -338,7 +334,7 @@ SplitView {
|
||||||
Component.onCompleted: append(data)
|
Component.onCompleted: append(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property var flatCollectibles: ListModel {
|
readonly property var collectibles: ListModel {
|
||||||
readonly property var data: [
|
readonly property var data: [
|
||||||
{
|
{
|
||||||
//id: 123,
|
//id: 123,
|
||||||
|
|
|
@ -28,63 +28,52 @@ SplitView {
|
||||||
id: collectiblesModel
|
id: collectiblesModel
|
||||||
readonly property var data: [
|
readonly property var data: [
|
||||||
{
|
{
|
||||||
id: 123,
|
uid: "123",
|
||||||
name: "SNT",
|
name: "SNT",
|
||||||
description: "",
|
|
||||||
collectionName: "Super Nitro Toluen (with pink bg)",
|
collectionName: "Super Nitro Toluen (with pink bg)",
|
||||||
backgroundColor: "pink",
|
backgroundColor: "pink",
|
||||||
imageUrl: ModelsData.collectibles.custom,
|
imageUrl: ModelsData.collectibles.custom,
|
||||||
permalink: "green",
|
|
||||||
isLoading: false
|
isLoading: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 34545656768,
|
uid: "34545656768",
|
||||||
name: "Kitty 1",
|
name: "Kitty 1",
|
||||||
description: "",
|
|
||||||
collectionName: "Kitties",
|
collectionName: "Kitties",
|
||||||
backgroundColor: "",
|
backgroundColor: "",
|
||||||
imageUrl: ModelsData.collectibles.kitty1Big,
|
imageUrl: ModelsData.collectibles.kitty1Big,
|
||||||
permalink: "",
|
|
||||||
isLoading: false
|
isLoading: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 123456,
|
uid: "123456",
|
||||||
name: "Kitty 2",
|
name: "Kitty 2",
|
||||||
description: "",
|
|
||||||
collectionName: "",
|
collectionName: "",
|
||||||
backgroundColor: "",
|
backgroundColor: "",
|
||||||
imageUrl: ModelsData.collectibles.kitty2Big,
|
imageUrl: ModelsData.collectibles.kitty2Big,
|
||||||
permalink: "",
|
|
||||||
isLoading: false
|
isLoading: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 12345645459537432,
|
uid: "12345645459537432",
|
||||||
name: "",
|
name: "",
|
||||||
description: "Kitty 3 description",
|
|
||||||
collectionName: "Super Kitties",
|
collectionName: "Super Kitties",
|
||||||
backgroundColor: "oink",
|
backgroundColor: "oink",
|
||||||
imageUrl: ModelsData.collectibles.kitty3Big,
|
imageUrl: ModelsData.collectibles.kitty3Big,
|
||||||
permalink: "",
|
|
||||||
isLoading: false
|
isLoading: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 691,
|
uid: "691",
|
||||||
name: "KILLABEAR",
|
name: "KILLABEAR",
|
||||||
description: "Please note that weapons are not yet reflected in the rarity stats.",
|
|
||||||
collectionName: "KILLABEARS",
|
collectionName: "KILLABEARS",
|
||||||
backgroundColor: "#807c56",
|
backgroundColor: "#807c56",
|
||||||
imageUrl: "https://assets.killabears.com/content/killabears/img/691-e81f892696a8ae700e0dbc62eb072060679a2046d1ef5eb2671bdb1fad1f68e3.png",
|
imageUrl: "https://assets.killabears.com/content/killabears/img/691-e81f892696a8ae700e0dbc62eb072060679a2046d1ef5eb2671bdb1fad1f68e3.png",
|
||||||
permalink: "https://opensea.io/assets/ethereum/0xc99c679c50033bbc5321eb88752e89a93e9e83c5/691",
|
|
||||||
isLoading: true
|
isLoading: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 8876,
|
uid: "8876",
|
||||||
name: "AIORBIT",
|
name: "AIORBIT",
|
||||||
description: "",
|
description: "",
|
||||||
collectionName: "AIORBIT (Animated SVG)",
|
collectionName: "AIORBIT (Animated SVG)",
|
||||||
backgroundColor: "",
|
backgroundColor: "",
|
||||||
imageUrl: "https://dl.openseauserdata.com/cache/originImage/files/8b14ef530b28853445c27d6693c4e805.svg",
|
imageUrl: "https://dl.openseauserdata.com/cache/originImage/files/8b14ef530b28853445c27d6693c4e805.svg",
|
||||||
permalink: "https://opensea.io/assets/ethereum/0xba66a7c5e1f89a542e3108e3df155a9bf41ac824/8876",
|
|
||||||
isLoading: false
|
isLoading: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -8,9 +8,9 @@ ProfileShowcasePanel {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
settingsKey: "collectibles"
|
settingsKey: "collectibles"
|
||||||
keyRole: "id"
|
keyRole: "uid"
|
||||||
roleNames: ["id", "name", "description", "collectionName", "backgroundColor", "imageUrl"]
|
roleNames: ["uid", "name", "collectionName", "backgroundColor", "imageUrl"]
|
||||||
filterFunc: (modelData) => !showcaseModel.hasItem(modelData.id)
|
filterFunc: (modelData) => !showcaseModel.hasItem(modelData.uid)
|
||||||
hiddenPlaceholderBanner: qsTr("Collectibles here will show on your profile")
|
hiddenPlaceholderBanner: qsTr("Collectibles here will show on your profile")
|
||||||
showcasePlaceholderBanner: qsTr("Collectibles here will be hidden from your profile")
|
showcasePlaceholderBanner: qsTr("Collectibles here will be hidden from your profile")
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,8 @@ QtObject {
|
||||||
|
|
||||||
// TODO(alaibe): there should be no access to wallet section, create collectible in profile
|
// TODO(alaibe): there should be no access to wallet section, create collectible in profile
|
||||||
property var overview: walletSectionOverview
|
property var overview: walletSectionOverview
|
||||||
property var flatCollectibles: Global.appIsReady ? walletSectionCollectibles.model : null
|
|
||||||
property var assets: walletSectionAssets.assets
|
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 accounts: Global.appIsReady? accountsModule.accounts : null
|
||||||
property var originModel: accountsModule.keyPairModel
|
property var originModel: accountsModule.keyPairModel
|
||||||
property bool includeWatchOnlyAccount: accountsModule.includeWatchOnlyAccount
|
property bool includeWatchOnlyAccount: accountsModule.includeWatchOnlyAccount
|
||||||
|
|
|
@ -211,7 +211,7 @@ ColumnLayout {
|
||||||
ProfileShowcaseCollectiblesPanel {
|
ProfileShowcaseCollectiblesPanel {
|
||||||
Layout.minimumHeight: implicitHeight
|
Layout.minimumHeight: implicitHeight
|
||||||
Layout.maximumHeight: implicitHeight
|
Layout.maximumHeight: implicitHeight
|
||||||
baseModel: root.walletStore.flatCollectibles
|
baseModel: root.walletStore.collectibles
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfileShowcaseAssetsPanel {
|
ProfileShowcaseAssetsPanel {
|
||||||
|
|
|
@ -156,7 +156,8 @@ QtObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collectibles Filters
|
// 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: []
|
property var collectiblesFilter: []
|
||||||
function toggleCollectibles(id) {
|
function toggleCollectibles(id) {
|
||||||
// update filters
|
// update filters
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,8 +29,7 @@ QtObject {
|
||||||
property string signingPhrase: walletSection.signingPhrase
|
property string signingPhrase: walletSection.signingPhrase
|
||||||
property string mnemonicBackedUp: walletSection.isMnemonicBackedUp
|
property string mnemonicBackedUp: walletSection.isMnemonicBackedUp
|
||||||
|
|
||||||
property var flatCollectibles: walletSectionCollectibles.model
|
property CollectiblesStore collectiblesStore: CollectiblesStore {}
|
||||||
property var currentCollectible: walletSectionCurrentCollectible
|
|
||||||
|
|
||||||
property var areTestNetworksEnabled: networksModule.areTestNetworksEnabled
|
property var areTestNetworksEnabled: networksModule.areTestNetworksEnabled
|
||||||
|
|
||||||
|
@ -175,19 +174,6 @@ QtObject {
|
||||||
return globalUtils.hex2Dec(value)
|
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) {
|
function getNameForSavedWalletAddress(address) {
|
||||||
return walletSectionSavedAddresses.getNameByAddress(address)
|
return walletSectionSavedAddresses.getNameByAddress(address)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
singleton RootStore 1.0 RootStore.qml
|
singleton RootStore 1.0 RootStore.qml
|
||||||
ActivityFiltersStore 1.0 ActivityFiltersStore.qml
|
ActivityFiltersStore 1.0 ActivityFiltersStore.qml
|
||||||
|
CollectiblesStore 1.0 CollectiblesStore.qml
|
||||||
|
|
|
@ -16,7 +16,7 @@ Item {
|
||||||
property var collectiblesModel
|
property var collectiblesModel
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
signal collectibleClicked(string address, string tokenId)
|
signal collectibleClicked(int chainId, string contractAddress, string tokenId)
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: contentLoader
|
id: contentLoader
|
||||||
|
@ -24,8 +24,10 @@ Item {
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
|
||||||
sourceComponent: {
|
sourceComponent: {
|
||||||
if (root.collectiblesModel.allCollectiblesLoaded && root.collectiblesModel.count === 0)
|
/* TODO: Issue #11635
|
||||||
|
if (!root.collectiblesModel.hasMore && root.collectiblesModel.count === 0)
|
||||||
return empty;
|
return empty;
|
||||||
|
*/
|
||||||
return loaded;
|
return loaded;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +64,7 @@ Item {
|
||||||
backgroundColor: model.backgroundColor ? model.backgroundColor : "transparent"
|
backgroundColor: model.backgroundColor ? model.backgroundColor : "transparent"
|
||||||
isLoading: model.isLoading
|
isLoading: model.isLoading
|
||||||
|
|
||||||
onClicked: root.collectibleClicked(model.address, model.tokenId)
|
onClicked: root.collectibleClicked(model.chainId, model.contractAddress, model.tokenId)
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollBar.vertical: StatusScrollBar {}
|
ScrollBar.vertical: StatusScrollBar {}
|
||||||
|
|
|
@ -115,9 +115,9 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CollectiblesView {
|
CollectiblesView {
|
||||||
collectiblesModel: RootStore.flatCollectibles
|
collectiblesModel: RootStore.collectiblesStore.ownedCollectibles
|
||||||
onCollectibleClicked: {
|
onCollectibleClicked: {
|
||||||
RootStore.selectCollectible(address, tokenId)
|
RootStore.collectiblesStore.getDetailedCollectible(chainId, contractAddress, tokenId)
|
||||||
stack.currentIndex = 1
|
stack.currentIndex = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,6 +134,8 @@ Item {
|
||||||
CollectibleDetailView {
|
CollectibleDetailView {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
collectible: RootStore.collectiblesStore.detailedCollectible
|
||||||
|
isCollectibleLoading: RootStore.collectiblesStore.isDetailedCollectibleLoading
|
||||||
}
|
}
|
||||||
AssetsDetailView {
|
AssetsDetailView {
|
||||||
id: assetDetailView
|
id: assetDetailView
|
||||||
|
|
|
@ -16,7 +16,8 @@ import "../../controls"
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var currentCollectible: RootStore.currentCollectible
|
property var collectible
|
||||||
|
property bool isCollectibleLoading
|
||||||
readonly property int isNarrowMode : width < 700
|
readonly property int isNarrowMode : width < 700
|
||||||
|
|
||||||
CollectibleDetailsHeader {
|
CollectibleDetailsHeader {
|
||||||
|
@ -24,14 +25,14 @@ Item {
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
asset.name: currentCollectible.collectionImageUrl
|
asset.name: collectible.collectionImageUrl
|
||||||
asset.isImage: true
|
asset.isImage: true
|
||||||
primaryText: currentCollectible.collectionName
|
primaryText: collectible.collectionName
|
||||||
secondaryText: "#" + currentCollectible.tokenId
|
secondaryText: "#" + collectible.tokenId
|
||||||
isNarrowMode: root.isNarrowMode
|
isNarrowMode: root.isNarrowMode
|
||||||
networkShortName: currentCollectible.networkShortName
|
networkShortName: collectible.networkShortName
|
||||||
networkColor: currentCollectible.networkColor
|
networkColor: collectible.networkColor
|
||||||
networkIconURL: currentCollectible.networkIconUrl
|
networkIconURL: collectible.networkIconUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
@ -57,12 +58,12 @@ Item {
|
||||||
width: size
|
width: size
|
||||||
height: size
|
height: size
|
||||||
radius: 2
|
radius: 2
|
||||||
color: currentCollectible.backgroundColor
|
color: collectible.backgroundColor
|
||||||
border.color: Theme.palette.directColor8
|
border.color: Theme.palette.directColor8
|
||||||
border.width: 1
|
border.width: 1
|
||||||
mediaUrl: currentCollectible.mediaUrl
|
mediaUrl: collectible.mediaUrl
|
||||||
mediaType: currentCollectible.mediaType
|
mediaType: collectible.mediaType
|
||||||
fallbackImageUrl: currentCollectible.imageUrl
|
fallbackImageUrl: collectible.imageUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
@ -76,7 +77,7 @@ Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 24
|
height: 24
|
||||||
|
|
||||||
text: currentCollectible.name
|
text: collectible.name
|
||||||
color: Theme.palette.directColor1
|
color: Theme.palette.directColor1
|
||||||
font.pixelSize: 17
|
font.pixelSize: 17
|
||||||
lineHeight: 24
|
lineHeight: 24
|
||||||
|
@ -97,7 +98,7 @@ Item {
|
||||||
id: descriptionText
|
id: descriptionText
|
||||||
width: descriptionScrollView.availableWidth
|
width: descriptionScrollView.availableWidth
|
||||||
|
|
||||||
text: currentCollectible.description
|
text: collectible.description
|
||||||
textFormat: Text.MarkdownText
|
textFormat: Text.MarkdownText
|
||||||
color: Theme.palette.directColor4
|
color: Theme.palette.directColor4
|
||||||
font.pixelSize: 15
|
font.pixelSize: 15
|
||||||
|
@ -114,7 +115,7 @@ Item {
|
||||||
id: collectiblesDetailsTab
|
id: collectiblesDetailsTab
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: root.isNarrowMode ? 0 : Style.current.xlPadding
|
Layout.topMargin: root.isNarrowMode ? 0 : Style.current.xlPadding
|
||||||
visible: currentCollectible.properties.count > 0
|
visible: collectible.traits.count > 0
|
||||||
|
|
||||||
StatusTabButton {
|
StatusTabButton {
|
||||||
leftPadding: 0
|
leftPadding: 0
|
||||||
|
@ -132,7 +133,7 @@ Item {
|
||||||
width: scrollView.availableWidth
|
width: scrollView.availableWidth
|
||||||
spacing: 10
|
spacing: 10
|
||||||
Repeater {
|
Repeater {
|
||||||
model: currentCollectible.properties
|
model: collectible.traits
|
||||||
InformationTile {
|
InformationTile {
|
||||||
maxWidth: parent.width
|
maxWidth: parent.width
|
||||||
primaryText: model.traitType
|
primaryText: model.traitType
|
||||||
|
|
|
@ -13,7 +13,7 @@ QtObject {
|
||||||
|
|
||||||
readonly property bool balanceCache: walletSectionAssets.hasBalanceCache
|
readonly property bool balanceCache: walletSectionAssets.hasBalanceCache
|
||||||
readonly property bool marketValuesCache: walletSectionAssets.hasMarketValuesCache
|
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 var blockchainNetworksDown: !!networkConnectionModule.blockchainNetworkConnection.chainIds ? networkConnectionModule.blockchainNetworkConnection.chainIds.split(";") : []
|
||||||
readonly property bool atleastOneBlockchainNetworkAvailable: blockchainNetworksDown.length < networksModule.all.count
|
readonly property bool atleastOneBlockchainNetworkAvailable: blockchainNetworksDown.length < networksModule.all.count
|
||||||
|
|
|
@ -247,7 +247,8 @@ Control {
|
||||||
cellWidth: (width-rightMargin)/4
|
cellWidth: (width-rightMargin)/4
|
||||||
cellHeight: cellWidth
|
cellHeight: cellWidth
|
||||||
visible: count
|
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 { }
|
ScrollBar.vertical: StatusScrollBar { }
|
||||||
delegate: StatusRoundedImage {
|
delegate: StatusRoundedImage {
|
||||||
width: GridView.view.cellWidth - Style.current.smallPadding
|
width: GridView.view.cellWidth - Style.current.smallPadding
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 3d1b1bab572eeedb7028abf2486dec3d9fe04858
|
Subproject commit 10a42e639d6c455e2a14a89006c8d653e9d1d441
|
Loading…
Reference in New Issue