feat(@desktop/wallet): integrate collectibles search backend
Closes #13922
This commit is contained in:
parent
0645ed4712
commit
99af24c39e
|
@ -0,0 +1,28 @@
|
|||
import app/modules/shared_modules/collectibles_search/controller as collectibles_search_c
|
||||
import app/modules/shared_modules/collections_search/controller as collections_search_c
|
||||
|
||||
|
||||
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 getCollectiblesSearchController*(self: AccessInterface): collectibles_search_c.Controller {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getCollectionsSearchController*(self: AccessInterface): collections_search_c.Controller {.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")
|
|
@ -0,0 +1,71 @@
|
|||
import NimQml
|
||||
|
||||
import ./io_interface, ./view
|
||||
import ../io_interface as delegate_interface
|
||||
|
||||
import app/global/global_singleton
|
||||
import app/core/eventemitter
|
||||
import app/modules/shared_modules/collectibles_search/controller as collectibles_c
|
||||
import app/modules/shared_modules/collections_search/controller as collections_c
|
||||
import app_service/service/network/service as network_service
|
||||
|
||||
import backend/collectibles as backend_collectibles
|
||||
|
||||
export io_interface
|
||||
|
||||
type
|
||||
Module* = ref object of io_interface.AccessInterface
|
||||
delegate: delegate_interface.AccessInterface
|
||||
events: EventEmitter
|
||||
view: View
|
||||
collectiblesController: collectibles_c.Controller
|
||||
collectionsController: collections_c.Controller
|
||||
moduleLoaded: bool
|
||||
|
||||
proc newModule*(
|
||||
delegate: delegate_interface.AccessInterface,
|
||||
events: EventEmitter,
|
||||
networkService: network_service.Service
|
||||
): Module =
|
||||
result = Module()
|
||||
result.delegate = delegate
|
||||
result.events = events
|
||||
|
||||
let collectiblesController = collectibles_c.newController(
|
||||
requestId = int32(backend_collectibles.CollectiblesRequestID.Search),
|
||||
networkService = networkService,
|
||||
events = events
|
||||
)
|
||||
result.collectiblesController = collectiblesController
|
||||
|
||||
let collectionsController = collections_c.newController(
|
||||
requestId = int32(backend_collectibles.CollectiblesRequestID.Search),
|
||||
networkService = networkService,
|
||||
events = events
|
||||
)
|
||||
result.collectionsController = collectionsController
|
||||
|
||||
result.view = newView(result)
|
||||
result.moduleLoaded = false
|
||||
|
||||
method delete*(self: Module) =
|
||||
self.view.delete
|
||||
self.collectionsController.delete
|
||||
self.collectiblesController.delete
|
||||
|
||||
method load*(self: Module) =
|
||||
singletonInstance.engine.setRootContextProperty("walletSectionCollectiblesSearch", newQVariant(self.view))
|
||||
self.view.load()
|
||||
|
||||
method isLoaded*(self: Module): bool =
|
||||
return self.moduleLoaded
|
||||
|
||||
method viewDidLoad*(self: Module) =
|
||||
self.moduleLoaded = true
|
||||
self.delegate.searchCollectiblesModuleDidLoad()
|
||||
|
||||
method getCollectiblesSearchController*(self: Module): collectibles_c.Controller =
|
||||
return self.collectiblesController
|
||||
|
||||
method getCollectionsSearchController*(self: Module): collections_c.Controller =
|
||||
return self.collectionsController
|
|
@ -0,0 +1,36 @@
|
|||
import NimQml, sequtils, strutils
|
||||
|
||||
import ./io_interface
|
||||
|
||||
import app/modules/shared_modules/collectibles_search/controller as collectibles_search_c
|
||||
import app/modules/shared_modules/collections_search/controller as collections_search_c
|
||||
|
||||
QtObject:
|
||||
type
|
||||
View* = ref object of QObject
|
||||
delegate: io_interface.AccessInterface
|
||||
collectiblesSearchController: collectibles_search_c.Controller
|
||||
collectionsSearchController: collections_search_c.Controller
|
||||
|
||||
proc delete*(self: View) =
|
||||
self.QObject.delete
|
||||
|
||||
proc newView*(delegate: io_interface.AccessInterface): View =
|
||||
new(result, delete)
|
||||
result.QObject.setup
|
||||
result.delegate = delegate
|
||||
result.collectiblesSearchController = delegate.getCollectiblesSearchController()
|
||||
result.collectionsSearchController = delegate.getCollectionsSearchController()
|
||||
|
||||
proc load*(self: View) =
|
||||
self.delegate.viewDidLoad()
|
||||
|
||||
proc getCollectiblesSearchController(self: View): QVariant {.slot.} =
|
||||
return newQVariant(self.collectiblesSearchController)
|
||||
QtProperty[QVariant] collectiblesSearchController:
|
||||
read = getCollectiblesSearchController
|
||||
|
||||
proc getCollectionsSearchController(self: View): QVariant {.slot.} =
|
||||
return newQVariant(self.collectionsSearchController)
|
||||
QtProperty[QVariant] collectionsSearchController:
|
||||
read = getCollectionsSearchController
|
|
@ -49,6 +49,9 @@ method allTokensModuleDidLoad*(self: AccessInterface) {.base.} =
|
|||
method allCollectiblesModuleDidLoad*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method searchCollectiblesModuleDidLoad*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method collectiblesModuleDidLoad*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import ../io_interface as delegate_interface
|
|||
import ./accounts/module as accounts_module
|
||||
import ./all_tokens/module as all_tokens_module
|
||||
import ./all_collectibles/module as all_collectibles_module
|
||||
import ./collectibles_search/module as collectibles_search_module
|
||||
import ./assets/module as assets_module
|
||||
import ./saved_addresses/module as saved_addresses_module
|
||||
import ./buy_sell_crypto/module as buy_sell_crypto_module
|
||||
|
@ -65,6 +66,7 @@ type
|
|||
accountsModule: accounts_module.AccessInterface
|
||||
allTokensModule: all_tokens_module.AccessInterface
|
||||
allCollectiblesModule: all_collectibles_module.AccessInterface
|
||||
collectiblesSearchModule: collectibles_search_module.AccessInterface
|
||||
assetsModule: assets_module.AccessInterface
|
||||
sendModule: send_module.AccessInterface
|
||||
savedAddressesModule: saved_addresses_module.AccessInterface
|
||||
|
@ -131,6 +133,7 @@ proc newModule*(
|
|||
result.allTokensModule = all_tokens_module.newModule(result, events, tokenService, walletAccountService, settingsService, communityTokensService)
|
||||
let allCollectiblesModule = all_collectibles_module.newModule(result, events, collectibleService, networkService, walletAccountService, settingsService)
|
||||
result.allCollectiblesModule = allCollectiblesModule
|
||||
result.collectiblesSearchModule = collectibles_search_module.newModule(result, events, networkService)
|
||||
result.assetsModule = assets_module.newModule(result, events, walletAccountService, networkService, tokenService,
|
||||
currencyService)
|
||||
result.sendModule = send_module.newModule(result, events, walletAccountService, networkService, currencyService,
|
||||
|
@ -176,6 +179,7 @@ method delete*(self: Module) =
|
|||
self.accountsModule.delete
|
||||
self.allTokensModule.delete
|
||||
self.allCollectiblesModule.delete
|
||||
self.collectiblesSearchModule.delete
|
||||
self.assetsModule.delete
|
||||
self.savedAddressesModule.delete
|
||||
self.buySellCryptoModule.delete
|
||||
|
@ -335,6 +339,7 @@ method load*(self: Module) =
|
|||
self.accountsModule.load()
|
||||
self.allTokensModule.load()
|
||||
self.allCollectiblesModule.load()
|
||||
self.collectiblesSearchModule.load()
|
||||
self.assetsModule.load()
|
||||
self.savedAddressesModule.load()
|
||||
self.buySellCryptoModule.load()
|
||||
|
@ -356,6 +361,9 @@ proc checkIfModuleDidLoad(self: Module) =
|
|||
if(not self.allCollectiblesModule.isLoaded()):
|
||||
return
|
||||
|
||||
if(not self.collectiblesSearchModule.isLoaded()):
|
||||
return
|
||||
|
||||
if(not self.assetsModule.isLoaded()):
|
||||
return
|
||||
|
||||
|
@ -397,6 +405,9 @@ method allTokensModuleDidLoad*(self: Module) =
|
|||
method allCollectiblesModuleDidLoad*(self: Module) =
|
||||
self.checkIfModuleDidLoad()
|
||||
|
||||
method searchCollectiblesModuleDidLoad*(self: Module) =
|
||||
self.checkIfModuleDidLoad()
|
||||
|
||||
method collectiblesModuleDidLoad*(self: Module) =
|
||||
self.checkIfModuleDidLoad()
|
||||
|
||||
|
|
|
@ -5,6 +5,9 @@ import app_service/service/currency/dto as currency_dto
|
|||
import ../main/wallet_section/accounts/item as wallet_accounts_item
|
||||
import ../main/wallet_section/send/account_item as wallet_send_account_item
|
||||
|
||||
import backend/collectibles_types as collectibles
|
||||
import app_service/common/types
|
||||
|
||||
proc currencyAmountToItem*(amount: float64, format: CurrencyFormatDto) : CurrencyAmount =
|
||||
return newCurrencyAmount(
|
||||
amount,
|
||||
|
@ -72,3 +75,11 @@ proc walletAccountToWalletSendAccountItem*(w: WalletAccountDto, chainIds: seq[in
|
|||
w.testPreferredChainIds,
|
||||
canSend=w.walletType != "watch" and (w.operable==AccountFullyOperable or w.operable==AccountPartiallyOperable)
|
||||
)
|
||||
|
||||
proc contractTypeToTokenType*(contractType : ContractType): TokenType =
|
||||
case contractType:
|
||||
of ContractType.ContractTypeUnknown: return TokenType.Unknown
|
||||
of ContractType.ContractTypeERC20: return TokenType.ERC20
|
||||
of ContractType.ContractTypeERC721: return TokenType.ERC721
|
||||
of ContractType.ContractTypeERC1155: return TokenType.ERC1155
|
||||
else: return TokenType.Unknown
|
|
@ -0,0 +1,216 @@
|
|||
import NimQml, json, stew/shims/strformat, sequtils, strutils, stint, strutils
|
||||
import options
|
||||
|
||||
import backend/collectibles as backend
|
||||
import collectible_trait_model
|
||||
import app_service/common/types
|
||||
import app/modules/shared/wallet_utils
|
||||
|
||||
# It is used to display a detailed collectibles entry in the QML UI
|
||||
QtObject:
|
||||
type
|
||||
CollectiblesDataEntry* = ref object of QObject
|
||||
id: backend.CollectibleUniqueID
|
||||
data: backend.Collectible
|
||||
traits: TraitModel
|
||||
generatedId: string
|
||||
generatedCollectionId: string
|
||||
tokenType: TokenType
|
||||
|
||||
proc setup(self: CollectiblesDataEntry) =
|
||||
self.QObject.setup
|
||||
|
||||
proc delete*(self: CollectiblesDataEntry) =
|
||||
self.QObject.delete
|
||||
|
||||
proc setData(self: CollectiblesDataEntry, data: backend.Collectible) =
|
||||
self.data = data
|
||||
self.traits = newTraitModel()
|
||||
if isSome(data.collectibleData) and isSome(data.collectibleData.get().traits):
|
||||
let traits = data.collectibleData.get().traits.get()
|
||||
self.traits.setItems(traits)
|
||||
self.setup()
|
||||
|
||||
proc `$`*(self: CollectiblesDataEntry): string =
|
||||
return fmt"""CollectiblesDataEntry(
|
||||
id:{self.id},
|
||||
data:{self.data},
|
||||
traits:{self.traits},
|
||||
generatedId:{self.generatedId},
|
||||
generatedCollectionId:{self.generatedCollectionId},
|
||||
tokenType:{self.tokenType},
|
||||
)"""
|
||||
|
||||
proc hasCollectibleData(self: CollectiblesDataEntry): bool =
|
||||
return self.data != nil and isSome(self.data.collectibleData)
|
||||
|
||||
proc getCollectibleData(self: CollectiblesDataEntry): backend.CollectibleData =
|
||||
return self.data.collectibleData.get()
|
||||
|
||||
proc getChainID*(self: CollectiblesDataEntry): int {.slot.} =
|
||||
return self.id.contractID.chainID
|
||||
|
||||
QtProperty[int] chainId:
|
||||
read = getChainID
|
||||
|
||||
proc getContractAddress*(self: CollectiblesDataEntry): string {.slot.} =
|
||||
return self.id.contractID.address
|
||||
|
||||
QtProperty[string] contractAddress:
|
||||
read = getContractAddress
|
||||
|
||||
proc getTokenID*(self: CollectiblesDataEntry): UInt256 =
|
||||
return self.id.tokenID
|
||||
|
||||
proc getTokenIDAsString*(self: CollectiblesDataEntry): string {.slot.} =
|
||||
return self.getTokenID().toString()
|
||||
|
||||
QtProperty[string] tokenId:
|
||||
read = getTokenIDAsString
|
||||
|
||||
# Unique ID to identify collectible, generated by us
|
||||
proc getID*(self: CollectiblesDataEntry): backend.CollectibleUniqueID =
|
||||
return self.id
|
||||
|
||||
proc getIDAsString*(self: CollectiblesDataEntry): string =
|
||||
return self.generatedId
|
||||
|
||||
# Unique ID to identify collection, generated by us
|
||||
proc getCollectionID*(self: CollectiblesDataEntry): backend.ContractID =
|
||||
return self.id.contractID
|
||||
|
||||
proc getCollectionIDAsString*(self: CollectiblesDataEntry): string =
|
||||
return self.generatedCollectionId
|
||||
|
||||
proc nameChanged*(self: CollectiblesDataEntry) {.signal.}
|
||||
proc getName*(self: CollectiblesDataEntry): string {.slot.} =
|
||||
if self.hasCollectibleData():
|
||||
result = self.data.collectibleData.get().name
|
||||
if result == "":
|
||||
result = "#" & self.getTokenIDAsString()
|
||||
|
||||
QtProperty[string] name:
|
||||
read = getName
|
||||
notify = nameChanged
|
||||
|
||||
proc imageURLChanged*(self: CollectiblesDataEntry) {.signal.}
|
||||
proc getImageURL*(self: CollectiblesDataEntry): string {.slot.} =
|
||||
if not self.hasCollectibleData() or isNone(self.getCollectibleData().imageUrl):
|
||||
return ""
|
||||
return self.getCollectibleData().imageUrl.get()
|
||||
|
||||
QtProperty[string] imageUrl:
|
||||
read = getImageURL
|
||||
notify = imageURLChanged
|
||||
|
||||
proc getOriginalMediaURL(self: CollectiblesDataEntry): string =
|
||||
if not self.hasCollectibleData() or isNone(self.getCollectibleData().animationUrl):
|
||||
return ""
|
||||
return self.getCollectibleData().animationUrl.get()
|
||||
|
||||
proc mediaURLChanged*(self: CollectiblesDataEntry) {.signal.}
|
||||
proc getMediaURL*(self: CollectiblesDataEntry): string {.slot.} =
|
||||
result = self.getOriginalMediaURL()
|
||||
if result == "":
|
||||
result = self.getImageURL()
|
||||
|
||||
QtProperty[string] mediaUrl:
|
||||
read = getMediaURL
|
||||
notify = mediaURLChanged
|
||||
|
||||
proc getOriginalMediaType(self: CollectiblesDataEntry): string =
|
||||
if not self.hasCollectibleData() or isNone(self.getCollectibleData().animationMediaType):
|
||||
return ""
|
||||
return self.getCollectibleData().animationMediaType.get()
|
||||
|
||||
proc mediaTypeChanged*(self: CollectiblesDataEntry) {.signal.}
|
||||
proc getMediaType*(self: CollectiblesDataEntry): string {.slot.} =
|
||||
result = self.getOriginalMediaType()
|
||||
if result == "":
|
||||
result = "image"
|
||||
|
||||
QtProperty[string] mediaType:
|
||||
read = getMediaType
|
||||
notify = mediaTypeChanged
|
||||
|
||||
proc backgroundColorChanged*(self: CollectiblesDataEntry) {.signal.}
|
||||
proc getBackgroundColor*(self: CollectiblesDataEntry): string {.slot.} =
|
||||
var color = "transparent"
|
||||
if self.hasCollectibleData() and isSome(self.getCollectibleData().backgroundColor):
|
||||
let backgroundColor = self.getCollectibleData().backgroundColor.get()
|
||||
if backgroundColor != "":
|
||||
color = "#" & backgroundColor
|
||||
return color
|
||||
|
||||
QtProperty[string] backgroundColor:
|
||||
read = getBackgroundColor
|
||||
notify = backgroundColorChanged
|
||||
|
||||
proc descriptionChanged*(self: CollectiblesDataEntry) {.signal.}
|
||||
proc getDescription*(self: CollectiblesDataEntry): string {.slot.} =
|
||||
if not self.hasCollectibleData() or isNone(self.getCollectibleData().description):
|
||||
return ""
|
||||
return self.getCollectibleData().description.get()
|
||||
|
||||
QtProperty[string] description:
|
||||
read = getDescription
|
||||
notify = descriptionChanged
|
||||
|
||||
proc traitsChanged*(self: CollectiblesDataEntry) {.signal.}
|
||||
proc getTraits*(self: CollectiblesDataEntry): QVariant {.slot.} =
|
||||
return newQVariant(self.traits)
|
||||
|
||||
QtProperty[QVariant] traits:
|
||||
read = getTraits
|
||||
notify = traitsChanged
|
||||
|
||||
proc tokenTypeChanged*(self: CollectiblesDataEntry) {.signal.}
|
||||
proc getTokenType*(self: CollectiblesDataEntry): int {.slot.} =
|
||||
return self.tokenType.int
|
||||
|
||||
QtProperty[int] tokenType:
|
||||
read = getTokenType
|
||||
notify = tokenTypeChanged
|
||||
|
||||
proc updateDataIfSameID*(self: CollectiblesDataEntry, update: backend.Collectible): bool =
|
||||
if self.id != update.id:
|
||||
return false
|
||||
|
||||
self.setData(update)
|
||||
|
||||
# Notify changes for all properties
|
||||
self.nameChanged()
|
||||
self.imageUrlChanged()
|
||||
self.mediaUrlChanged()
|
||||
self.mediaTypeChanged()
|
||||
self.backgroundColorChanged()
|
||||
self.descriptionChanged()
|
||||
self.traitsChanged()
|
||||
return true
|
||||
|
||||
proc newCollectiblesDataFullEntry*(data: backend.Collectible): CollectiblesDataEntry =
|
||||
new(result, delete)
|
||||
result.id = data.id
|
||||
result.setData(data)
|
||||
result.generatedId = result.id.toString()
|
||||
result.generatedCollectionId = result.id.contractID.toString()
|
||||
result.tokenType = contractTypeToTokenType(data.contractType.get())
|
||||
result.setup()
|
||||
|
||||
proc newCollectiblesDataBasicEntry*(id: backend.CollectibleUniqueID): CollectiblesDataEntry =
|
||||
new(result, delete)
|
||||
result.id = id
|
||||
result.traits = newTraitModel()
|
||||
result.generatedId = result.id.toString()
|
||||
result.generatedCollectionId = result.id.contractID.toString()
|
||||
result.setup()
|
||||
|
||||
proc newCollectiblesDataEmptyEntry*(): CollectiblesDataEntry =
|
||||
let id = backend.CollectibleUniqueID(
|
||||
contractID: backend.ContractID(
|
||||
chainID: 0,
|
||||
address: ""
|
||||
),
|
||||
tokenID: stint.u256(0)
|
||||
)
|
||||
return newCollectiblesDataBasicEntry(id)
|
|
@ -0,0 +1,240 @@
|
|||
import NimQml, Tables, strutils, stew/shims/strformat, sequtils, stint, json
|
||||
import logging
|
||||
|
||||
import ./collectibles_data_entry
|
||||
import backend/collectibles as backend_collectibles
|
||||
|
||||
type
|
||||
CollectibleRole* {.pure.} = enum
|
||||
Uid = UserRole + 1,
|
||||
ChainId
|
||||
ContractAddress
|
||||
TokenId
|
||||
Name
|
||||
ImageUrl
|
||||
MediaUrl
|
||||
MediaType
|
||||
BackgroundColor
|
||||
CollectionUid
|
||||
|
||||
QtObject:
|
||||
type
|
||||
Model* = ref object of QAbstractListModel
|
||||
items: seq[CollectiblesDataEntry]
|
||||
hasMore: bool
|
||||
|
||||
proc delete(self: Model) =
|
||||
self.items = @[]
|
||||
self.QAbstractListModel.delete
|
||||
|
||||
proc setup(self: Model) =
|
||||
self.QAbstractListModel.setup
|
||||
|
||||
proc newModel*(): Model =
|
||||
new(result, delete)
|
||||
result.setup
|
||||
result.items = @[]
|
||||
result.hasMore = true
|
||||
|
||||
proc `$`*(self: Model): string =
|
||||
for i in 0 ..< self.items.len:
|
||||
result &= fmt"""[{i}]:({$self.items[i]})"""
|
||||
|
||||
proc countChanged(self: Model) {.signal.}
|
||||
proc getCount*(self: Model): int {.slot.} =
|
||||
return self.items.len
|
||||
|
||||
QtProperty[int] count:
|
||||
read = getCount
|
||||
notify = countChanged
|
||||
|
||||
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) =
|
||||
if hasMore == self.hasMore:
|
||||
return
|
||||
self.hasMore = hasMore
|
||||
self.hasMoreChanged()
|
||||
|
||||
method canFetchMore*(self: Model, parent: QModelIndex): bool =
|
||||
return self.hasMore
|
||||
|
||||
proc loadMoreItems(self: Model) {.signal.}
|
||||
|
||||
proc loadMore*(self: Model) {.slot.} =
|
||||
self.loadMoreItems()
|
||||
|
||||
method fetchMore*(self: Model, parent: QModelIndex) =
|
||||
self.loadMore()
|
||||
|
||||
method rowCount*(self: Model, index: QModelIndex = nil): int =
|
||||
return self.getCount()
|
||||
|
||||
method roleNames(self: Model): Table[int, string] =
|
||||
{
|
||||
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.CollectionUid.int:"collectionUid"
|
||||
}.toTable
|
||||
|
||||
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
||||
if (not index.isValid):
|
||||
return
|
||||
|
||||
if (index.row < 0 or index.row >= self.getCount()):
|
||||
return
|
||||
|
||||
let enumRole = role.CollectibleRole
|
||||
|
||||
if index.row < self.items.len:
|
||||
let item = self.items[index.row]
|
||||
case enumRole:
|
||||
of CollectibleRole.Uid:
|
||||
result = newQVariant(item.getIDAsString())
|
||||
of CollectibleRole.ChainId:
|
||||
result = newQVariant(item.getChainID())
|
||||
of CollectibleRole.ContractAddress:
|
||||
result = newQVariant(item.getContractAddress())
|
||||
of CollectibleRole.TokenId:
|
||||
result = newQVariant(item.getTokenIDAsString())
|
||||
of CollectibleRole.Name:
|
||||
result = newQVariant(item.getName())
|
||||
of CollectibleRole.MediaUrl:
|
||||
result = newQVariant(item.getMediaURL())
|
||||
of CollectibleRole.MediaType:
|
||||
result = newQVariant(item.getMediaType())
|
||||
of CollectibleRole.ImageUrl:
|
||||
result = newQVariant(item.getImageURL())
|
||||
of CollectibleRole.BackgroundColor:
|
||||
result = newQVariant(item.getBackgroundColor())
|
||||
of CollectibleRole.CollectionUid:
|
||||
result = newQVariant(item.getCollectionIDAsString())
|
||||
|
||||
proc resetCollectibleItems(self: Model, newItems: seq[CollectiblesDataEntry] = @[]) =
|
||||
self.beginResetModel()
|
||||
self.items = newItems
|
||||
self.endResetModel()
|
||||
self.countChanged()
|
||||
|
||||
proc appendCollectibleItems(self: Model, newItems: seq[CollectiblesDataEntry]) =
|
||||
if len(newItems) == 0:
|
||||
return
|
||||
|
||||
let parentModelIndex = newQModelIndex()
|
||||
defer: parentModelIndex.delete
|
||||
|
||||
# Start after the current last real item
|
||||
let startIdx = self.items.len
|
||||
# End at the new last real item
|
||||
let endIdx = startIdx + newItems.len - 1
|
||||
|
||||
self.beginInsertRows(parentModelIndex, startIdx, endIdx)
|
||||
self.items.insert(newItems, startIdx)
|
||||
self.endInsertRows()
|
||||
self.countChanged()
|
||||
|
||||
proc removeCollectibleItem(self: Model, idx: int) =
|
||||
if idx < 0 or idx >= self.items.len:
|
||||
return
|
||||
|
||||
let parentModelIndex = newQModelIndex()
|
||||
defer: parentModelIndex.delete
|
||||
|
||||
self.beginRemoveRows(parentModelIndex, idx, idx)
|
||||
self.items.delete(idx)
|
||||
self.endRemoveRows()
|
||||
self.countChanged()
|
||||
|
||||
proc updateCollectibleItems(self: Model, newItems: seq[CollectiblesDataEntry]) =
|
||||
if len(self.items) == 0:
|
||||
# Current list is empty, just replace with new list
|
||||
self.resetCollectibleItems(newItems)
|
||||
return
|
||||
|
||||
if len(newItems) == 0:
|
||||
# New list is empty, just remove all items
|
||||
self.resetCollectibleItems()
|
||||
return
|
||||
|
||||
var newTable = initTable[string, int](len(newItems))
|
||||
for i in 0 ..< len(newItems):
|
||||
newTable[newItems[i].getIDAsString()] = i
|
||||
|
||||
# Needs to be built in sequential index order
|
||||
var oldIndicesToRemove: seq[int] = @[]
|
||||
for idx in 0 ..< len(self.items):
|
||||
let uid = self.items[idx].getIDAsString()
|
||||
if not newTable.hasKey(uid):
|
||||
# Item in old list but not in new -> Must remove
|
||||
oldIndicesToRemove.add(idx)
|
||||
else:
|
||||
# Item both in old and new lists -> Nothing to do in the current list,
|
||||
# remove from the new list so it only holds new items.
|
||||
newTable.del(uid)
|
||||
|
||||
if len(oldIndicesToRemove) > 0:
|
||||
var removedItems = 0
|
||||
for idx in oldIndicesToRemove:
|
||||
let updatedIdx = idx - removedItems
|
||||
self.removeCollectibleItem(updatedIdx)
|
||||
removedItems += 1
|
||||
self.countChanged()
|
||||
|
||||
var newItemsToAdd: seq[CollectiblesDataEntry] = @[]
|
||||
for uid, idx in newTable:
|
||||
newItemsToAdd.add(newItems[idx])
|
||||
self.appendCollectibleItems(newItemsToAdd)
|
||||
|
||||
proc getItems*(self: Model): seq[CollectiblesDataEntry] =
|
||||
return self.items
|
||||
|
||||
proc getItemById*(self: Model, id: string): CollectiblesDataEntry =
|
||||
for item in self.items:
|
||||
if(cmpIgnoreCase(item.getIDAsString(), id) == 0):
|
||||
return item
|
||||
return nil
|
||||
|
||||
proc setItems*(self: Model, newItems: seq[CollectiblesDataEntry], offset: int, hasMore: bool) =
|
||||
if offset == 0:
|
||||
self.resetCollectibleItems(newItems)
|
||||
elif offset != self.getCount():
|
||||
error "invalid offset"
|
||||
return
|
||||
else:
|
||||
self.appendCollectibleItems(newItems)
|
||||
self.setHasMore(hasMore)
|
||||
|
||||
# Checks the diff between the current list and the new list, appends new items,
|
||||
# removes missing items.
|
||||
# We assume the order of the items in the input could change, and we don't care
|
||||
# about the order of the items in the model.
|
||||
proc updateItems*(self: Model, newItems: seq[CollectiblesDataEntry]) =
|
||||
self.updateCollectibleItems(newItems)
|
||||
self.setHasMore(false)
|
||||
|
||||
proc itemsDataUpdated(self: Model) {.signal.}
|
||||
proc updateItemsData*(self: Model, updates: seq[backend_collectibles.Collectible]) =
|
||||
var anyUpdated = false
|
||||
for i in countdown(self.items.high, 0):
|
||||
let entry = self.items[i]
|
||||
for j in countdown(updates.high, 0):
|
||||
let update = updates[j]
|
||||
if entry.updateDataIfSameID(update):
|
||||
let index = self.createIndex(i, 0, nil)
|
||||
defer: index.delete
|
||||
self.dataChanged(index, index)
|
||||
anyUpdated = true
|
||||
break
|
||||
if anyUpdated:
|
||||
self.itemsDataUpdated()
|
|
@ -6,8 +6,7 @@ import collectible_trait_model
|
|||
import collectible_ownership_model
|
||||
import app_service/service/community_tokens/dto/community_token
|
||||
import app_service/common/types
|
||||
|
||||
const invalidTimestamp* = high(int)
|
||||
import app/modules/shared/wallet_utils
|
||||
|
||||
# Additional data needed to build an Entry, which is
|
||||
# not included in the backend data and needs to be
|
||||
|
@ -373,14 +372,6 @@ QtObject:
|
|||
self.communityImageChanged()
|
||||
return true
|
||||
|
||||
proc contractTypeToTokenType(contractType : ContractType): TokenType =
|
||||
case contractType:
|
||||
of ContractType.ContractTypeUnknown: return TokenType.Unknown
|
||||
of ContractType.ContractTypeERC20: return TokenType.ERC20
|
||||
of ContractType.ContractTypeERC721: return TokenType.ERC721
|
||||
of ContractType.ContractTypeERC1155: return TokenType.ERC1155
|
||||
else: return TokenType.Unknown
|
||||
|
||||
proc newCollectibleDetailsFullEntry*(data: backend.Collectible, extradata: ExtraData): CollectiblesEntry =
|
||||
new(result, delete)
|
||||
result.id = data.id
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
import NimQml, json, stew/shims/strformat, sequtils, strutils, strutils
|
||||
import options
|
||||
|
||||
import backend/collectibles_types as backend
|
||||
import app_service/common/types
|
||||
import app/modules/shared/wallet_utils
|
||||
|
||||
QtObject:
|
||||
type
|
||||
CollectionsDataEntry* = ref object of QObject
|
||||
id: backend.ContractID
|
||||
data: backend.Collection
|
||||
generatedId: string
|
||||
tokenType: TokenType
|
||||
|
||||
proc setup(self: CollectionsDataEntry) =
|
||||
self.QObject.setup
|
||||
|
||||
proc delete*(self: CollectionsDataEntry) =
|
||||
self.QObject.delete
|
||||
|
||||
proc setData(self: CollectionsDataEntry, data: backend.Collection) =
|
||||
self.data = data
|
||||
self.setup()
|
||||
|
||||
proc `$`*(self: CollectionsDataEntry): string =
|
||||
return fmt"""CollectionsDataEntry(
|
||||
id:{self.id},
|
||||
data:{self.data},
|
||||
generatedId:{self.generatedId},
|
||||
tokenType:{self.tokenType},
|
||||
)"""
|
||||
|
||||
proc hasCollectionData(self: CollectionsDataEntry): bool =
|
||||
return self.data != nil and isSome(self.data.collectionData)
|
||||
|
||||
proc getCollectionData(self: CollectionsDataEntry): backend.CollectionData =
|
||||
return self.data.collectionData.get()
|
||||
|
||||
proc getChainID*(self: CollectionsDataEntry): int {.slot.} =
|
||||
return self.id.chainID
|
||||
|
||||
QtProperty[int] chainId:
|
||||
read = getChainID
|
||||
|
||||
proc getContractAddress*(self: CollectionsDataEntry): string {.slot.} =
|
||||
return self.id.address
|
||||
|
||||
QtProperty[string] contractAddress:
|
||||
read = getContractAddress
|
||||
|
||||
# Unique ID to identify collection, generated by us
|
||||
proc getID*(self: CollectionsDataEntry): backend.ContractID =
|
||||
return self.id
|
||||
|
||||
proc getIDAsString*(self: CollectionsDataEntry): string =
|
||||
return self.generatedId
|
||||
|
||||
proc nameChanged*(self: CollectionsDataEntry) {.signal.}
|
||||
proc getName*(self: CollectionsDataEntry): string {.slot.} =
|
||||
if self.hasCollectionData():
|
||||
result = self.getCollectionData().name
|
||||
if result == "":
|
||||
result = self.getIDAsString()
|
||||
|
||||
QtProperty[string] name:
|
||||
read = getName
|
||||
notify = nameChanged
|
||||
|
||||
proc imageURLChanged*(self: CollectionsDataEntry) {.signal.}
|
||||
proc getImageURL*(self: CollectionsDataEntry): string {.slot.} =
|
||||
if not self.hasCollectionData():
|
||||
return ""
|
||||
return self.getCollectionData().imageUrl
|
||||
|
||||
QtProperty[string] imageUrl:
|
||||
read = getImageURL
|
||||
notify = imageURLChanged
|
||||
|
||||
proc slugChanged*(self: CollectionsDataEntry) {.signal.}
|
||||
proc getSlug*(self: CollectionsDataEntry): string {.slot.} =
|
||||
if not self.hasCollectionData():
|
||||
return ""
|
||||
return self.getCollectionData().slug
|
||||
|
||||
QtProperty[string] collectionSlug:
|
||||
read = getCollectionSlug
|
||||
notify = collectionSlugChanged
|
||||
|
||||
proc tokenTypeChanged*(self: CollectionsDataEntry) {.signal.}
|
||||
proc getTokenType*(self: CollectionsDataEntry): int {.slot.} =
|
||||
return self.tokenType.int
|
||||
|
||||
QtProperty[int] tokenType:
|
||||
read = getTokenType
|
||||
notify = tokenTypeChanged
|
||||
|
||||
proc communityIdChanged*(self: CollectionsDataEntry) {.signal.}
|
||||
proc getCommunityId*(self: CollectionsDataEntry): string {.slot.} =
|
||||
if not self.hasCollectionData():
|
||||
return ""
|
||||
return self.data.communityId
|
||||
|
||||
QtProperty[string] communityId:
|
||||
read = getCommunityId
|
||||
notify = communityIdChanged
|
||||
|
||||
proc updateDataIfSameID*(self: CollectionsDataEntry, update: backend.Collection): bool =
|
||||
if self.id != update.id:
|
||||
return false
|
||||
|
||||
self.setData(update)
|
||||
|
||||
# Notify changes for all properties
|
||||
self.nameChanged()
|
||||
self.slugChanged()
|
||||
self.imageUrlChanged()
|
||||
return true
|
||||
|
||||
proc newCollectionsDataFullEntry*(data: backend.Collection): CollectionsDataEntry =
|
||||
new(result, delete)
|
||||
result.id = data.id
|
||||
result.setData(data)
|
||||
result.generatedId = result.id.toString()
|
||||
result.tokenType = contractTypeToTokenType(data.contractType)
|
||||
result.setup()
|
||||
|
||||
proc newCollectionsDataBasicEntry*(id: backend.ContractID): CollectionsDataEntry =
|
||||
new(result, delete)
|
||||
result.id = id
|
||||
result.generatedId = result.id.toString()
|
||||
result.setup()
|
||||
|
||||
proc newCollectionsDataEmptyEntry*(): CollectionsDataEntry =
|
||||
let id = backend.ContractID(
|
||||
chainID: 0,
|
||||
address: ""
|
||||
)
|
||||
return newCollectionsDataBasicEntry(id)
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
import NimQml, Tables, strutils, stew/shims/strformat, sequtils, stint, json
|
||||
import logging
|
||||
|
||||
import ./collections_data_entry
|
||||
import backend/collectibles_types as backend_collectibles
|
||||
|
||||
type
|
||||
CollectionRole* {.pure.} = enum
|
||||
# ID roles
|
||||
Uid = UserRole + 1,
|
||||
ChainId
|
||||
ContractAddress
|
||||
TokenType
|
||||
# Metadata roles
|
||||
Name
|
||||
Slug
|
||||
ImageUrl
|
||||
|
||||
QtObject:
|
||||
type
|
||||
Model* = ref object of QAbstractListModel
|
||||
items: seq[CollectionsDataEntry]
|
||||
hasMore: bool
|
||||
|
||||
proc delete(self: Model) =
|
||||
self.items = @[]
|
||||
self.QAbstractListModel.delete
|
||||
|
||||
proc setup(self: Model) =
|
||||
self.QAbstractListModel.setup
|
||||
|
||||
proc newModel*(): Model =
|
||||
new(result, delete)
|
||||
result.setup
|
||||
result.items = @[]
|
||||
result.hasMore = true
|
||||
|
||||
proc `$`*(self: Model): string =
|
||||
for i in 0 ..< self.items.len:
|
||||
result &= fmt"""[{i}]:({$self.items[i]})"""
|
||||
|
||||
proc countChanged(self: Model) {.signal.}
|
||||
proc getCount*(self: Model): int {.slot.} =
|
||||
return self.items.len
|
||||
|
||||
QtProperty[int] count:
|
||||
read = getCount
|
||||
notify = countChanged
|
||||
|
||||
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) =
|
||||
if hasMore == self.hasMore:
|
||||
return
|
||||
self.hasMore = hasMore
|
||||
self.hasMoreChanged()
|
||||
|
||||
method canFetchMore*(self: Model, parent: QModelIndex): bool =
|
||||
return self.hasMore
|
||||
|
||||
proc loadMoreItems(self: Model) {.signal.}
|
||||
|
||||
proc loadMore*(self: Model) {.slot.} =
|
||||
self.loadMoreItems()
|
||||
|
||||
method fetchMore*(self: Model, parent: QModelIndex) =
|
||||
self.loadMore()
|
||||
|
||||
method rowCount*(self: Model, index: QModelIndex = nil): int =
|
||||
return self.getCount()
|
||||
|
||||
method roleNames(self: Model): Table[int, string] =
|
||||
{
|
||||
CollectionRole.Uid.int:"uid",
|
||||
CollectionRole.ChainId.int:"chainId",
|
||||
CollectionRole.ContractAddress.int:"contractAddress",
|
||||
CollectionRole.TokenType.int:"tokenType",
|
||||
CollectionRole.Name.int:"name",
|
||||
CollectionRole.Slug.int:"slug",
|
||||
CollectionRole.ImageUrl.int:"imageUrl"
|
||||
}.toTable
|
||||
|
||||
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
||||
if (not index.isValid):
|
||||
return
|
||||
|
||||
if (index.row < 0 or index.row >= self.getCount()):
|
||||
return
|
||||
|
||||
let enumRole = role.CollectionRole
|
||||
|
||||
if index.row < self.items.len:
|
||||
let item = self.items[index.row]
|
||||
case enumRole:
|
||||
of CollectionRole.Uid:
|
||||
result = newQVariant(item.getIDAsString())
|
||||
of CollectionRole.ChainId:
|
||||
result = newQVariant(item.getChainID())
|
||||
of CollectionRole.ContractAddress:
|
||||
result = newQVariant(item.getContractAddress())
|
||||
of CollectionRole.Name:
|
||||
result = newQVariant(item.getName())
|
||||
of CollectionRole.ImageUrl:
|
||||
result = newQVariant(item.getImageURL())
|
||||
of CollectionRole.Slug:
|
||||
result = newQVariant(item.getSlug())
|
||||
of CollectionRole.TokenType:
|
||||
result = newQVariant(item.getTokenType())
|
||||
|
||||
proc resetItems(self: Model, newItems: seq[CollectionsDataEntry] = @[]) =
|
||||
self.beginResetModel()
|
||||
self.items = newItems
|
||||
self.endResetModel()
|
||||
self.countChanged()
|
||||
|
||||
proc appendItems(self: Model, newItems: seq[CollectionsDataEntry]) =
|
||||
if len(newItems) == 0:
|
||||
return
|
||||
|
||||
let parentModelIndex = newQModelIndex()
|
||||
defer: parentModelIndex.delete
|
||||
|
||||
# Start after the current last real item
|
||||
let startIdx = self.items.len
|
||||
# End at the new last real item
|
||||
let endIdx = startIdx + newItems.len - 1
|
||||
|
||||
self.beginInsertRows(parentModelIndex, startIdx, endIdx)
|
||||
self.items.insert(newItems, startIdx)
|
||||
self.endInsertRows()
|
||||
self.countChanged()
|
||||
|
||||
proc removeItem(self: Model, idx: int) =
|
||||
if idx < 0 or idx >= self.items.len:
|
||||
return
|
||||
|
||||
let parentModelIndex = newQModelIndex()
|
||||
defer: parentModelIndex.delete
|
||||
|
||||
self.beginRemoveRows(parentModelIndex, idx, idx)
|
||||
self.items.delete(idx)
|
||||
self.endRemoveRows()
|
||||
self.countChanged()
|
||||
|
||||
proc updateCollectionItems(self: Model, newItems: seq[CollectionsDataEntry]) =
|
||||
if len(self.items) == 0:
|
||||
# Current list is empty, just replace with new list
|
||||
self.resetItems(newItems)
|
||||
return
|
||||
|
||||
if len(newItems) == 0:
|
||||
# New list is empty, just remove all items
|
||||
self.resetItems()
|
||||
return
|
||||
|
||||
var newTable = initTable[string, int](len(newItems))
|
||||
for i in 0 ..< len(newItems):
|
||||
newTable[newItems[i].getIDAsString()] = i
|
||||
|
||||
# Needs to be built in sequential index order
|
||||
var oldIndicesToRemove: seq[int] = @[]
|
||||
for idx in 0 ..< len(self.items):
|
||||
let uid = self.items[idx].getIDAsString()
|
||||
if not newTable.hasKey(uid):
|
||||
# Item in old list but not in new -> Must remove
|
||||
oldIndicesToRemove.add(idx)
|
||||
else:
|
||||
# Item both in old and new lists -> Nothing to do in the current list,
|
||||
# remove from the new list so it only holds new items.
|
||||
newTable.del(uid)
|
||||
|
||||
if len(oldIndicesToRemove) > 0:
|
||||
var removedItems = 0
|
||||
for idx in oldIndicesToRemove:
|
||||
let updatedIdx = idx - removedItems
|
||||
self.removeItem(updatedIdx)
|
||||
removedItems += 1
|
||||
self.countChanged()
|
||||
|
||||
var newItemsToAdd: seq[CollectionsDataEntry] = @[]
|
||||
for uid, idx in newTable:
|
||||
newItemsToAdd.add(newItems[idx])
|
||||
self.appendItems(newItemsToAdd)
|
||||
|
||||
proc getItems*(self: Model): seq[CollectionsDataEntry] =
|
||||
return self.items
|
||||
|
||||
proc getItemById*(self: Model, id: string): CollectionsDataEntry =
|
||||
for item in self.items:
|
||||
if(cmpIgnoreCase(item.getIDAsString(), id) == 0):
|
||||
return item
|
||||
return nil
|
||||
|
||||
proc setItems*(self: Model, newItems: seq[CollectionsDataEntry], offset: int, hasMore: bool) =
|
||||
if offset == 0:
|
||||
self.resetItems(newItems)
|
||||
elif offset != self.getCount():
|
||||
error "invalid offset"
|
||||
return
|
||||
else:
|
||||
self.appendItems(newItems)
|
||||
self.setHasMore(hasMore)
|
||||
|
||||
# Checks the diff between the current list and the new list, appends new items,
|
||||
# removes missing items.
|
||||
# We assume the order of the items in the input could change, and we don't care
|
||||
# about the order of the items in the model.
|
||||
proc updateItems*(self: Model, newItems: seq[CollectionsDataEntry]) =
|
||||
self.updateCollectionItems(newItems)
|
||||
self.setHasMore(false)
|
||||
|
||||
proc itemsDataUpdated(self: Model) {.signal.}
|
||||
proc updateItemsData*(self: Model, updates: seq[backend_collectibles.Collection]) =
|
||||
var anyUpdated = false
|
||||
for i in countdown(self.items.high, 0):
|
||||
let entry = self.items[i]
|
||||
for j in countdown(updates.high, 0):
|
||||
let update = updates[j]
|
||||
if entry.updateDataIfSameID(update):
|
||||
let index = self.createIndex(i, 0, nil)
|
||||
defer: index.delete
|
||||
self.dataChanged(index, index)
|
||||
anyUpdated = true
|
||||
break
|
||||
if anyUpdated:
|
||||
self.itemsDataUpdated()
|
|
@ -0,0 +1,218 @@
|
|||
import NimQml, std/json, sequtils, sugar, strutils
|
||||
import logging
|
||||
|
||||
import app/modules/shared_models/collectibles_data_entry
|
||||
import app/modules/shared_models/collectibles_data_model
|
||||
import events_handler
|
||||
|
||||
import app/core/eventemitter
|
||||
|
||||
import backend/collectibles as backend_collectibles
|
||||
import app_service/service/network/service as network_service
|
||||
|
||||
const FETCH_BATCH_COUNT_DEFAULT = 50
|
||||
|
||||
QtObject:
|
||||
type
|
||||
Controller* = ref object of QObject
|
||||
networkService: network_service.Service
|
||||
|
||||
model: Model
|
||||
fetchFromStart: bool
|
||||
|
||||
eventsHandler: EventsHandler
|
||||
|
||||
requestId: int32
|
||||
|
||||
chainId: int
|
||||
contractAddress: string
|
||||
text: string
|
||||
|
||||
isFetching: bool
|
||||
isError: bool
|
||||
|
||||
previousCursor: string
|
||||
nextCursor: string
|
||||
provider: string
|
||||
|
||||
dataType: backend_collectibles.CollectibleDataType
|
||||
|
||||
proc setup(self: Controller) =
|
||||
self.QObject.setup
|
||||
|
||||
proc delete*(self: Controller) =
|
||||
self.QObject.delete
|
||||
|
||||
proc mustFetchFromStart(self: Controller): bool =
|
||||
return self.previousCursor == ""
|
||||
|
||||
proc hasMore(self: Controller): bool =
|
||||
return self.mustFetchFromStart() or self.nextCursor != ""
|
||||
|
||||
proc getModel*(self: Controller): Model =
|
||||
return self.model
|
||||
|
||||
proc getModelAsVariant*(self: Controller): QVariant {.slot.} =
|
||||
return newQVariant(self.model)
|
||||
|
||||
QtProperty[QVariant] model:
|
||||
read = getModelAsVariant
|
||||
|
||||
proc isFetchingChanged(self: Controller) {.signal.}
|
||||
proc getIsFetching*(self: Controller): bool {.slot.} =
|
||||
self.isFetching
|
||||
QtProperty[bool] isFetching:
|
||||
read = getIsFetching
|
||||
notify = isFetchingChanged
|
||||
proc setIsFetching*(self: Controller, value: bool) =
|
||||
if value == self.isFetching:
|
||||
return
|
||||
self.isFetching = value
|
||||
self.isFetchingChanged()
|
||||
|
||||
proc isErrorChanged(self: Controller) {.signal.}
|
||||
proc getIsError*(self: Controller): bool {.slot.} =
|
||||
self.isError
|
||||
QtProperty[bool] isError:
|
||||
read = getIsError
|
||||
notify = isErrorChanged
|
||||
proc setIsError*(self: Controller, value: bool) =
|
||||
if value == self.isError:
|
||||
return
|
||||
self.isError = value
|
||||
self.isErrorChanged()
|
||||
|
||||
proc loadMoreItems(self: Controller) =
|
||||
if self.getIsFetching():
|
||||
return
|
||||
|
||||
if not self.hasMore():
|
||||
return
|
||||
|
||||
self.setIsFetching(true)
|
||||
self.setIsError(false)
|
||||
|
||||
let params = backend_collectibles.SearchCollectiblesParams(
|
||||
chainID: self.chainId,
|
||||
contractAddress: self.contractAddress,
|
||||
text: self.text,
|
||||
cursor: self.nextCursor,
|
||||
limit: FETCH_BATCH_COUNT_DEFAULT,
|
||||
providerID: self.provider
|
||||
)
|
||||
let response = backend_collectibles.searchCollectiblesAsync(self.requestId, params, self.dataType)
|
||||
if response.error != nil:
|
||||
self.setIsFetching(false)
|
||||
self.setIsError(true)
|
||||
error "error searching collections: ", response.error
|
||||
|
||||
proc resetModel(self: Controller) {.slot.} =
|
||||
self.model.setItems(@[], 0, true)
|
||||
|
||||
self.previousCursor = ""
|
||||
self.nextCursor = ""
|
||||
self.provider = ""
|
||||
|
||||
self.loadMoreItems()
|
||||
|
||||
proc onModelLoadMoreItems(self: Controller) {.slot.} =
|
||||
self.loadMoreItems()
|
||||
|
||||
proc textChanged(self: Controller) {.signal.}
|
||||
proc getText*(self: Controller): string {.slot.} =
|
||||
self.text
|
||||
|
||||
QtProperty[string] text:
|
||||
read = getText
|
||||
notify = textChanged
|
||||
|
||||
proc chainIdChanged(self: Controller) {.signal.}
|
||||
proc getChainId*(self: Controller): int {.slot.} =
|
||||
self.chainId
|
||||
|
||||
QtProperty[int] chainId:
|
||||
read = getChainId
|
||||
notify = chainIdChanged
|
||||
|
||||
proc contractAddressChanged(self: Controller) {.signal.}
|
||||
proc getContractAddress*(self: Controller): string {.slot.} =
|
||||
self.contractAddress
|
||||
|
||||
QtProperty[string] contractAddress:
|
||||
read = getContractAddress
|
||||
notify = contractAddressChanged
|
||||
|
||||
proc search*(self: Controller, chainId: int, contractAddress: string, text: string) {.slot.} =
|
||||
if chainId == self.chainId and contractAddress == self.contractAddress and text == self.text:
|
||||
return
|
||||
|
||||
self.chainId = chainId
|
||||
self.contractAddress = contractAddress
|
||||
self.text = text
|
||||
self.resetModel()
|
||||
|
||||
proc processSearchCollectiblesResponse(self: Controller, response: JsonNode) =
|
||||
let res = fromJson(response, backend_collectibles.SearchCollectiblesResponse)
|
||||
|
||||
let isError = res.errorCode != backend_collectibles.ErrorCodeSuccess
|
||||
|
||||
if isError:
|
||||
error "error fetching collectibles entries: ", res.errorCode
|
||||
self.setIsError(true)
|
||||
self.setIsFetching(false)
|
||||
return
|
||||
|
||||
if self.nextCursor != res.previousCursor:
|
||||
error "nextCursor mismatch"
|
||||
self.setIsError(true)
|
||||
self.setIsFetching(false)
|
||||
return
|
||||
|
||||
self.previousCursor = res.previousCursor
|
||||
self.nextCursor = res.nextCursor
|
||||
self.provider = res.provider
|
||||
|
||||
let items = res.collectibles.map(header => (block:
|
||||
newCollectiblesDataFullEntry(header)
|
||||
))
|
||||
self.model.setItems(items, self.model.getCount(), self.hasMore())
|
||||
self.setIsFetching(false)
|
||||
|
||||
proc setupEventHandlers(self: Controller) =
|
||||
self.eventsHandler.onSearchCollectiblesDone(proc (jsonObj: JsonNode) =
|
||||
self.processSearchCollectiblesResponse(jsonObj)
|
||||
)
|
||||
|
||||
proc newController*(
|
||||
requestId: int32,
|
||||
networkService: network_service.Service,
|
||||
events: EventEmitter,
|
||||
dataType: backend_collectibles.CollectibleDataType = backend_collectibles.CollectibleDataType.Details,
|
||||
): Controller =
|
||||
new(result, delete)
|
||||
|
||||
result.requestId = requestId
|
||||
result.dataType = dataType
|
||||
|
||||
result.networkService = networkService
|
||||
|
||||
result.model = newModel()
|
||||
|
||||
result.isFetching = false
|
||||
result.isError = false
|
||||
|
||||
result.chainId = 0
|
||||
result.contractAddress = ""
|
||||
result.text = ""
|
||||
|
||||
result.previousCursor = ""
|
||||
result.nextCursor = ""
|
||||
result.provider = ""
|
||||
|
||||
result.eventsHandler = newEventsHandler(result.requestId, events)
|
||||
|
||||
result.setup()
|
||||
|
||||
result.setupEventHandlers()
|
||||
|
||||
signalConnect(result.model, "loadMoreItems()", result, "onModelLoadMoreItems()")
|
|
@ -0,0 +1,54 @@
|
|||
import NimQml, std/json, sequtils, strutils, options
|
||||
import tables
|
||||
|
||||
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]
|
||||
|
||||
requestId: int32
|
||||
|
||||
proc setup(self: EventsHandler) =
|
||||
self.QObject.setup
|
||||
|
||||
proc delete*(self: EventsHandler) =
|
||||
self.QObject.delete
|
||||
|
||||
proc onSearchCollectiblesDone*(self: EventsHandler, handler: EventCallbackProc) =
|
||||
self.eventHandlers[backend_collectibles.eventSearchCollectiblesDone] = handler
|
||||
|
||||
proc handleApiEvents(self: EventsHandler, e: Args) =
|
||||
var data = WalletSignal(e)
|
||||
|
||||
if data.requestId.isSome and data.requestId.get() != self.requestId:
|
||||
return
|
||||
|
||||
if self.eventHandlers.hasKey(data.eventType):
|
||||
let callback = self.eventHandlers[data.eventType]
|
||||
let responseJson = parseJson(data.message)
|
||||
callback(responseJson)
|
||||
|
||||
proc newEventsHandler*(requestId: int32, events: EventEmitter): EventsHandler =
|
||||
new(result, delete)
|
||||
|
||||
result.requestId = requestId
|
||||
|
||||
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)
|
||||
)
|
|
@ -0,0 +1,206 @@
|
|||
import NimQml, std/json, sequtils, sugar, strutils
|
||||
import logging
|
||||
|
||||
import app/modules/shared_models/collections_data_entry
|
||||
import app/modules/shared_models/collections_data_model
|
||||
import events_handler
|
||||
|
||||
import app/core/eventemitter
|
||||
|
||||
import backend/collectibles as backend_collectibles
|
||||
import app_service/service/network/service as network_service
|
||||
|
||||
const FETCH_BATCH_COUNT_DEFAULT = 50
|
||||
|
||||
QtObject:
|
||||
type
|
||||
Controller* = ref object of QObject
|
||||
networkService: network_service.Service
|
||||
|
||||
model: Model
|
||||
fetchFromStart: bool
|
||||
|
||||
eventsHandler: EventsHandler
|
||||
|
||||
requestId: int32
|
||||
|
||||
chainId: int
|
||||
text: string
|
||||
|
||||
isFetching: bool
|
||||
isError: bool
|
||||
|
||||
previousCursor: string
|
||||
nextCursor: string
|
||||
provider: string
|
||||
|
||||
dataType: backend_collectibles.CollectionDataType
|
||||
|
||||
proc setup(self: Controller) =
|
||||
self.QObject.setup
|
||||
|
||||
proc delete*(self: Controller) =
|
||||
self.QObject.delete
|
||||
|
||||
proc mustFetchFromStart(self: Controller): bool =
|
||||
return self.previousCursor == ""
|
||||
|
||||
proc hasMore(self: Controller): bool =
|
||||
return self.mustFetchFromStart() or self.nextCursor != ""
|
||||
|
||||
proc getModel*(self: Controller): Model =
|
||||
return self.model
|
||||
|
||||
proc getModelAsVariant*(self: Controller): QVariant {.slot.} =
|
||||
return newQVariant(self.model)
|
||||
|
||||
QtProperty[QVariant] model:
|
||||
read = getModelAsVariant
|
||||
|
||||
proc isFetchingChanged(self: Controller) {.signal.}
|
||||
proc getIsFetching*(self: Controller): bool {.slot.} =
|
||||
self.isFetching
|
||||
QtProperty[bool] isFetching:
|
||||
read = getIsFetching
|
||||
notify = isFetchingChanged
|
||||
proc setIsFetching*(self: Controller, value: bool) =
|
||||
if value == self.isFetching:
|
||||
return
|
||||
self.isFetching = value
|
||||
self.isFetchingChanged()
|
||||
|
||||
proc isErrorChanged(self: Controller) {.signal.}
|
||||
proc getIsError*(self: Controller): bool {.slot.} =
|
||||
self.isError
|
||||
QtProperty[bool] isError:
|
||||
read = getIsError
|
||||
notify = isErrorChanged
|
||||
proc setIsError*(self: Controller, value: bool) =
|
||||
if value == self.isError:
|
||||
return
|
||||
self.isError = value
|
||||
self.isErrorChanged()
|
||||
|
||||
proc loadMoreItems(self: Controller) =
|
||||
if self.getIsFetching():
|
||||
return
|
||||
|
||||
if not self.hasMore():
|
||||
return
|
||||
|
||||
self.setIsFetching(true)
|
||||
self.setIsError(false)
|
||||
|
||||
let params = backend_collectibles.SearchCollectionsParams(
|
||||
chainID: self.chainId,
|
||||
text: self.text,
|
||||
cursor: self.nextCursor,
|
||||
limit: FETCH_BATCH_COUNT_DEFAULT,
|
||||
providerID: self.provider
|
||||
)
|
||||
let response = backend_collectibles.searchCollectionsAsync(self.requestId, params, self.dataType)
|
||||
if response.error != nil:
|
||||
self.setIsFetching(false)
|
||||
self.setIsError(true)
|
||||
error "error searching collections: ", response.error
|
||||
|
||||
proc resetModel(self: Controller) {.slot.} =
|
||||
self.model.setItems(@[], 0, true)
|
||||
|
||||
self.previousCursor = ""
|
||||
self.nextCursor = ""
|
||||
self.provider = ""
|
||||
|
||||
self.loadMoreItems()
|
||||
|
||||
proc textChanged(self: Controller) {.signal.}
|
||||
proc getText*(self: Controller): string {.slot.} =
|
||||
self.text
|
||||
|
||||
QtProperty[string] text:
|
||||
read = getText
|
||||
notify = textChanged
|
||||
|
||||
proc chainIdChanged(self: Controller) {.signal.}
|
||||
proc getChainId*(self: Controller): int {.slot.} =
|
||||
self.chainId
|
||||
|
||||
QtProperty[int] chainId:
|
||||
read = getChainId
|
||||
notify = chainIdChanged
|
||||
|
||||
proc search*(self: Controller, chainId: int, text: string) {.slot.} =
|
||||
if chainId == self.chainId and text == self.text:
|
||||
return
|
||||
|
||||
self.chainId = chainId
|
||||
self.text = text
|
||||
self.resetModel()
|
||||
|
||||
proc onModelLoadMoreItems(self: Controller) {.slot.} =
|
||||
self.loadMoreItems()
|
||||
|
||||
proc processSearchCollectionsResponse(self: Controller, response: JsonNode) =
|
||||
let res = fromJson(response, backend_collectibles.SearchCollectionsResponse)
|
||||
|
||||
let isError = res.errorCode != backend_collectibles.ErrorCodeSuccess
|
||||
|
||||
if isError:
|
||||
error "error fetching collections entries: ", res.errorCode
|
||||
self.setIsError(true)
|
||||
self.setIsFetching(false)
|
||||
return
|
||||
|
||||
if self.nextCursor != res.previousCursor:
|
||||
error "nextCursor mismatch"
|
||||
self.setIsError(true)
|
||||
self.setIsFetching(false)
|
||||
return
|
||||
|
||||
self.previousCursor = res.previousCursor
|
||||
self.nextCursor = res.nextCursor
|
||||
self.provider = res.provider
|
||||
|
||||
let items = res.collections.map(data => (block:
|
||||
newCollectionsDataFullEntry(data)
|
||||
))
|
||||
self.model.setItems(items, self.model.getCount(), self.hasMore())
|
||||
self.setIsFetching(false)
|
||||
|
||||
proc setupEventHandlers(self: Controller) =
|
||||
self.eventsHandler.onSearchCollectionsDone(proc (jsonObj: JsonNode) =
|
||||
self.processSearchCollectionsResponse(jsonObj)
|
||||
)
|
||||
|
||||
proc newController*(
|
||||
requestId: int32,
|
||||
networkService: network_service.Service,
|
||||
events: EventEmitter,
|
||||
dataType: backend_collectibles.CollectionDataType = backend_collectibles.CollectionDataType.Details,
|
||||
): Controller =
|
||||
new(result, delete)
|
||||
|
||||
result.requestId = requestId
|
||||
result.dataType = dataType
|
||||
|
||||
result.networkService = networkService
|
||||
|
||||
result.model = newModel()
|
||||
|
||||
result.isFetching = false
|
||||
result.isError = false
|
||||
|
||||
result.chainId = 0
|
||||
result.text = ""
|
||||
|
||||
result.previousCursor = ""
|
||||
result.nextCursor = ""
|
||||
result.provider = ""
|
||||
|
||||
result.eventsHandler = newEventsHandler(result.requestId, events)
|
||||
|
||||
result.setup()
|
||||
|
||||
result.setupEventHandlers()
|
||||
|
||||
signalConnect(result.model, "loadMoreItems()", result, "onModelLoadMoreItems()")
|
|
@ -0,0 +1,54 @@
|
|||
import NimQml, std/json, sequtils, strutils, options
|
||||
import tables
|
||||
|
||||
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]
|
||||
|
||||
requestId: int32
|
||||
|
||||
proc setup(self: EventsHandler) =
|
||||
self.QObject.setup
|
||||
|
||||
proc delete*(self: EventsHandler) =
|
||||
self.QObject.delete
|
||||
|
||||
proc onSearchCollectionsDone*(self: EventsHandler, handler: EventCallbackProc) =
|
||||
self.eventHandlers[backend_collectibles.eventSearchCollectionsDone] = handler
|
||||
|
||||
proc handleApiEvents(self: EventsHandler, e: Args) =
|
||||
var data = WalletSignal(e)
|
||||
|
||||
if data.requestId.isSome and data.requestId.get() != self.requestId:
|
||||
return
|
||||
|
||||
if self.eventHandlers.hasKey(data.eventType):
|
||||
let callback = self.eventHandlers[data.eventType]
|
||||
let responseJson = parseJson(data.message)
|
||||
callback(responseJson)
|
||||
|
||||
proc newEventsHandler*(requestId: int32, events: EventEmitter): EventsHandler =
|
||||
new(result, delete)
|
||||
|
||||
result.requestId = requestId
|
||||
|
||||
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)
|
||||
)
|
|
@ -14,6 +14,7 @@ type
|
|||
ProfileShowcase
|
||||
WalletSend
|
||||
AllCollectibles
|
||||
Search
|
||||
|
||||
# Declared in services/wallet/collectibles/service.go
|
||||
const eventCollectiblesOwnershipUpdateStarted*: string = "wallet-collectibles-ownership-update-started"
|
||||
|
@ -26,6 +27,8 @@ const eventCollectiblesDataUpdated*: string = "wallet-collectibles-data-updated"
|
|||
const eventOwnedCollectiblesFilteringDone*: string = "wallet-owned-collectibles-filtering-done"
|
||||
const eventGetCollectiblesDetailsDone*: string = "wallet-get-collectibles-details-done"
|
||||
const eventGetCollectionSocialsDone*: string ="wallet-get-collection-socials-done"
|
||||
const eventSearchCollectiblesDone*: string ="wallet-search-collectibles-done"
|
||||
const eventSearchCollectionsDone*: string ="wallet-search-collections-done"
|
||||
|
||||
const invalidTimestamp*: int = -1
|
||||
|
||||
|
@ -67,6 +70,22 @@ type
|
|||
collectibles*: seq[Collectible]
|
||||
errorCode*: ErrorCode
|
||||
|
||||
# Mirrors services/wallet/collectibles/service.go SearchCollectiblesResponse
|
||||
SearchCollectiblesResponse* = object
|
||||
collectibles*: seq[Collectible]
|
||||
nextCursor*: string
|
||||
previousCursor*: string
|
||||
provider*: string
|
||||
errorCode*: ErrorCode
|
||||
|
||||
# Mirrors services/wallet/collectibles/service.go SearchCollectionsResponse
|
||||
SearchCollectionsResponse* = object
|
||||
collections*: seq[Collection]
|
||||
nextCursor*: string
|
||||
previousCursor*: string
|
||||
provider*: string
|
||||
errorCode*: ErrorCode
|
||||
|
||||
CommunityCollectiblesReceivedPayload* = object
|
||||
collectibles*: seq[Collectible]
|
||||
|
||||
|
@ -90,6 +109,23 @@ type
|
|||
FetchCriteria* = object
|
||||
fetchType*: FetchType
|
||||
maxCacheAgeSeconds*: int
|
||||
|
||||
# see status-go/services/wallet/collectibles/manager.go SearchCollectionsParams
|
||||
SearchCollectionsParams* = object
|
||||
chainID*: int
|
||||
text*: string
|
||||
cursor*: string
|
||||
limit*: int
|
||||
providerID*: string
|
||||
|
||||
# see status-go/services/wallet/collectibles/manager.go SearchCollectiblesParams
|
||||
SearchCollectiblesParams* = object
|
||||
chainID*: int
|
||||
contractAddress*: string
|
||||
text*: string
|
||||
cursor*: string
|
||||
limit*: int
|
||||
providerID*: string
|
||||
|
||||
# CollectibleOwnershipState
|
||||
proc `$`*(self: OwnershipStatus): string =
|
||||
|
@ -176,6 +212,13 @@ proc `%`*(t: CollectibleDataType): JsonNode {.inline.} =
|
|||
proc `%`*(t: ref CollectibleDataType): JsonNode {.inline.} =
|
||||
return %(t[])
|
||||
|
||||
# CollectionDataType
|
||||
proc `%`*(t: CollectionDataType): JsonNode {.inline.} =
|
||||
result = %(t.int)
|
||||
|
||||
proc `%`*(t: ref CollectionDataType): JsonNode {.inline.} =
|
||||
return %(t[])
|
||||
|
||||
# FetchCriteria
|
||||
proc `$`*(self: FetchCriteria): string =
|
||||
return fmt"""FetchCriteria(
|
||||
|
@ -191,6 +234,47 @@ proc `%`*(t: FetchCriteria): JsonNode {.inline.} =
|
|||
proc `%`*(t: ref FetchCriteria): JsonNode {.inline.} =
|
||||
return %(t[])
|
||||
|
||||
#SearchCollectionsParams
|
||||
proc `$`*(self: SearchCollectionsParams): string =
|
||||
return fmt"""SearchCollectionsParams(
|
||||
chainID:{self.chainID},
|
||||
text:{self.text},
|
||||
cursor:{self.cursor},
|
||||
limit:{self.limit},
|
||||
providerID:{self.providerID}
|
||||
"""
|
||||
|
||||
proc `%`*(t: SearchCollectionsParams): JsonNode {.inline.} =
|
||||
result = newJObject()
|
||||
result["chain_id"] = %t.chainID
|
||||
result["text"] = %t.text
|
||||
result["cursor"] = %t.cursor
|
||||
result["limit"] = %t.limit
|
||||
result["provider_id"] = %t.providerID
|
||||
|
||||
proc `%`*(t: ref SearchCollectionsParams): JsonNode {.inline.} =
|
||||
return %(t[])
|
||||
|
||||
#SearchCollectiblesParams
|
||||
proc `$`*(self: SearchCollectiblesParams): string =
|
||||
return fmt"""SearchCollectiblesParams(
|
||||
chainID:{self.chainID},
|
||||
contractAddress:{self.contractAddress},
|
||||
text:{self.text},
|
||||
cursor:{self.cursor},
|
||||
limit:{self.limit},
|
||||
providerID:{self.providerID}
|
||||
"""
|
||||
|
||||
proc `%`*(t: SearchCollectiblesParams): JsonNode {.inline.} =
|
||||
result = newJObject()
|
||||
result["chain_id"] = %t.chainID
|
||||
result["contract_address"] = %t.contractAddress
|
||||
result["text"] = %t.text
|
||||
result["cursor"] = %t.cursor
|
||||
result["limit"] = %t.limit
|
||||
result["provider_id"] = %t.providerID
|
||||
|
||||
# Responses
|
||||
proc fromJson*(e: JsonNode, T: typedesc[GetOwnedCollectiblesResponse]): GetOwnedCollectiblesResponse {.inline.} =
|
||||
var collectibles: seq[Collectible]
|
||||
|
@ -228,6 +312,32 @@ proc fromJson*(e: JsonNode, T: typedesc[GetCollectiblesByUniqueIDResponse]): Get
|
|||
errorCode: ErrorCode(e["errorCode"].getInt())
|
||||
)
|
||||
|
||||
proc fromJson*(e: JsonNode, T: typedesc[SearchCollectiblesResponse]): SearchCollectiblesResponse {.inline.} =
|
||||
var collectibles: seq[Collectible] = @[]
|
||||
for item in e["collectibles"].getElems():
|
||||
collectibles.add(fromJson(item, Collectible))
|
||||
|
||||
result = T(
|
||||
collectibles: collectibles,
|
||||
nextCursor: e["nextCursor"].getStr(),
|
||||
previousCursor: e["previousCursor"].getStr(),
|
||||
provider: e["provider"].getStr(),
|
||||
errorCode: ErrorCode(e["errorCode"].getInt())
|
||||
)
|
||||
|
||||
proc fromJson*(e: JsonNode, T: typedesc[SearchCollectionsResponse]): SearchCollectionsResponse {.inline.} =
|
||||
var collections: seq[Collection] = @[]
|
||||
for item in e["collections"].getElems():
|
||||
collections.add(fromJson(item, Collection))
|
||||
|
||||
result = T(
|
||||
collections: collections,
|
||||
nextCursor: e["nextCursor"].getStr(),
|
||||
previousCursor: e["previousCursor"].getStr(),
|
||||
provider: e["provider"].getStr(),
|
||||
errorCode: ErrorCode(e["errorCode"].getInt())
|
||||
)
|
||||
|
||||
proc fromJson*(e: JsonNode, T: typedesc[CommunityCollectiblesReceivedPayload]): CommunityCollectiblesReceivedPayload {.inline.} =
|
||||
var collectibles: seq[Collectible] = @[]
|
||||
for item in e.getElems():
|
||||
|
@ -278,6 +388,16 @@ rpc(fetchCollectionSocialsAsync, "wallet"):
|
|||
rpc(refetchOwnedCollectibles, "wallet"):
|
||||
discard
|
||||
|
||||
rpc(searchCollectionsAsync, "wallet"):
|
||||
requestId: int32
|
||||
params: SearchCollectionsParams
|
||||
dataType: CollectionDataType
|
||||
|
||||
rpc(searchCollectiblesAsync, "wallet"):
|
||||
requestId: int32
|
||||
params: SearchCollectiblesParams
|
||||
dataType: CollectibleDataType
|
||||
|
||||
rpc(updateCollectiblePreferences, "accounts"):
|
||||
preferences: seq[CollectiblePreferences]
|
||||
|
||||
|
|
|
@ -22,10 +22,14 @@ type
|
|||
contractID*: ContractID
|
||||
tokenID*: UInt256
|
||||
|
||||
# see status-go/services/wallet/collectibles/service.go CollectibleDataType
|
||||
# see status-go/services/wallet/collectibles/types.go CollectibleDataType
|
||||
CollectibleDataType* {.pure.} = enum
|
||||
UniqueID, Header, Details, CommunityHeader
|
||||
|
||||
# see status-go/services/wallet/collectibles/types.go CollectionDataType
|
||||
CollectionDataType* {.pure.} = enum
|
||||
ContractID, Details
|
||||
|
||||
# Mirrors services/wallet/thirdparty/collectible_types.go CollectibleTrait
|
||||
CollectibleTrait* = ref object of RootObj
|
||||
trait_type*: string
|
||||
|
@ -73,6 +77,13 @@ type
|
|||
latestTxHash*: Option[string]
|
||||
receivedAmount*: Option[float64]
|
||||
contractType*: Option[ContractType]
|
||||
|
||||
Collection* = ref object of RootObj
|
||||
dataType*: CollectionDataType
|
||||
id* : ContractID
|
||||
communityId*: string
|
||||
contractType*: ContractType
|
||||
collectionData*: Option[CollectionData]
|
||||
|
||||
CollectionSocials* = ref object of RootObj
|
||||
website*: string
|
||||
|
@ -409,6 +420,33 @@ proc toIds(self: seq[Collectible]): seq[CollectibleUniqueID] =
|
|||
for c in self:
|
||||
result.add(c.id)
|
||||
|
||||
# Collection
|
||||
proc `$`*(self: Collection): string =
|
||||
return fmt"""Collection(
|
||||
dataType:{self.dataType},
|
||||
id:{self.id},
|
||||
contractType:{self.contractType},
|
||||
collectionData:{self.collectionData},
|
||||
communityId:{self.communityId}
|
||||
)"""
|
||||
|
||||
proc fromJson*(t: JsonNode, T: typedesc[Collection]): Collection {.inline.} =
|
||||
result = Collection()
|
||||
result.dataType = t["data_type"].getInt().CollectionDataType
|
||||
result.id = fromJson(t["id"], ContractID)
|
||||
let collectionDataNode = t{"collection_data"}
|
||||
if collectionDataNode != nil and collectionDataNode.kind != JNull:
|
||||
result.collectionData = some(fromJson(collectionDataNode, CollectionData))
|
||||
else:
|
||||
result.collectionData = none(CollectionData)
|
||||
result.communityId = t["community_id"].getStr
|
||||
result.contractType = ContractType(t["contract_type"].getInt())
|
||||
|
||||
proc toIds(self: seq[Collection]): seq[ContractID] =
|
||||
result = @[]
|
||||
for c in self:
|
||||
result.add(c.id)
|
||||
|
||||
# CollectibleBalance
|
||||
proc `$`*(self: CollectibleBalance): string =
|
||||
return fmt"""CollectibleBalance(
|
||||
|
|
|
@ -12,6 +12,7 @@ QtObject {
|
|||
|
||||
/* PRIVATE: Modules used to get data from backend */
|
||||
readonly property var _allCollectiblesModule: !!walletSectionAllCollectibles ? walletSectionAllCollectibles : null
|
||||
readonly property var _collectiblesSearchModule : !!walletSectionCollectiblesSearch ? walletSectionCollectiblesSearch : null
|
||||
|
||||
/* This list contains the complete list of collectibles with separate
|
||||
entry per collectible which has a unique [network + contractAddress + tokenID] */
|
||||
|
@ -103,4 +104,26 @@ QtObject {
|
|||
function getDetailedCollectible(chainId, contractAddress, tokenId) {
|
||||
walletSection.collectibleDetailsController.getDetailedCollectible(chainId, contractAddress, tokenId)
|
||||
}
|
||||
|
||||
/* The following are used to search for collections */
|
||||
readonly property var _collectionsSearchController: !!root._collectiblesSearchModule ? root._collectiblesSearchModule.collectionsSearchController : null
|
||||
function searchCollections(chainId, text) {
|
||||
root._collectionsSearchController.search(chainId, text)
|
||||
}
|
||||
|
||||
/* The following are used to display the collection search results */
|
||||
readonly property var collectionsSearchResults: !!root._collectionsSearchController ? root._collectionsSearchController.model : null
|
||||
readonly property bool areCollectionsSearchResultsFetching: !!root._collectionsSearchController ? root._collectionsSearchController.isFetching : true
|
||||
readonly property bool areCollectionsSearchResultsError: !!root._collectionsSearchController ? root._collectionsSearchController.isError : false
|
||||
|
||||
/* The following are used to search for collectibles */
|
||||
readonly property var _collectiblesSearchController: !!root._collectiblesSearchModule ? root._collectiblesSearchModule.collectiblesSearchController : null
|
||||
function searchCollectibles(chainId, contractAddress, text) {
|
||||
root._collectiblesSearchController.search(chainId, contractAddress, text)
|
||||
}
|
||||
|
||||
/* The following are used to display the collection search results */
|
||||
readonly property var collectiblesSearchResults: !!root._collectiblesSearchController ? root._collectiblesSearchController.model : null
|
||||
readonly property bool areCollectiblesSearchResultsFetching: !!root._collectiblesSearchController ? root._collectiblesSearchController.isFetching : true
|
||||
readonly property bool areCollectiblesSearchResultsError: !!root._collectiblesSearchController ? root._collectiblesSearchController.isError : false
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 10cf2862086630625434f8b2239227dee7440858
|
||||
Subproject commit 7d7af6249176ea32bd458c67bca4010444df6389
|
Loading…
Reference in New Issue