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