chore(wallet) extract responsibilities from activity.Controller

Extract processing of Events and Status keeping responsibilities from
activity.Controller

Updates #11233
This commit is contained in:
Stefan 2023-07-03 15:49:58 +01:00 committed by Stefan Dunca
parent 24cdab41c6
commit d17f2c70f1
5 changed files with 210 additions and 130 deletions

View File

@ -1,18 +1,19 @@
import NimQml, logging, std/json, sequtils, sugar, options, strutils, times
import tables, stint, sets, atomics
import NimQml, logging, std/json, sequtils, sugar, options, strutils
import tables, stint, sets
import model
import entry
import recipients_model
import events_handler
import status
import web3/conversions
import app/core/eventemitter
import ../transactions/item
import ../transactions/module as transactions_module
import app/core/eventemitter
import app/core/signals/types
import backend/activity as backend_activity
import backend/backend as backend
import backend/transactions
@ -28,8 +29,6 @@ proc toRef*[T](obj: T): ref T =
const FETCH_BATCH_COUNT_DEFAULT = 10
const FETCH_RECIPIENTS_BATCH_COUNT_DEFAULT = 2000
type EventCallbackProc = proc (eventObject: JsonNode)
# TODO: implement passing of collectibles
QtObject:
type
@ -41,15 +40,8 @@ QtObject:
currencyService: currency_service.Service
tokenService: token_service.Service
events: EventEmitter
# Event name and handler pairs
eventHandlers: Table[string, EventCallbackProc]
loadingData: Atomic[int]
errorCode: backend_activity.ErrorCode
loadingRecipients: Atomic[int]
loadingStartTimestamp: Atomic[int]
eventsHandler: EventsHandler
status: Status
# call updateAssetsIdentities after updating filterTokenCodes
filterTokenCodes: HashSet[string]
@ -58,8 +50,6 @@ QtObject:
# call updateAssetsIdentities after updating chainIds
chainIds: seq[int]
startTimestamp: int
proc setup(self: Controller) =
self.QObject.setup
@ -174,36 +164,12 @@ QtObject:
error "failed to find pending transaction with identity: ", identity
ptIndex += 1
proc loadingDataChanged*(self: Controller) {.signal.}
proc setLoadingData(self: Controller, loadingData: bool) =
discard fetchAdd(self.loadingData, if loadingData: 1 else: -1)
self.loadingDataChanged()
proc loadingRecipientsChanged*(self: Controller) {.signal.}
proc setLoadingRecipients(self: Controller, loadingData: bool) =
discard fetchAdd(self.loadingRecipients, if loadingData: 1 else: -1)
self.loadingRecipientsChanged()
proc loadingStartTimestampChanged*(self: Controller) {.signal.}
proc setLoadingStartTimestamp(self: Controller, loadingData: bool) =
discard fetchAdd(self.loadingStartTimestamp, if loadingData: 1 else: -1)
self.loadingStartTimestampChanged()
proc errorCodeChanged*(self: Controller) {.signal.}
proc setErrorCode(self: Controller, errorCode: int) =
self.errorCode = backend_activity.ErrorCode(errorCode)
self.errorCodeChanged()
proc processResponse(self: Controller, response: JsonNode) =
defer: self.setLoadingData(false)
defer: self.status.setLoadingData(false)
let res = fromJson(response, backend_activity.FilterResponse)
defer: self.setErrorCode(res.errorCode.int)
defer: self.status.setErrorCode(res.errorCode.int)
if res.errorCode != ErrorCodeSuccess:
error "error fetching activity entries: ", res.errorCode
@ -213,21 +179,21 @@ QtObject:
self.model.setEntries(entries, res.offset, res.hasMore)
proc updateFilter*(self: Controller) {.slot.} =
self.setLoadingData(true)
self.status.setLoadingData(true)
self.model.resetModel(@[])
let response = backend_activity.filterActivityAsync(self.addresses, seq[backend_activity.ChainId](self.chainIds), self.currentActivityFilter, 0, FETCH_BATCH_COUNT_DEFAULT)
if response.error != nil:
error "error fetching activity entries: ", response.error
self.setLoadingData(false)
self.status.setLoadingData(false)
return
proc loadMoreItems(self: Controller) {.slot.} =
self.setLoadingData(true)
self.status.setLoadingData(true)
let response = backend_activity.filterActivityAsync(self.addresses, seq[backend_activity.ChainId](self.chainIds), self.currentActivityFilter, self.model.getCount(), FETCH_BATCH_COUNT_DEFAULT)
if response.error != nil:
self.setLoadingData(false)
self.status.setLoadingData(false)
error "error fetching activity entries: ", response.error
return
@ -246,40 +212,24 @@ QtObject:
self.currentActivityFilter.types = types
proc startTimestampChanged*(self: Controller) {.signal.}
# Call this method on every data update (ideally only if updates are before the last timestamp retrieved)
# This depends on self.addresses being set, call on every address change
proc updateStartTimestamp*(self: Controller) {.slot.} =
self.setLoadingStartTimestamp(true)
self.status.setLoadingStartTimestamp(true)
let resJson = backend_activity.getOldestActivityTimestampAsync(self.addresses)
if resJson.error != nil:
self.setLoadingStartTimestamp(false)
self.status.setLoadingStartTimestamp(false)
error "error requesting oldest activity timestamp: ", resJson.error
return
proc handleApiEvents(self: Controller, e: Args) =
var data = WalletSignal(e)
if self.eventHandlers.hasKey(data.eventType):
var responseJson: JsonNode
responseJson = parseJson(data.message)
if responseJson.kind != JObject:
error "unexpected json type", responseJson.kind
return
let callback = self.eventHandlers[data.eventType]
callback(responseJson)
else:
discard
proc setupEventHandlers(self: Controller) =
self.eventHandlers[backend_activity.eventActivityFilteringDone] = proc (jsonObj: JsonNode) =
self.eventsHandler.onFilteringDone(proc (jsonObj: JsonNode) =
self.processResponse(jsonObj)
)
self.eventHandlers[backend_activity.eventActivityGetRecipientsDone] = proc (jsonObj: JsonNode) =
defer: self.setLoadingRecipients(false)
self.eventsHandler.onGetRecipientsDone(proc (jsonObj: JsonNode) =
defer: self.status.setLoadingRecipients(false)
let res = fromJson(jsonObj, backend_activity.GetRecipientsResponse)
if res.errorCode != ErrorCodeSuccess:
@ -287,33 +237,35 @@ QtObject:
return
self.recipientsModel.addAddresses(res.addresses, res.offset, res.hasMore)
)
self.eventHandlers[backend_activity.eventActivityGetOldestTimestampDone] = proc (jsonObj: JsonNode) =
defer: self.setLoadingStartTimestamp(false)
self.eventsHandler.onGetOldestTimestampDone(proc (jsonObj: JsonNode) =
defer: self.status.setLoadingStartTimestamp(false)
let res = fromJson(jsonObj, backend_activity.GetOldestTimestampResponse)
if res.errorCode != ErrorCodeSuccess:
error "error fetching start timestamp: ", res.errorCode
return
self.startTimestamp = res.timestamp
self.startTimestampChanged()
self.status.setStartTimestamp(res.timestamp)
)
proc newController*(transactionsModule: transactions_module.AccessInterface,
currencyService: currency_service.Service,
tokenService: token_service.Service,
events: EventEmitter): Controller =
new(result, delete)
result.model = newModel()
result.recipientsModel = newRecipientsModel()
result.transactionsModule = transactionsModule
result.tokenService = tokenService
result.currentActivityFilter = backend_activity.getIncludeAllActivityFilter()
result.events = events
result.eventHandlers = initTable[string, EventCallbackProc]()
result.currencyService = currencyService
result.errorCode = backend_activity.ErrorCode.ErrorCodeSuccess
result.eventsHandler = newEventsHandler(events)
result.status = newStatus()
result.currencyService = currencyService
result.filterTokenCodes = initHashSet[string]()
@ -323,10 +275,6 @@ QtObject:
result.setup()
result.setupEventHandlers()
let controller = result
result.events.on(SignalType.Wallet.event, proc(e: Args) =
controller.handleApiEvents(e)
)
proc setFilterStatus*(self: Controller, statusesArrayJsonString: string) {.slot.} =
let statusesJson = parseJson(statusesArrayJsonString)
@ -397,67 +345,35 @@ QtObject:
self.updateAssetsIdentities()
proc getLoadingData*(self: Controller): bool {.slot.} =
return load(self.loadingData) > 0
QtProperty[bool] loadingData:
read = getLoadingData
notify = loadingDataChanged
proc getErrorCode*(self: Controller): int {.slot.} =
return self.errorCode.int
QtProperty[int] errorCode:
read = getErrorCode
notify = errorCodeChanged
proc getLoadingRecipients*(self: Controller): bool {.slot.} =
return load(self.loadingRecipients) > 0
QtProperty[bool] loadingRecipients:
read = getLoadingRecipients
notify = loadingRecipientsChanged
proc getLoadingStartTimestamp*(self: Controller): bool {.slot.} =
return load(self.loadingStartTimestamp) > 0
QtProperty[bool] loadingStartTimestamp:
read = getLoadingStartTimestamp
notify = loadingStartTimestampChanged
proc updateRecipientsModel*(self: Controller) {.slot.} =
self.setLoadingRecipients(true)
self.status.setLoadingRecipients(true)
let res = backend_activity.getRecipientsAsync(0, FETCH_RECIPIENTS_BATCH_COUNT_DEFAULT)
if res.error != nil or res.result.kind != JBool:
self.setLoadingRecipients(false)
self.status.setLoadingRecipients(false)
error "error fetching recipients: ", res.error, "; kind ", res.result.kind
return
# If the request was enqueued and already waiting for a response, we don't need to do anything
if res.result.getBool():
self.setLoadingRecipients(false)
self.status.setLoadingRecipients(false)
proc loadMoreRecipients(self: Controller) {.slot.} =
self.setLoadingRecipients(true)
self.status.setLoadingRecipients(true)
let res = backend_activity.getRecipientsAsync(self.recipientsModel.getCount(), FETCH_RECIPIENTS_BATCH_COUNT_DEFAULT)
if res.error != nil:
self.setLoadingRecipients(false)
self.status.setLoadingRecipients(false)
error "error fetching more recipient entries: ", res.error
return
# If the request was enqueued and waiting for an answer, we don't need to do anything
if res.result.getBool():
self.setLoadingRecipients(false)
proc getStartTimestamp*(self: Controller): int {.slot.} =
return if self.startTimestamp > 0:
self.startTimestamp
else:
int(times.parse("2000-01-01", "yyyy-MM-dd").toTime().toUnix())
QtProperty[int] startTimestamp:
read = getStartTimestamp
notify = startTimestampChanged
self.status.setLoadingRecipients(false)
proc updateFilterBase(self: Controller) {.slot.} =
self.updateStartTimestamp()
proc getStatus*(self: Controller): QVariant {.slot.} =
return newQVariant(self.status)
QtProperty[QVariant] status:
read = getStatus

View File

@ -0,0 +1,65 @@
import NimQml, logging, std/json, sequtils, strutils
import tables, stint, sets
import model
import entry
import recipients_model
import web3/conversions
import app/core/eventemitter
import app/core/signals/types
import backend/activity as backend_activity
type EventCallbackProc = proc (eventObject: JsonNode)
# EventsHandler responsible for catching activity related backend events and reporting them
QtObject:
type
EventsHandler* = ref object of QObject
events: EventEmitter
# Event name and handler pairs
eventHandlers: Table[string, EventCallbackProc]
proc setup(self: EventsHandler) =
self.QObject.setup
proc delete*(self: EventsHandler) =
self.QObject.delete
proc onFilteringDone*(self: EventsHandler, handler: EventCallbackProc) =
self.eventHandlers[backend_activity.eventActivityFilteringDone] = handler
proc onGetRecipientsDone*(self: EventsHandler, handler: EventCallbackProc) =
self.eventHandlers[backend_activity.eventActivityGetRecipientsDone] = handler
proc onGetOldestTimestampDone*(self: EventsHandler, handler: EventCallbackProc) =
self.eventHandlers[backend_activity.eventActivityGetOldestTimestampDone] = handler
proc handleApiEvents(self: EventsHandler, e: Args) =
var data = WalletSignal(e)
if self.eventHandlers.hasKey(data.eventType):
var responseJson: JsonNode
responseJson = parseJson(data.message)
if responseJson.kind != JObject:
error "unexpected json type", responseJson.kind
return
let callback = self.eventHandlers[data.eventType]
callback(responseJson)
else:
discard
proc newEventsHandler*(events: EventEmitter): EventsHandler =
new(result, delete)
result.events = events
result.eventHandlers = initTable[string, EventCallbackProc]()
result.setup()
let eventsHandler = result
result.events.on(SignalType.Wallet.event, proc(e: Args) =
eventsHandler.handleApiEvents(e)
)

View File

@ -0,0 +1,99 @@
import NimQml, std/json, sequtils, strutils, times
import tables, stint, sets, atomics
import web3/conversions
import app/core/signals/types
import backend/activity as backend_activity
# Status's responsibility is to keep track and report the general state of the backend
QtObject:
type
Status* = ref object of QObject
loadingData: Atomic[int]
errorCode: backend_activity.ErrorCode
loadingRecipients: Atomic[int]
loadingStartTimestamp: Atomic[int]
startTimestamp: int
proc setup(self: Status) =
self.QObject.setup
proc delete*(self: Status) =
self.QObject.delete
proc loadingDataChanged*(self: Status) {.signal.}
proc setLoadingData*(self: Status, loadingData: bool) =
discard fetchAdd(self.loadingData, if loadingData: 1 else: -1)
self.loadingDataChanged()
proc loadingRecipientsChanged*(self: Status) {.signal.}
proc setLoadingRecipients*(self: Status, loadingData: bool) =
discard fetchAdd(self.loadingRecipients, if loadingData: 1 else: -1)
self.loadingRecipientsChanged()
proc loadingStartTimestampChanged*(self: Status) {.signal.}
proc setLoadingStartTimestamp*(self: Status, loadingData: bool) =
discard fetchAdd(self.loadingStartTimestamp, if loadingData: 1 else: -1)
self.loadingStartTimestampChanged()
proc errorCodeChanged*(self: Status) {.signal.}
proc setErrorCode*(self: Status, errorCode: int) =
self.errorCode = backend_activity.ErrorCode(errorCode)
self.errorCodeChanged()
proc newStatus*(): Status =
new(result, delete)
result.errorCode = backend_activity.ErrorCode.ErrorCodeSuccess
result.setup()
proc getLoadingData*(self: Status): bool {.slot.} =
return load(self.loadingData) > 0
QtProperty[bool] loadingData:
read = getLoadingData
notify = loadingDataChanged
proc getErrorCode*(self: Status): int {.slot.} =
return self.errorCode.int
QtProperty[int] errorCode:
read = getErrorCode
notify = errorCodeChanged
proc getLoadingRecipients*(self: Status): bool {.slot.} =
return load(self.loadingRecipients) > 0
QtProperty[bool] loadingRecipients:
read = getLoadingRecipients
notify = loadingRecipientsChanged
proc getLoadingStartTimestamp*(self: Status): bool {.slot.} =
return load(self.loadingStartTimestamp) > 0
QtProperty[bool] loadingStartTimestamp:
read = getLoadingStartTimestamp
notify = loadingStartTimestampChanged
proc startTimestampChanged*(self: Status) {.signal.}
proc setStartTimestamp*(self: Status, startTimestamp: int) =
self.startTimestamp = startTimestamp
self.startTimestampChanged()
proc getStartTimestamp*(self: Status): int {.slot.} =
return if self.startTimestamp > 0: self.startTimestamp
else: int(times.parse("2000-01-01", "yyyy-MM-dd").toTime().toUnix())
QtProperty[int] startTimestamp:
read = getStartTimestamp
notify = startTimestampChanged

View File

@ -21,7 +21,7 @@ QtObject {
// Time filters
property int selectedTime: Constants.TransactionTimePeriod.All
property double fromTimestamp: activityController.startTimestamp * 1000
property double fromTimestamp: activityController.status.startTimestamp * 1000
property double toTimestamp: new Date().valueOf()
function setSelectedTimestamp(selcTime) {
selectedTime = selcTime
@ -137,7 +137,7 @@ QtObject {
property var recentsList: activityController.recipientsModel
property bool loadingRecipients: activityController.loadingRecipients
property bool loadingRecipients: activityController.status.loadingRecipients
property var recentsFilters: []
function updateRecipientsModel() {
activityController.updateRecipientsModel()
@ -202,7 +202,7 @@ QtObject {
function resetAllFilters() {
selectedTime = Constants.TransactionTimePeriod.All
fromTimestamp = activityController.startTimestamp * 1000
fromTimestamp = activityController.status.startTimestamp * 1000
toTimestamp = new Date().valueOf()
activityController.setFilterTime(fromTimestamp/1000, toTimestamp/1000)

View File

@ -38,7 +38,7 @@ QtObject {
property var history: typeof walletSectionTransactions !== "undefined" ? walletSectionTransactions
: null
property var historyTransactions: Global.appIsReady? walletSection.activityController.model : null
readonly property bool loadingHistoryTransactions: Global.appIsReady && walletSection.activityController.loadingData
readonly property bool loadingHistoryTransactions: Global.appIsReady && walletSection.activityController.status.loadingData
property bool isNonArchivalNode: history ? history.isNonArchivalNode
: false
property var marketValueStore: TokenMarketValuesStore{}