feat(@desktop/wallet): implement collectibles ownership state

Fixes #12159
This commit is contained in:
Dario Gabriel Lipicar 2023-09-14 19:06:44 -03:00 committed by dlipicar
parent 52e6fddcab
commit 2ac484dd57
3 changed files with 117 additions and 33 deletions

View File

@ -22,7 +22,7 @@ QtObject:
addresses: seq[string]
chainIds: seq[int]
stateTable: Table[string, Table[int, bool]] # Table[address][chainID] -> isUpdating
ownershipStatus: Table[string, Table[int, OwnershipStatus]] # Table[address][chainID] -> OwnershipStatus
requestId: int32
autofetch: bool
@ -42,6 +42,63 @@ QtObject:
QtProperty[QVariant] model:
read = getModelAsVariant
proc checkModelState(self: Controller) =
var overallState = OwnershipStateIdle
# If any address+chainID is error, then the whole model is error
# Otherwise, if any address+chainID is updating, then the whole model is updating
# Otherwise, the model is idle
for address, statusPerChainID in self.ownershipStatus.pairs:
for chainID, status in statusPerChainID:
if status.state == OwnershipStateError:
overallState = OwnershipStateError
break
elif status.state == OwnershipStateUpdating:
overallState = OwnershipStateUpdating
break
case overallState:
of OwnershipStateIdle:
self.model.setIsUpdating(false)
self.model.setIsError(false)
of OwnershipStateUpdating:
self.model.setIsUpdating(true)
self.model.setIsError(false)
of OwnershipStateError:
self.model.setIsUpdating(false)
self.model.setIsError(true)
proc resetOwnershipStatus(self: Controller) =
# Initialize state table
self.ownershipStatus = initTable[string, Table[int, OwnershipStatus]]()
for address in self.addresses:
self.ownershipStatus[address] = initTable[int, OwnershipStatus]()
for chainID in self.chainIds:
self.ownershipStatus[address][chainID] = OwnershipStatus(
state: OwnershipStateUpdating,
timestamp: invalidTimestamp
)
self.model.setIsUpdating(true)
proc setOwnershipStatus(self: Controller, statusPerAddressAndChainID: Table[string, Table[int, OwnershipStatus]]) =
for address, statusPerChainID in statusPerAddressAndChainID.pairs:
if not self.ownershipStatus.hasKey(address):
continue
for chainID, status in statusPerChainID:
if not self.ownershipStatus[address].hasKey(chainID):
continue
self.ownershipStatus[address][chainID] = status
self.checkModelState()
proc setOwnershipState(self: Controller, address: string, chainID: int, state: OwnershipState) =
if not self.ownershipStatus.hasKey(address) or not self.ownershipStatus[address].hasKey(chainID):
return
self.ownershipStatus[address][chainID].state = state
self.checkModelState()
proc loadMoreItems(self: Controller) {.slot.} =
if self.model.getIsFetching():
return
@ -67,10 +124,10 @@ QtObject:
let res = fromJson(response, backend_collectibles.FilterOwnedCollectiblesResponse)
let isError = res.errorCode != ErrorCodeSuccess
defer: self.model.setIsError(isError)
if isError:
error "error fetching collectibles entries: ", res.errorCode
self.model.setIsError(true)
return
try:
@ -79,59 +136,40 @@ QtObject:
except Exception as e:
error "Error converting activity entries: ", e.msg
self.setOwnershipStatus(res.ownershipStatus)
if self.autofetch and res.hasMore:
self.loadMoreItems()
proc resetModel*(self: Controller) {.slot.} =
proc resetModel(self: Controller) {.slot.} =
self.model.setItems(@[], 0, true)
self.fetchFromStart = true
if self.autofetch:
self.loadMoreItems()
proc resetUpdateState*(self: Controller) =
# Initialize state table
# We assume that ownership is initially not being updated. This will change if an
# update starts or a partial update is received.
# TODO: Get the update state at the time of filter switch from the backend?
self.stateTable = initTable[string, Table[int, bool]]()
for address in self.addresses:
self.stateTable[address] = initTable[int, bool]()
for chainID in self.chainIds:
self.stateTable[address][chainID] = false
self.model.setIsUpdating(false)
proc setUpdateState*(self: Controller, address: string, chainID: int, isUpdating: bool) =
if not self.stateTable.hasKey(address) or not self.stateTable[address].hasKey(chainID):
return
self.stateTable[address][chainID] = isUpdating
# If any address+chainID is updating, then the whole model is updating
for address, chainIDsPerAddress in self.stateTable.pairs:
for chainID, isUpdating in chainIDsPerAddress:
if isUpdating:
self.model.setIsUpdating(true)
return
self.model.setIsUpdating(false)
proc setupEventHandlers(self: Controller) =
self.eventsHandler.onOwnedCollectiblesFilteringDone(proc (jsonObj: JsonNode) =
self.processFilterOwnedCollectiblesResponse(jsonObj)
)
self.eventsHandler.onCollectiblesOwnershipUpdateStarted(proc (address: string, chainID: int) =
self.setUpdateState(address, chainID, true)
self.setOwnershipState(address, chainID, OwnershipStateUpdating)
)
self.eventsHandler.onCollectiblesOwnershipUpdatePartial(proc (address: string, chainID: int) =
self.setUpdateState(address, chainID, true)
self.setOwnershipState(address, chainID, OwnershipStateUpdating)
self.resetModel()
)
self.eventsHandler.onCollectiblesOwnershipUpdateFinished(proc (address: string, chainID: int) =
self.setUpdateState(address, chainID, false)
self.setOwnershipState(address, chainID, OwnershipStateIdle)
self.resetModel()
)
self.eventsHandler.onCollectiblesOwnershipUpdateFinishedWithError(proc (address: string, chainID: int) =
self.setOwnershipState(address, chainID, OwnershipStateError)
)
proc newController*(requestId: int32, autofetch: bool, events: EventEmitter): Controller =
new(result, delete)
@ -159,7 +197,7 @@ QtObject:
self.chainIds = chainIds
self.addresses = addresses
self.resetUpdateState()
self.resetOwnershipStatus()
self.eventsHandler.updateSubscribedAddresses(self.addresses)
self.eventsHandler.updateSubscribedChainIDs(self.chainIds)

View File

@ -24,6 +24,7 @@ QtObject:
collectiblesOwnershipUpdateStartedFn: OwnershipUpdateCallbackProc
collectiblesOwnershipUpdatePartialFn: OwnershipUpdateCallbackProc
collectiblesOwnershipUpdateFinishedFn: OwnershipUpdateCallbackProc
collectiblesOwnershipUpdateFinishedWithErrorFn: OwnershipUpdateCallbackProc
requestId: int32
@ -45,6 +46,9 @@ QtObject:
proc onCollectiblesOwnershipUpdateFinished*(self: EventsHandler, handler: OwnershipUpdateCallbackProc) =
self.collectiblesOwnershipUpdateFinishedFn = handler
proc onCollectiblesOwnershipUpdateFinishedWithError*(self: EventsHandler, handler: OwnershipUpdateCallbackProc) =
self.collectiblesOwnershipUpdateFinishedWithErrorFn = handler
proc handleApiEvents(self: EventsHandler, e: Args) =
var data = WalletSignal(e)
@ -89,6 +93,11 @@ QtObject:
return
self.collectiblesOwnershipUpdateFinishedFn(data.accounts[0], data.chainID)
self.walletEventHandlers[backend_collectibles.eventCollectiblesOwnershipUpdateFinishedWithError] = proc (data: WalletSignal) =
if self.collectiblesOwnershipUpdateFinishedWithErrorFn == nil or self.shouldIgnoreEvent(data):
return
self.collectiblesOwnershipUpdateFinishedWithErrorFn(data.accounts[0], data.chainID)
proc newEventsHandler*(requestId: int32, events: EventEmitter): EventsHandler =
new(result, delete)

View File

@ -1,5 +1,5 @@
import json, json_serialization, strformat
import stint, Tables
import stint, Tables, strutils
import core
import response_type, collectibles_types
@ -23,6 +23,8 @@ const eventCollectiblesOwnershipUpdateFinishedWithError*: string = "wallet-colle
const eventOwnedCollectiblesFilteringDone*: string = "wallet-owned-collectibles-filtering-done"
const eventGetCollectiblesDetailsDone*: string = "wallet-get-collectibles-details-done"
const invalidTimestamp*: int = -1
type
# Mirrors services/wallet/collectibles/service.go ErrorCode
ErrorCode* = enum
@ -30,11 +32,23 @@ type
ErrorCodeTaskCanceled,
ErrorCodeFailed
# Mirrors services/wallet/collectibles/service.go OwnershipState
OwnershipState* = enum
OwnershipStateIdle = 1,
OwnershipStateUpdating,
OwnershipStateError
# Mirrors services/wallet/collectibles/service.go OwnershipState
OwnershipStatus* = ref object
state*: OwnershipState
timestamp*: int
# Mirrors services/wallet/collectibles/service.go FilterOwnedCollectiblesResponse
FilterOwnedCollectiblesResponse* = object
collectibles*: seq[CollectibleHeader]
offset*: int
hasMore*: bool
ownershipStatus*: Table[string, Table[int, OwnershipStatus]]
errorCode*: ErrorCode
# Mirrors services/wallet/collectibles/service.go GetCollectiblesDetailsResponse
@ -42,6 +56,19 @@ type
collectibles*: seq[CollectibleDetails]
errorCode*: ErrorCode
# CollectibleOwnershipState
proc `$`*(self: OwnershipStatus): string =
return fmt"""OwnershipStatus(
state:{self.state},
timestamp:{self.timestamp}
"""
proc fromJson*(t: JsonNode, T: typedesc[OwnershipStatus]): OwnershipStatus {.inline.} =
return OwnershipStatus(
state: OwnershipState(t{"state"}.getInt),
timestamp: t{"timestamp"}.getInt
)
# Responses
proc fromJson*(e: JsonNode, T: typedesc[FilterOwnedCollectiblesResponse]): FilterOwnedCollectiblesResponse {.inline.} =
var collectibles: seq[CollectibleHeader]
@ -51,11 +78,21 @@ proc fromJson*(e: JsonNode, T: typedesc[FilterOwnedCollectiblesResponse]): Filte
for i in 0 ..< jsonCollectibles.len:
collectibles[i] = fromJson(jsonCollectibles[i], CollectibleHeader)
var ownershipStatus = initTable[string, Table[int, OwnershipStatus]]()
if e.hasKey("ownershipStatus"):
let jsonOwnershipStatus = e["ownershipStatus"]
for address, jsonStatusPerChain in jsonOwnershipStatus.getFields():
var statusPerChain = initTable[int, OwnershipStatus]()
for chainId, jsonStatus in jsonStatusPerChain.getFields():
statusPerChain[parseInt(chainId)] = fromJson(jsonStatus, OwnershipStatus)
ownershipStatus[address] = statusPerChain
result = T(
collectibles: collectibles,
offset: e["offset"].getInt(),
hasMore: if e.hasKey("hasMore"): e["hasMore"].getBool()
else: false,
ownershipStatus: ownershipStatus,
errorCode: ErrorCode(e["errorCode"].getInt())
)