@bug(wallet/activity): Implemented collectibles model (#12294)
This commit is contained in:
parent
c155c9c9d0
commit
158bb87b4a
|
@ -0,0 +1,61 @@
|
||||||
|
import strformat, stint
|
||||||
|
import backend/activity as backend
|
||||||
|
|
||||||
|
type
|
||||||
|
CollectibleItem* = object
|
||||||
|
chainId: int
|
||||||
|
contractAddress: string
|
||||||
|
tokenId: UInt256
|
||||||
|
name: string
|
||||||
|
imageUrl: string
|
||||||
|
|
||||||
|
proc initItem*(
|
||||||
|
chainId: int,
|
||||||
|
contractAddress: string,
|
||||||
|
tokenId: UInt256,
|
||||||
|
name: string,
|
||||||
|
imageUrl: string
|
||||||
|
): CollectibleItem =
|
||||||
|
result.chainId = chainId
|
||||||
|
result.contractAddress = contractAddress
|
||||||
|
result.tokenId = tokenId
|
||||||
|
result.name = if (name != ""): name else: ("#" & tokenId.toString())
|
||||||
|
result.imageUrl = imageUrl
|
||||||
|
|
||||||
|
proc collectibleToItem*(c: backend.CollectibleHeader) : CollectibleItem =
|
||||||
|
return initItem(
|
||||||
|
c.id.contractID.chainID,
|
||||||
|
c.id.contractID.address,
|
||||||
|
c.id.tokenID,
|
||||||
|
c.name,
|
||||||
|
c.imageUrl
|
||||||
|
)
|
||||||
|
|
||||||
|
proc `$`*(self: CollectibleItem): string =
|
||||||
|
result = fmt"""Collectibles(
|
||||||
|
chainId: {self.chainId},
|
||||||
|
contractAddress: {self.contractAddress},
|
||||||
|
tokenId: {self.tokenId},
|
||||||
|
name: {self.name},
|
||||||
|
imageUrl: {self.imageUrl}
|
||||||
|
]"""
|
||||||
|
|
||||||
|
proc getChainId*(self: CollectibleItem): int =
|
||||||
|
return self.chainId
|
||||||
|
|
||||||
|
proc getContractAddress*(self: CollectibleItem): string =
|
||||||
|
return self.contractAddress
|
||||||
|
|
||||||
|
proc getTokenId*(self: CollectibleItem): UInt256 =
|
||||||
|
return self.tokenId
|
||||||
|
|
||||||
|
# Unique ID to identify collectible, generated by us
|
||||||
|
proc getId*(self: CollectibleItem): string =
|
||||||
|
return fmt"{self.getChainId}+{self.getContractAddress}+{self.getTokenID}"
|
||||||
|
|
||||||
|
proc getName*(self: CollectibleItem): string =
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
proc getImageUrl*(self: CollectibleItem): string =
|
||||||
|
return self.imageUrl
|
||||||
|
|
|
@ -0,0 +1,219 @@
|
||||||
|
import NimQml, Tables, strutils, strformat, sequtils, stint
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import ./collectibles_item
|
||||||
|
import web3/ethtypes as eth
|
||||||
|
import backend/activity as backend_activity
|
||||||
|
|
||||||
|
type
|
||||||
|
CollectibleRole* {.pure.} = enum
|
||||||
|
Uid = UserRole + 1,
|
||||||
|
ChainId
|
||||||
|
ContractAddress
|
||||||
|
TokenId
|
||||||
|
Name
|
||||||
|
ImageUrl
|
||||||
|
|
||||||
|
QtObject:
|
||||||
|
type
|
||||||
|
CollectiblesModel* = ref object of QAbstractListModel
|
||||||
|
items: seq[CollectibleItem]
|
||||||
|
hasMore: bool
|
||||||
|
|
||||||
|
proc delete(self: CollectiblesModel) =
|
||||||
|
self.items = @[]
|
||||||
|
self.QAbstractListModel.delete
|
||||||
|
|
||||||
|
proc setup(self: CollectiblesModel) =
|
||||||
|
self.QAbstractListModel.setup
|
||||||
|
|
||||||
|
proc newCollectiblesModel*(): CollectiblesModel =
|
||||||
|
new(result, delete)
|
||||||
|
result.setup
|
||||||
|
result.items = @[]
|
||||||
|
result.hasMore = true
|
||||||
|
|
||||||
|
proc `$`*(self: CollectiblesModel): string =
|
||||||
|
for i in 0 ..< self.items.len:
|
||||||
|
result &= fmt"""[{i}]:({$self.items[i]})"""
|
||||||
|
|
||||||
|
proc getCollectiblesCount*(self: CollectiblesModel): int =
|
||||||
|
return self.items.len
|
||||||
|
|
||||||
|
proc countChanged(self: CollectiblesModel) {.signal.}
|
||||||
|
proc getCount*(self: CollectiblesModel): int {.slot.} =
|
||||||
|
return self.items.len
|
||||||
|
|
||||||
|
QtProperty[int] count:
|
||||||
|
read = getCount
|
||||||
|
notify = countChanged
|
||||||
|
|
||||||
|
proc hasMoreChanged*(self: CollectiblesModel) {.signal.}
|
||||||
|
proc getHasMore*(self: CollectiblesModel): bool {.slot.} =
|
||||||
|
self.hasMore
|
||||||
|
QtProperty[bool] hasMore:
|
||||||
|
read = getHasMore
|
||||||
|
notify = hasMoreChanged
|
||||||
|
|
||||||
|
proc setHasMore(self: CollectiblesModel, hasMore: bool) =
|
||||||
|
if hasMore == self.hasMore:
|
||||||
|
return
|
||||||
|
self.hasMore = hasMore
|
||||||
|
self.hasMoreChanged()
|
||||||
|
|
||||||
|
method canFetchMore*(self: CollectiblesModel, parent: QModelIndex): bool =
|
||||||
|
return self.hasMore
|
||||||
|
|
||||||
|
proc loadMoreItems(self: CollectiblesModel) {.signal.}
|
||||||
|
|
||||||
|
proc loadMore*(self: CollectiblesModel) {.slot.} =
|
||||||
|
self.loadMoreItems()
|
||||||
|
|
||||||
|
method fetchMore*(self: CollectiblesModel, parent: QModelIndex) =
|
||||||
|
self.loadMore()
|
||||||
|
|
||||||
|
method rowCount*(self: CollectiblesModel, index: QModelIndex = nil): int =
|
||||||
|
return self.getCount()
|
||||||
|
|
||||||
|
method roleNames(self: CollectiblesModel): Table[int, string] =
|
||||||
|
{
|
||||||
|
CollectibleRole.Uid.int:"uid",
|
||||||
|
CollectibleRole.ChainId.int:"chainId",
|
||||||
|
CollectibleRole.ContractAddress.int:"contractAddress",
|
||||||
|
CollectibleRole.TokenId.int:"tokenId",
|
||||||
|
CollectibleRole.Name.int:"name",
|
||||||
|
CollectibleRole.ImageUrl.int:"imageUrl",
|
||||||
|
}.toTable
|
||||||
|
|
||||||
|
method data(self: CollectiblesModel, 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.getId())
|
||||||
|
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:
|
||||||
|
result = newQVariant(item.getName())
|
||||||
|
of CollectibleRole.ImageUrl:
|
||||||
|
result = newQVariant(item.getImageUrl())
|
||||||
|
else:
|
||||||
|
error "Invalid role for loading item"
|
||||||
|
result = newQVariant()
|
||||||
|
|
||||||
|
proc rowData(self: CollectiblesModel, index: int, column: string): string {.slot.} =
|
||||||
|
if (index >= self.items.len):
|
||||||
|
return
|
||||||
|
let item = self.items[index]
|
||||||
|
case column:
|
||||||
|
of "uid": result = item.getId()
|
||||||
|
of "chainId": result = $item.getChainId()
|
||||||
|
of "contractAddress": result = item.getContractAddress()
|
||||||
|
of "tokenId": result = item.getTokenId().toString()
|
||||||
|
of "name": result = item.getName()
|
||||||
|
of "imageUrl": result = item.getImageUrl()
|
||||||
|
|
||||||
|
proc appendCollectibleItems(self: CollectiblesModel, newItems: seq[CollectibleItem]) =
|
||||||
|
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 removeCollectibleItems(self: CollectiblesModel) =
|
||||||
|
if self.items.len <= 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
let parentModelIndex = newQModelIndex()
|
||||||
|
defer: parentModelIndex.delete
|
||||||
|
|
||||||
|
# Start from the beginning
|
||||||
|
let startIdx = 0
|
||||||
|
# End at the last real item
|
||||||
|
let endIdx = startIdx + self.items.len - 1
|
||||||
|
|
||||||
|
self.beginRemoveRows(parentModelIndex, startIdx, endIdx)
|
||||||
|
self.items = @[]
|
||||||
|
self.endRemoveRows()
|
||||||
|
self.countChanged()
|
||||||
|
|
||||||
|
proc getItems*(self: CollectiblesModel): seq[CollectibleItem] =
|
||||||
|
return self.items
|
||||||
|
|
||||||
|
proc setItems*(self: CollectiblesModel, newItems: seq[CollectibleItem], offset: int, hasMore: bool) =
|
||||||
|
if offset == 0:
|
||||||
|
self.removeCollectibleItems()
|
||||||
|
elif offset != self.getCollectiblesCount():
|
||||||
|
error "invalid offset"
|
||||||
|
return
|
||||||
|
|
||||||
|
self.appendCollectibleItems(newItems)
|
||||||
|
self.setHasMore(hasMore)
|
||||||
|
|
||||||
|
proc getImageUrl*(self: CollectiblesModel, id: string): string {.slot.} =
|
||||||
|
for item in self.items:
|
||||||
|
if(cmpIgnoreCase(item.getId(), id) == 0):
|
||||||
|
return item.getImageUrl()
|
||||||
|
return ""
|
||||||
|
|
||||||
|
proc getName*(self: CollectiblesModel, id: string): string {.slot.} =
|
||||||
|
for item in self.items:
|
||||||
|
if(cmpIgnoreCase(item.getId(), id) == 0):
|
||||||
|
return item.getName()
|
||||||
|
return ""
|
||||||
|
|
||||||
|
proc getActivityToken*(self: CollectiblesModel, id: string): backend_activity.Token =
|
||||||
|
for item in self.items:
|
||||||
|
if(cmpIgnoreCase(item.getId(), id) == 0):
|
||||||
|
result.tokenType = backend_activity.TokenType.Erc721
|
||||||
|
result.chainId = backend_activity.ChainId(item.getChainId())
|
||||||
|
var contract = item.getContractAddress()
|
||||||
|
if len(contract) > 0:
|
||||||
|
var address: eth.Address
|
||||||
|
address = eth.fromHex(eth.Address, contract)
|
||||||
|
result.address = some(address)
|
||||||
|
var tokenId = item.getTokenId()
|
||||||
|
if tokenId > 0:
|
||||||
|
result.tokenId = some(backend_activity.TokenId("0x" & stint.toHex(tokenId)))
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Fallback, use data from id
|
||||||
|
var parts = id.split("+")
|
||||||
|
if len(parts) == 3:
|
||||||
|
result.chainId = backend_activity.ChainId(parseInt(parts[0]))
|
||||||
|
result.address = some(eth.fromHex(eth.Address, parts[1]))
|
||||||
|
var tokenIdInt = u256(parseInt(parts[2]))
|
||||||
|
result.tokenId = some(backend_activity.TokenId("0x" & stint.toHex(tokenIdInt)))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
proc getUidForData*(self: CollectiblesModel, tokenId: string, tokenAddress: string, chainId: int): string {.slot.} =
|
||||||
|
for item in self.items:
|
||||||
|
if(cmpIgnoreCase(item.getTokenId().toString(), tokenId) == 0 and cmpIgnoreCase(item.getContractAddress(), tokenAddress) == 0):
|
||||||
|
return item.getId()
|
||||||
|
# Fallback, create uid from data, because it still might not be fetched
|
||||||
|
if chainId > 0 and len(tokenAddress) > 0 and len(tokenId) > 0:
|
||||||
|
return $chainId & "+" & tokenAddress & "+" & tokenId
|
||||||
|
return ""
|
|
@ -5,6 +5,8 @@ import model
|
||||||
import entry
|
import entry
|
||||||
import entry_details
|
import entry_details
|
||||||
import recipients_model
|
import recipients_model
|
||||||
|
import collectibles_model
|
||||||
|
import collectibles_item
|
||||||
import events_handler
|
import events_handler
|
||||||
import status
|
import status
|
||||||
|
|
||||||
|
@ -29,6 +31,7 @@ proc toRef*[T](obj: T): ref T =
|
||||||
|
|
||||||
const FETCH_BATCH_COUNT_DEFAULT = 10
|
const FETCH_BATCH_COUNT_DEFAULT = 10
|
||||||
const FETCH_RECIPIENTS_BATCH_COUNT_DEFAULT = 2000
|
const FETCH_RECIPIENTS_BATCH_COUNT_DEFAULT = 2000
|
||||||
|
const FETCH_COLLECTIBLES_BATCH_COUNT_DEFAULT = 2000
|
||||||
|
|
||||||
type
|
type
|
||||||
CollectiblesToTokenConverter* = proc (id: string): backend_activity.Token
|
CollectiblesToTokenConverter* = proc (id: string): backend_activity.Token
|
||||||
|
@ -39,6 +42,7 @@ QtObject:
|
||||||
model: Model
|
model: Model
|
||||||
|
|
||||||
recipientsModel: RecipientsModel
|
recipientsModel: RecipientsModel
|
||||||
|
collectiblesModel: CollectiblesModel
|
||||||
currentActivityFilter: backend_activity.ActivityFilter
|
currentActivityFilter: backend_activity.ActivityFilter
|
||||||
currencyService: currency_service.Service
|
currencyService: currency_service.Service
|
||||||
tokenService: token_service.Service
|
tokenService: token_service.Service
|
||||||
|
@ -77,6 +81,12 @@ QtObject:
|
||||||
QtProperty[QVariant] recipientsModel:
|
QtProperty[QVariant] recipientsModel:
|
||||||
read = getRecipientsModel
|
read = getRecipientsModel
|
||||||
|
|
||||||
|
proc getCollectiblesModel*(self: Controller): QVariant {.slot.} =
|
||||||
|
return newQVariant(self.collectiblesModel)
|
||||||
|
|
||||||
|
QtProperty[QVariant] collectiblesModel:
|
||||||
|
read = getCollectiblesModel
|
||||||
|
|
||||||
proc buildMultiTransactionExtraData(self: Controller, metadata: backend_activity.ActivityEntry): ExtraData =
|
proc buildMultiTransactionExtraData(self: Controller, metadata: backend_activity.ActivityEntry): ExtraData =
|
||||||
if metadata.symbolIn.isSome():
|
if metadata.symbolIn.isSome():
|
||||||
result.inAmount = self.currencyService.parseCurrencyValue(metadata.symbolIn.get(), metadata.amountIn)
|
result.inAmount = self.currencyService.parseCurrencyValue(metadata.symbolIn.get(), metadata.amountIn)
|
||||||
|
@ -170,6 +180,22 @@ QtObject:
|
||||||
error "error fetching activity entries: ", response.error
|
error "error fetching activity entries: ", response.error
|
||||||
return
|
return
|
||||||
|
|
||||||
|
proc updateCollectiblesModel*(self: Controller) {.slot.} =
|
||||||
|
self.status.setLoadingCollectibles(true)
|
||||||
|
let res = backend_activity.getActivityCollectiblesAsync(self.requestId, self.chainIds, self.addresses, 0, FETCH_COLLECTIBLES_BATCH_COUNT_DEFAULT)
|
||||||
|
if res.error != nil:
|
||||||
|
self.status.setLoadingCollectibles(false)
|
||||||
|
error "error fetching collectibles: ", res.error
|
||||||
|
return
|
||||||
|
|
||||||
|
proc loadMoreCollectibles*(self: Controller) {.slot.} =
|
||||||
|
self.status.setLoadingCollectibles(true)
|
||||||
|
let res = backend_activity.getActivityCollectiblesAsync(self.requestId, self.chainIds, self.addresses, self.collectiblesModel.getCount(), FETCH_COLLECTIBLES_BATCH_COUNT_DEFAULT)
|
||||||
|
if res.error != nil:
|
||||||
|
self.status.setLoadingCollectibles(false)
|
||||||
|
error "error fetching collectibles: ", res.error
|
||||||
|
return
|
||||||
|
|
||||||
proc setFilterTime*(self: Controller, startTimestamp: int, endTimestamp: int) {.slot.} =
|
proc setFilterTime*(self: Controller, startTimestamp: int, endTimestamp: int) {.slot.} =
|
||||||
self.currentActivityFilter.period = backend_activity.newPeriod(startTimestamp, endTimestamp)
|
self.currentActivityFilter.period = backend_activity.newPeriod(startTimestamp, endTimestamp)
|
||||||
|
|
||||||
|
@ -234,6 +260,21 @@ QtObject:
|
||||||
self.status.setStartTimestamp(res.timestamp)
|
self.status.setStartTimestamp(res.timestamp)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.eventsHandler.onGetCollectiblesDone(proc (jsonObj: JsonNode) =
|
||||||
|
defer: self.status.setLoadingCollectibles(false)
|
||||||
|
let res = fromJson(jsonObj, backend_activity.GetCollectiblesResponse)
|
||||||
|
|
||||||
|
if res.errorCode != ErrorCodeSuccess:
|
||||||
|
error "error fetching collectibles: ", res.errorCode
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
let items = res.collectibles.map(header => collectibleToItem(header))
|
||||||
|
self.collectiblesModel.setItems(items, res.offset, res.hasMore)
|
||||||
|
except Exception as e:
|
||||||
|
error "Error converting activity entries: ", e.msg
|
||||||
|
)
|
||||||
|
|
||||||
self.eventsHandler.onNewDataAvailable(proc () =
|
self.eventsHandler.onNewDataAvailable(proc () =
|
||||||
self.status.setNewDataAvailable(true)
|
self.status.setNewDataAvailable(true)
|
||||||
)
|
)
|
||||||
|
@ -248,6 +289,7 @@ QtObject:
|
||||||
result.requestId = requestId
|
result.requestId = requestId
|
||||||
result.model = newModel()
|
result.model = newModel()
|
||||||
result.recipientsModel = newRecipientsModel()
|
result.recipientsModel = newRecipientsModel()
|
||||||
|
result.collectiblesModel = newCollectiblesModel()
|
||||||
result.tokenService = tokenService
|
result.tokenService = tokenService
|
||||||
result.currentActivityFilter = backend_activity.getIncludeAllActivityFilter()
|
result.currentActivityFilter = backend_activity.getIncludeAllActivityFilter()
|
||||||
|
|
||||||
|
@ -364,6 +406,7 @@ QtObject:
|
||||||
proc setFilterChains*(self: Controller, chainIds: seq[int], allEnabled: bool) =
|
proc setFilterChains*(self: Controller, chainIds: seq[int], allEnabled: bool) =
|
||||||
self.chainIds = chainIds
|
self.chainIds = chainIds
|
||||||
self.status.setIsFilterDirty(true)
|
self.status.setIsFilterDirty(true)
|
||||||
|
self.status.emitFilterChainsChanged()
|
||||||
|
|
||||||
self.status.emitFilterChainsChanged()
|
self.status.emitFilterChainsChanged()
|
||||||
self.updateAssetsIdentities()
|
self.updateAssetsIdentities()
|
||||||
|
|
|
@ -46,6 +46,9 @@ QtObject:
|
||||||
proc onGetOldestTimestampDone*(self: EventsHandler, handler: EventCallbackProc) =
|
proc onGetOldestTimestampDone*(self: EventsHandler, handler: EventCallbackProc) =
|
||||||
self.eventHandlers[backend_activity.eventActivityGetOldestTimestampDone] = handler
|
self.eventHandlers[backend_activity.eventActivityGetOldestTimestampDone] = handler
|
||||||
|
|
||||||
|
proc onGetCollectiblesDone*(self: EventsHandler, handler: EventCallbackProc) =
|
||||||
|
self.eventHandlers[backend_activity.eventActivityGetCollectiblesDone] = handler
|
||||||
|
|
||||||
proc onNewDataAvailable*(self: EventsHandler, handler: proc()) =
|
proc onNewDataAvailable*(self: EventsHandler, handler: proc()) =
|
||||||
self.newDataAvailableFn = handler
|
self.newDataAvailableFn = handler
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ QtObject:
|
||||||
errorCode: backend_activity.ErrorCode
|
errorCode: backend_activity.ErrorCode
|
||||||
|
|
||||||
loadingRecipients: Atomic[int]
|
loadingRecipients: Atomic[int]
|
||||||
|
loadingCollectibles: Atomic[int]
|
||||||
loadingStartTimestamp: Atomic[int]
|
loadingStartTimestamp: Atomic[int]
|
||||||
|
|
||||||
startTimestamp: int
|
startTimestamp: int
|
||||||
|
@ -44,6 +45,12 @@ QtObject:
|
||||||
discard fetchAdd(self.loadingRecipients, if loadingData: 1 else: -1)
|
discard fetchAdd(self.loadingRecipients, if loadingData: 1 else: -1)
|
||||||
self.loadingRecipientsChanged()
|
self.loadingRecipientsChanged()
|
||||||
|
|
||||||
|
proc loadingCollectiblesChanged*(self: Status) {.signal.}
|
||||||
|
|
||||||
|
proc setLoadingCollectibles*(self: Status, loadingData: bool) =
|
||||||
|
discard fetchAdd(self.loadingCollectibles, if loadingData: 1 else: -1)
|
||||||
|
self.loadingCollectiblesChanged()
|
||||||
|
|
||||||
proc loadingStartTimestampChanged*(self: Status) {.signal.}
|
proc loadingStartTimestampChanged*(self: Status) {.signal.}
|
||||||
|
|
||||||
proc setLoadingStartTimestamp*(self: Status, loadingData: bool) =
|
proc setLoadingStartTimestamp*(self: Status, loadingData: bool) =
|
||||||
|
|
|
@ -20,6 +20,7 @@ const eventActivityFilteringUpdate*: string = "wallet-activity-filtering-entries
|
||||||
const eventActivityGetRecipientsDone*: string = "wallet-activity-get-recipients-result"
|
const eventActivityGetRecipientsDone*: string = "wallet-activity-get-recipients-result"
|
||||||
const eventActivityGetOldestTimestampDone*: string = "wallet-activity-get-oldest-timestamp-result"
|
const eventActivityGetOldestTimestampDone*: string = "wallet-activity-get-oldest-timestamp-result"
|
||||||
const eventActivityFetchTransactionDetails*: string = "wallet-activity-fetch-transaction-details-result"
|
const eventActivityFetchTransactionDetails*: string = "wallet-activity-fetch-transaction-details-result"
|
||||||
|
const eventActivityGetCollectiblesDone*: string = "wallet-activity-get-collectibles"
|
||||||
|
|
||||||
type
|
type
|
||||||
Period* = object
|
Period* = object
|
||||||
|
@ -527,6 +528,67 @@ rpc(getOldestActivityTimestampAsync, "wallet"):
|
||||||
requestId: int32
|
requestId: int32
|
||||||
addresses: seq[string]
|
addresses: seq[string]
|
||||||
|
|
||||||
|
type
|
||||||
|
# Mirrors services/wallet/thirdparty/collectible_types.go ContractID
|
||||||
|
ContractID* = ref object of RootObj
|
||||||
|
chainID*: int
|
||||||
|
address*: string
|
||||||
|
|
||||||
|
# Mirrors services/wallet/thirdparty/collectible_types.go CollectibleUniqueID
|
||||||
|
CollectibleUniqueID* = ref object of RootObj
|
||||||
|
contractID*: ContractID
|
||||||
|
tokenID*: UInt256
|
||||||
|
|
||||||
|
# see services/wallet/activity/service.go CollectibleHeader
|
||||||
|
CollectibleHeader* = object
|
||||||
|
id* : CollectibleUniqueID
|
||||||
|
name*: string
|
||||||
|
imageUrl*: string
|
||||||
|
|
||||||
|
# see services/wallet/activity/service.go CollectiblesResponse
|
||||||
|
GetCollectiblesResponse* = object
|
||||||
|
collectibles*: seq[CollectibleHeader]
|
||||||
|
offset*: int
|
||||||
|
hasMore*: bool
|
||||||
|
errorCode*: ErrorCode
|
||||||
|
|
||||||
|
proc fromJson*(t: JsonNode, T: typedesc[ContractID]): ContractID {.inline.} =
|
||||||
|
result = ContractID()
|
||||||
|
result.chainID = t["chainID"].getInt()
|
||||||
|
result.address = t["address"].getStr()
|
||||||
|
|
||||||
|
proc fromJson*(t: JsonNode, T: typedesc[CollectibleUniqueID]): CollectibleUniqueID {.inline.} =
|
||||||
|
result = CollectibleUniqueID()
|
||||||
|
result.contractID = fromJson(t["contractID"], ContractID)
|
||||||
|
result.tokenID = stint.parse(t["tokenID"].getStr(), UInt256)
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
proc fromJson*(e: JsonNode, T: typedesc[GetCollectiblesResponse]): GetCollectiblesResponse {.inline.} =
|
||||||
|
var collectibles: seq[CollectibleHeader] = @[]
|
||||||
|
if e.hasKey("collectibles"):
|
||||||
|
let jsonCollectibles = e["collectibles"]
|
||||||
|
for item in jsonCollectibles.getElems():
|
||||||
|
collectibles.add(fromJson(item, CollectibleHeader))
|
||||||
|
|
||||||
|
result = T(
|
||||||
|
collectibles: collectibles,
|
||||||
|
hasMore: e["hasMore"].getBool(),
|
||||||
|
offset: e["offset"].getInt(),
|
||||||
|
errorCode: ErrorCode(e["errorCode"].getInt())
|
||||||
|
)
|
||||||
|
|
||||||
|
rpc(getActivityCollectiblesAsync, "wallet"):
|
||||||
|
requestId: int32
|
||||||
|
chainIDs: seq[int]
|
||||||
|
addresses: seq[string]
|
||||||
|
offset: int
|
||||||
|
limit: int
|
||||||
|
|
||||||
rpc(getMultiTxDetails, "wallet"):
|
rpc(getMultiTxDetails, "wallet"):
|
||||||
id: int
|
id: int
|
||||||
|
|
||||||
|
|
|
@ -178,11 +178,11 @@ Column {
|
||||||
onClosed: activityFilterStore.toggleCollectibles(uid)
|
onClosed: activityFilterStore.toggleCollectibles(uid)
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
// Collectibles model is fetched asynchronousl, so data might not be available
|
// Collectibles model is fetched asynchronously, so data might not be available
|
||||||
target: activityFilterStore.collectiblesList
|
target: activityFilterStore
|
||||||
enabled: !collectibleTag.isValid
|
enabled: !collectibleTag.isValid
|
||||||
function onIsFetchingChanged() {
|
function onLoadingCollectiblesChanged() {
|
||||||
if (activityFilterStore.collectiblesList.isFetching || !activityFilterStore.collectiblesList.hasMore)
|
if (activityFilterStore.loadingCollectibles || !activityFilterStore.collectiblesList.hasMore)
|
||||||
return
|
return
|
||||||
collectibleTag.uid = ""
|
collectibleTag.uid = ""
|
||||||
collectibleTag.uid = modelData
|
collectibleTag.uid = modelData
|
||||||
|
@ -262,6 +262,7 @@ Column {
|
||||||
store: root.store
|
store: root.store
|
||||||
recentsList: activityFilterStore.recentsList
|
recentsList: activityFilterStore.recentsList
|
||||||
loadingRecipients: activityFilterStore.loadingRecipients
|
loadingRecipients: activityFilterStore.loadingRecipients
|
||||||
|
loadingCollectibles: activityFilterStore.loadingCollectibles
|
||||||
recentsFilters: activityFilterStore.recentsFilters
|
recentsFilters: activityFilterStore.recentsFilters
|
||||||
savedAddressList: activityFilterStore.savedAddressList
|
savedAddressList: activityFilterStore.savedAddressList
|
||||||
savedAddressFilters: activityFilterStore.savedAddressFilters
|
savedAddressFilters: activityFilterStore.savedAddressFilters
|
||||||
|
|
|
@ -33,6 +33,7 @@ StatusMenu {
|
||||||
// Collectibles filter
|
// Collectibles filter
|
||||||
property var collectiblesList: []
|
property var collectiblesList: []
|
||||||
property var collectiblesFilter: []
|
property var collectiblesFilter: []
|
||||||
|
property bool loadingCollectibles: false
|
||||||
readonly property bool allCollectiblesChecked: tokensMenu.allCollectiblesChecked
|
readonly property bool allCollectiblesChecked: tokensMenu.allCollectiblesChecked
|
||||||
signal updateCollectiblesFilter(string uid)
|
signal updateCollectiblesFilter(string uid)
|
||||||
|
|
||||||
|
@ -96,6 +97,7 @@ StatusMenu {
|
||||||
tokensFilter: root.tokensFilter
|
tokensFilter: root.tokensFilter
|
||||||
collectiblesList: root.collectiblesList
|
collectiblesList: root.collectiblesList
|
||||||
collectiblesFilter: root.collectiblesFilter
|
collectiblesFilter: root.collectiblesFilter
|
||||||
|
loadingCollectibles: root.loadingCollectibles
|
||||||
onTokenToggled: updateTokensFilter(tokenSymbol)
|
onTokenToggled: updateTokensFilter(tokenSymbol)
|
||||||
onCollectibleToggled: updateCollectiblesFilter(uid)
|
onCollectibleToggled: updateCollectiblesFilter(uid)
|
||||||
closePolicy: root.closePolicy
|
closePolicy: root.closePolicy
|
||||||
|
|
|
@ -20,6 +20,7 @@ StatusMenu {
|
||||||
property var tokensList: []
|
property var tokensList: []
|
||||||
readonly property bool allTokensChecked: tokensFilter.length === 0
|
readonly property bool allTokensChecked: tokensFilter.length === 0
|
||||||
|
|
||||||
|
property bool loadingCollectibles: false
|
||||||
property var collectiblesList: []
|
property var collectiblesList: []
|
||||||
property var collectiblesFilter: []
|
property var collectiblesFilter: []
|
||||||
readonly property bool allCollectiblesChecked: collectiblesFilter.length === 0
|
readonly property bool allCollectiblesChecked: collectiblesFilter.length === 0
|
||||||
|
@ -37,11 +38,6 @@ StatusMenu {
|
||||||
collectiblesSearchBox.reset()
|
collectiblesSearchBox.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
QtObject {
|
|
||||||
id: d
|
|
||||||
readonly property bool isFetching: root.collectiblesList.isFetching
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
spacing: 12
|
spacing: 12
|
||||||
MenuBackButton {
|
MenuBackButton {
|
||||||
|
@ -186,7 +182,7 @@ StatusMenu {
|
||||||
allChecked: root.allCollectiblesChecked
|
allChecked: root.allCollectiblesChecked
|
||||||
checked: !loading && (root.allCollectiblesChecked || root.collectiblesFilter.includes(model.uid))
|
checked: !loading && (root.allCollectiblesChecked || root.collectiblesFilter.includes(model.uid))
|
||||||
onActionTriggered: root.collectibleToggled(model.uid)
|
onActionTriggered: root.collectibleToggled(model.uid)
|
||||||
loading: d.isFetching
|
loading: root.loadingCollectibles
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,8 +193,15 @@ QtObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collectibles Filters
|
// Collectibles Filters
|
||||||
property var collectiblesList: walletSection.collectiblesController.model
|
property var collectiblesList: activityController.collectiblesModel
|
||||||
property var collectiblesFilter: []
|
property var collectiblesFilter: []
|
||||||
|
property bool loadingCollectibles: activityController.status.loadingCollectibles
|
||||||
|
function updateCollectiblesModel() {
|
||||||
|
activityController.updateCollectiblesModel()
|
||||||
|
}
|
||||||
|
function loadMoreCollectibles() {
|
||||||
|
activityController.loadMoreCollectibles()
|
||||||
|
}
|
||||||
function toggleCollectibles(uid) {
|
function toggleCollectibles(uid) {
|
||||||
// update filters
|
// update filters
|
||||||
collectiblesFilter = d.toggleFilterState(collectiblesFilter, uid, collectiblesList.count)
|
collectiblesFilter = d.toggleFilterState(collectiblesFilter, uid, collectiblesList.count)
|
||||||
|
|
|
@ -275,7 +275,7 @@ QtObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isTxRepeatable(tx) {
|
function isTxRepeatable(tx) {
|
||||||
if (tx.txType !== Constants.TransactionType.Send)
|
if (!tx || tx.txType !== Constants.TransactionType.Send)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
let res = root.lookupAddressObject(tx.sender)
|
let res = root.lookupAddressObject(tx.sender)
|
||||||
|
|
|
@ -12,6 +12,7 @@ import StatusQ.Popups 0.1
|
||||||
|
|
||||||
import shared.controls 1.0
|
import shared.controls 1.0
|
||||||
import shared.panels 1.0
|
import shared.panels 1.0
|
||||||
|
import shared.stores 1.0
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
import shared.popups.send 1.0
|
import shared.popups.send 1.0
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@ ColumnLayout {
|
||||||
WalletStores.RootStore.currentActivityFiltersStore.applyAllFilters()
|
WalletStores.RootStore.currentActivityFiltersStore.applyAllFilters()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WalletStores.RootStore.currentActivityFiltersStore.updateCollectiblesModel()
|
||||||
WalletStores.RootStore.currentActivityFiltersStore.updateRecipientsModel()
|
WalletStores.RootStore.currentActivityFiltersStore.updateRecipientsModel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,10 +59,15 @@ ColumnLayout {
|
||||||
RootStore.updateTransactionFilter()
|
RootStore.updateTransactionFilter()
|
||||||
}
|
}
|
||||||
function onFilterChainsChanged() {
|
function onFilterChainsChanged() {
|
||||||
|
WalletStores.RootStore.currentActivityFiltersStore.updateCollectiblesModel()
|
||||||
WalletStores.RootStore.currentActivityFiltersStore.updateRecipientsModel()
|
WalletStores.RootStore.currentActivityFiltersStore.updateRecipientsModel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
enabled: root.visible
|
||||||
|
}
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: d
|
id: d
|
||||||
readonly property bool isInitialLoading: RootStore.loadingHistoryTransactions && transactionListRoot.count === 0
|
readonly property bool isInitialLoading: RootStore.loadingHistoryTransactions && transactionListRoot.count === 0
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit cff96f99e0997b3f3197eecf7b568f7c2e42cac1
|
Subproject commit ecc8b4cb5513ed29deb4fdc85eaeed5e96d1e562
|
Loading…
Reference in New Issue