mirror of
https://github.com/status-im/status-desktop.git
synced 2025-02-23 03:58:49 +00:00
feat(activity): add incremental updates to current activity filter
Switch the activity filter to use the new session-based API that deliver incremental updates to the current filter. Drop the old quick win listening for individual change events and use the unified API instead. The new transactions (on-top) trigger the old "new transactions" buttons that trigger reset of the current filter and the top new transacitons highlighted. Highlight mixed changes (not new on top) as they come in Highlight new changes on filter reset Closes #12120
This commit is contained in:
parent
115610fcf6
commit
9202cce3f5
@ -63,8 +63,6 @@ QtObject:
|
||||
# call updateAssetsIdentities after updating chainIds
|
||||
chainIds: seq[int]
|
||||
|
||||
requestId: int32
|
||||
|
||||
proc setup(self: Controller) =
|
||||
self.QObject.setup
|
||||
|
||||
@ -89,31 +87,9 @@ QtObject:
|
||||
QtProperty[QVariant] collectiblesModel:
|
||||
read = getCollectiblesModel
|
||||
|
||||
proc buildMultiTransactionExtraData(self: Controller, metadata: backend_activity.ActivityEntry): ExtraData =
|
||||
if metadata.symbolIn.isSome():
|
||||
result.inAmount = self.currencyService.parseCurrencyValue(metadata.symbolIn.get(), metadata.amountIn)
|
||||
if metadata.symbolOut.isSome():
|
||||
result.outAmount = self.currencyService.parseCurrencyValue(metadata.symbolOut.get(), metadata.amountOut)
|
||||
|
||||
proc buildTransactionExtraData(self: Controller, metadata: backend_activity.ActivityEntry): ExtraData =
|
||||
if metadata.symbolIn.isSome() or metadata.amountIn > 0:
|
||||
result.inAmount = self.currencyService.parseCurrencyValue(metadata.symbolIn.get(""), metadata.amountIn)
|
||||
if metadata.symbolOut.isSome() or metadata.amountOut > 0:
|
||||
result.outAmount = self.currencyService.parseCurrencyValue(metadata.symbolOut.get(""), metadata.amountOut)
|
||||
|
||||
proc backendToPresentation(self: Controller, backendEntities: seq[backend_activity.ActivityEntry]): seq[entry.ActivityEntry] =
|
||||
let amountToCurrencyConvertor = proc(amount: UInt256, symbol: string): CurrencyAmount =
|
||||
return currencyAmountToItem(self.currencyService.parseCurrencyValue(symbol, amount),
|
||||
self.currencyService.getCurrencyFormat(symbol))
|
||||
for backendEntry in backendEntities:
|
||||
var ae: entry.ActivityEntry
|
||||
case backendEntry.getPayloadType():
|
||||
of MultiTransaction:
|
||||
let extraData = self.buildMultiTransactionExtraData(backendEntry)
|
||||
ae = entry.newMultiTransactionActivityEntry(backendEntry, extraData, amountToCurrencyConvertor)
|
||||
of SimpleTransaction, PendingTransaction:
|
||||
let extraData = self.buildTransactionExtraData(backendEntry)
|
||||
ae = entry.newTransactionActivityEntry(backendEntry, self.addresses, extraData, amountToCurrencyConvertor)
|
||||
let ae = entry.newActivityEntry(backendEntry, self.addresses, self.currencyService)
|
||||
result.add(ae)
|
||||
|
||||
proc fetchTxDetails*(self: Controller, txID: string) {.slot.} =
|
||||
@ -143,37 +119,69 @@ QtObject:
|
||||
|
||||
self.model.setEntries(entries, res.offset, res.hasMore)
|
||||
|
||||
if len(entries) > 0:
|
||||
self.eventsHandler.updateRelevantTimestamp(entries[len(entries) - 1].getTimestamp())
|
||||
if res.offset == 0:
|
||||
self.status.setNewDataAvailable(false)
|
||||
|
||||
proc updateFilter*(self: Controller) {.slot.} =
|
||||
proc sessionId(self: Controller): int32 =
|
||||
return self.eventsHandler.getSessionId()
|
||||
|
||||
proc invalidateData(self: Controller) =
|
||||
self.status.setLoadingData(true)
|
||||
self.status.setIsFilterDirty(false)
|
||||
|
||||
self.model.resetModel(@[])
|
||||
|
||||
self.eventsHandler.updateSubscribedAddresses(self.addresses)
|
||||
self.eventsHandler.updateSubscribedChainIDs(self.chainIds)
|
||||
self.status.setNewDataAvailable(false)
|
||||
|
||||
let response = backend_activity.filterActivityAsync(self.requestId, self.addresses, self.allAddressesSelected, seq[backend_activity.ChainId](self.chainIds), self.currentActivityFilter, 0, FETCH_BATCH_COUNT_DEFAULT)
|
||||
if response.error != nil:
|
||||
error "error fetching activity entries: ", response.error
|
||||
|
||||
# Stops the old session and starts a new one. All the incremental changes are lost
|
||||
proc newFilterSession*(self: Controller) {.slot.} =
|
||||
self.invalidateData()
|
||||
|
||||
# stop the previous filter session
|
||||
if self.eventsHandler.hasSessionId():
|
||||
let res = backend_activity.stopActivityFilterSession(self.sessionId())
|
||||
if res.error != nil:
|
||||
error "error stopping the previous session of activity fitlering: ", res.error
|
||||
self.eventsHandler.clearSessionId()
|
||||
|
||||
# start a new filter session
|
||||
let (sessionId, ok) = backend_activity.newActivityFilterSession(self.addresses, self.allAddressesSelected, seq[backend_activity.ChainId](self.chainIds), self.currentActivityFilter, FETCH_BATCH_COUNT_DEFAULT)
|
||||
if not ok:
|
||||
self.status.setLoadingData(false)
|
||||
return
|
||||
|
||||
self.eventsHandler.setSessionId(sessionId)
|
||||
|
||||
proc updateFilter*(self: Controller) {.slot.} =
|
||||
self.invalidateData()
|
||||
|
||||
if not backend_activity.updateFilterForSession(self.sessionId(), self.currentActivityFilter, FETCH_BATCH_COUNT_DEFAULT):
|
||||
self.status.setLoadingData(false)
|
||||
error "error updating activity filter"
|
||||
return
|
||||
|
||||
proc resetActivityData*(self: Controller) {.slot.} =
|
||||
self.invalidateData()
|
||||
|
||||
let response = backend_activity.resetActivityFilterSession(self.sessionId(), FETCH_BATCH_COUNT_DEFAULT)
|
||||
if response.error != nil:
|
||||
self.status.setLoadingData(false)
|
||||
error "error fetching activity entries from start: ", response.error
|
||||
return
|
||||
|
||||
proc loadMoreItems(self: Controller) {.slot.} =
|
||||
self.status.setLoadingData(true)
|
||||
|
||||
let response = backend_activity.filterActivityAsync(self.requestId, self.addresses, self.allAddressesSelected, seq[backend_activity.ChainId](self.chainIds), self.currentActivityFilter, self.model.getCount(), FETCH_BATCH_COUNT_DEFAULT)
|
||||
let response = backend_activity.getMoreForActivityFilterSession(self.sessionId(), FETCH_BATCH_COUNT_DEFAULT)
|
||||
if response.error != nil:
|
||||
self.status.setLoadingData(false)
|
||||
error "error fetching activity entries: ", response.error
|
||||
error "error fetching more activity entries: ", response.error
|
||||
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)
|
||||
let res = backend_activity.getActivityCollectiblesAsync(self.sessionId(), self.chainIds, self.addresses, 0, FETCH_COLLECTIBLES_BATCH_COUNT_DEFAULT)
|
||||
if res.error != nil:
|
||||
self.status.setLoadingCollectibles(false)
|
||||
error "error fetching collectibles: ", res.error
|
||||
@ -181,7 +189,7 @@ QtObject:
|
||||
|
||||
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)
|
||||
let res = backend_activity.getActivityCollectiblesAsync(self.sessionId(), 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
|
||||
@ -210,7 +218,7 @@ QtObject:
|
||||
proc updateStartTimestamp*(self: Controller) {.slot.} =
|
||||
self.status.setLoadingStartTimestamp(true)
|
||||
|
||||
let resJson = backend_activity.getOldestActivityTimestampAsync(self.requestId, self.addresses)
|
||||
let resJson = backend_activity.getOldestActivityTimestampAsync(self.sessionId(), self.addresses)
|
||||
if resJson.error != nil:
|
||||
self.status.setLoadingStartTimestamp(false)
|
||||
error "error requesting oldest activity timestamp: ", resJson.error
|
||||
@ -254,6 +262,24 @@ QtObject:
|
||||
self.model.updateEntries(entries)
|
||||
)
|
||||
|
||||
self.eventsHandler.onFilteringSessionUpdated(proc (jn: JsonNode) =
|
||||
if jn.kind != JObject:
|
||||
error "expected an object"
|
||||
|
||||
let res = fromJson(jn, backend_activity.SessionUpdate)
|
||||
|
||||
var updated = newSeq[backend_activity.ActivityEntry](len(res.`new`))
|
||||
var indices = newSeq[int](len(res.`new`))
|
||||
for i in 0 ..< len(res.`new`):
|
||||
updated[i] = res.`new`[i].entry
|
||||
indices[i] = res.`new`[i].pos
|
||||
|
||||
self.status.setNewDataAvailable(res.hasNewOnTop)
|
||||
if len(res.`new`) > 0:
|
||||
let entries = self.backendToPresentation(updated)
|
||||
self.model.addNewEntries(entries, indices)
|
||||
)
|
||||
|
||||
self.eventsHandler.onGetRecipientsDone(proc (jsonObj: JsonNode) =
|
||||
defer: self.status.setLoadingRecipients(false)
|
||||
let res = fromJson(jsonObj, backend_activity.GetRecipientsResponse)
|
||||
@ -291,19 +317,13 @@ QtObject:
|
||||
error "Error converting activity entries: ", e.msg
|
||||
)
|
||||
|
||||
self.eventsHandler.onNewDataAvailable(proc () =
|
||||
self.status.setNewDataAvailable(true)
|
||||
)
|
||||
|
||||
proc newController*(requestId: int32,
|
||||
detailsController: details_controller.Controller,
|
||||
proc newController*(detailsController: details_controller.Controller,
|
||||
currencyService: currency_service.Service,
|
||||
tokenService: token_service.Service,
|
||||
savedAddressService: saved_address_service.Service,
|
||||
events: EventEmitter): Controller =
|
||||
new(result, delete)
|
||||
|
||||
result.requestId = requestId
|
||||
result.model = newModel()
|
||||
result.recipientsModel = newRecipientsModel()
|
||||
result.collectiblesModel = newCollectiblesModel()
|
||||
@ -311,7 +331,7 @@ QtObject:
|
||||
result.savedAddressService = savedAddressService
|
||||
result.currentActivityFilter = backend_activity.getIncludeAllActivityFilter()
|
||||
|
||||
result.eventsHandler = newEventsHandler(result.requestId, events)
|
||||
result.eventsHandler = newEventsHandler(events)
|
||||
result.status = newStatus()
|
||||
|
||||
result.currencyService = currencyService
|
||||
@ -452,7 +472,7 @@ QtObject:
|
||||
|
||||
proc updateRecipientsModel*(self: Controller) {.slot.} =
|
||||
self.status.setLoadingRecipients(true)
|
||||
let res = backend_activity.getRecipientsAsync(self.requestId, self.chainIds, self.addresses, 0, FETCH_RECIPIENTS_BATCH_COUNT_DEFAULT)
|
||||
let res = backend_activity.getRecipientsAsync(self.sessionId(), self.chainIds, self.addresses, 0, FETCH_RECIPIENTS_BATCH_COUNT_DEFAULT)
|
||||
if res.error != nil or res.result.kind != JBool:
|
||||
self.status.setLoadingRecipients(false)
|
||||
error "error fetching recipients: ", res.error, "; kind ", res.result.kind
|
||||
@ -464,7 +484,7 @@ QtObject:
|
||||
|
||||
proc loadMoreRecipients(self: Controller) {.slot.} =
|
||||
self.status.setLoadingRecipients(true)
|
||||
let res = backend_activity.getRecipientsAsync(self.requestId, self.chainIds, self.addresses, self.recipientsModel.getCount(), FETCH_RECIPIENTS_BATCH_COUNT_DEFAULT)
|
||||
let res = backend_activity.getRecipientsAsync(self.sessionId(), self.chainIds, self.addresses, self.recipientsModel.getCount(), FETCH_RECIPIENTS_BATCH_COUNT_DEFAULT)
|
||||
if res.error != nil:
|
||||
self.status.setLoadingRecipients(false)
|
||||
error "error fetching more recipient entries: ", res.error
|
||||
@ -483,8 +503,12 @@ QtObject:
|
||||
proc globalFilterChanged*(self: Controller, addresses: seq[string], allAddressesSelected: bool, chainIds: seq[int], allChainsEnabled: bool) =
|
||||
if (self.addresses == addresses and self.allAddressesSelected == allAddressesSelected and self.chainIds == chainIds):
|
||||
return
|
||||
|
||||
self.setFilterAddresses(addresses, allAddressesSelected)
|
||||
self.setFilterChains(chainIds, allChainsEnabled)
|
||||
|
||||
# Every change of chains and addresses have to start a new session to get incremental updates when filter is cleared
|
||||
self.newFilterSession()
|
||||
|
||||
proc noLimitTimestamp*(self: Controller): int {.slot.} =
|
||||
return backend_activity.noLimitTimestampForPeriod
|
||||
|
@ -7,6 +7,8 @@ import app/global/global_singleton
|
||||
|
||||
import app_service/service/currency/service
|
||||
|
||||
import app/modules/shared/wallet_utils
|
||||
|
||||
import web3/ethtypes as eth
|
||||
|
||||
# Additional data needed to build an Entry, which is
|
||||
@ -17,13 +19,10 @@ type
|
||||
inAmount*: float64
|
||||
outAmount*: float64
|
||||
|
||||
AmountToCurrencyConvertor* = proc (amount: UInt256, symbol: string): CurrencyAmount
|
||||
|
||||
# Used to display an activity history header entry in the QML UI
|
||||
QtObject:
|
||||
type
|
||||
ActivityEntry* = ref object of QObject
|
||||
valueConvertor: AmountToCurrencyConvertor
|
||||
metadata: backend.ActivityEntry
|
||||
extradata: ExtraData
|
||||
|
||||
@ -33,6 +32,9 @@ QtObject:
|
||||
nftName: string
|
||||
nftImageURL: string
|
||||
|
||||
# true for entries that were changed/added in the current session
|
||||
highlight: bool
|
||||
|
||||
proc setup(self: ActivityEntry) =
|
||||
self.QObject.setup
|
||||
|
||||
@ -42,32 +44,61 @@ QtObject:
|
||||
proc isInTransactionType(self: ActivityEntry): bool =
|
||||
return self.metadata.activityType == backend.ActivityType.Receive or self.metadata.activityType == backend.ActivityType.Mint
|
||||
|
||||
proc newMultiTransactionActivityEntry*(metadata: backend.ActivityEntry, extradata: ExtraData, valueConvertor: AmountToCurrencyConvertor): ActivityEntry =
|
||||
proc extractCurrencyAmount(self: ActivityEntry, currencyService: Service): CurrencyAmount =
|
||||
let amount = if self.isInTransactionType(): self.metadata.amountIn else: self.metadata.amountOut
|
||||
let symbol = if self.isInTransactionType(): self.metadata.symbolIn.get("") else: self.metadata.symbolOut.get("")
|
||||
return currencyAmountToItem(
|
||||
currencyService.parseCurrencyValue(symbol, amount),
|
||||
currencyService.getCurrencyFormat(symbol),
|
||||
)
|
||||
|
||||
proc newMultiTransactionActivityEntry*(metadata: backend.ActivityEntry, extradata: ExtraData, currencyService: Service): ActivityEntry =
|
||||
new(result, delete)
|
||||
result.valueConvertor = valueConvertor
|
||||
result.metadata = metadata
|
||||
result.extradata = extradata
|
||||
result.noAmount = newCurrencyAmount()
|
||||
result.amountCurrency = valueConvertor(
|
||||
if result.isInTransactionType(): metadata.amountIn else: metadata.amountOut,
|
||||
if result.isInTransactionType(): metadata.symbolIn.get("") else: metadata.symbolOut.get(""),
|
||||
)
|
||||
|
||||
result.amountCurrency = result.extractCurrencyAmount(currencyService)
|
||||
|
||||
result.highlight = metadata.isNew
|
||||
|
||||
result.setup()
|
||||
|
||||
proc newTransactionActivityEntry*(metadata: backend.ActivityEntry, fromAddresses: seq[string], extradata: ExtraData, valueConvertor: AmountToCurrencyConvertor): ActivityEntry =
|
||||
proc newTransactionActivityEntry*(metadata: backend.ActivityEntry, fromAddresses: seq[string], extradata: ExtraData, currencyService: Service): ActivityEntry =
|
||||
new(result, delete)
|
||||
result.valueConvertor = valueConvertor
|
||||
result.metadata = metadata
|
||||
result.extradata = extradata
|
||||
|
||||
result.amountCurrency = valueConvertor(
|
||||
if result.isInTransactionType(): metadata.amountIn else: metadata.amountOut,
|
||||
if result.isInTransactionType(): metadata.symbolIn.get("") else: metadata.symbolOut.get(""),
|
||||
)
|
||||
result.noAmount = newCurrencyAmount()
|
||||
|
||||
result.amountCurrency = result.extractCurrencyAmount(currencyService)
|
||||
|
||||
result.highlight = metadata.isNew
|
||||
|
||||
result.setup()
|
||||
|
||||
proc buildMultiTransactionExtraData(metadata: backend.ActivityEntry, currencyService: Service): ExtraData =
|
||||
if metadata.symbolIn.isSome():
|
||||
result.inAmount = currencyService.parseCurrencyValue(metadata.symbolIn.get(), metadata.amountIn)
|
||||
if metadata.symbolOut.isSome():
|
||||
result.outAmount = currencyService.parseCurrencyValue(metadata.symbolOut.get(), metadata.amountOut)
|
||||
|
||||
proc buildTransactionExtraData(metadata: backend.ActivityEntry, currencyService: Service): ExtraData =
|
||||
if metadata.symbolIn.isSome() or metadata.amountIn > 0:
|
||||
result.inAmount = currencyService.parseCurrencyValue(metadata.symbolIn.get(""), metadata.amountIn)
|
||||
if metadata.symbolOut.isSome() or metadata.amountOut > 0:
|
||||
result.outAmount = currencyService.parseCurrencyValue(metadata.symbolOut.get(""), metadata.amountOut)
|
||||
|
||||
proc newActivityEntry*(backendEntry: backend.ActivityEntry, addresses: seq[string], currencyService: Service): ActivityEntry =
|
||||
var ae: entry.ActivityEntry
|
||||
case backendEntry.getPayloadType():
|
||||
of MultiTransaction:
|
||||
let extraData = buildMultiTransactionExtraData(backendEntry, currencyService)
|
||||
ae = newMultiTransactionActivityEntry(backendEntry, extraData, currencyService)
|
||||
of SimpleTransaction, PendingTransaction:
|
||||
let extraData = buildTransactionExtraData(backendEntry, currencyService)
|
||||
ae = newTransactionActivityEntry(backendEntry, addresses, extraData, currencyService)
|
||||
return ae
|
||||
|
||||
proc isMultiTransaction*(self: ActivityEntry): bool {.slot.} =
|
||||
return self.metadata.getPayloadType() == backend.PayloadType.MultiTransaction
|
||||
|
||||
@ -281,3 +312,17 @@ QtObject:
|
||||
|
||||
QtProperty[string] communityId:
|
||||
read = getCommunityId
|
||||
|
||||
proc highlightChanged*(self: ActivityEntry) {.signal.}
|
||||
|
||||
proc getHighlight*(self: ActivityEntry): bool {.slot.} =
|
||||
return self.highlight
|
||||
|
||||
proc doneHighlighting*(self: ActivityEntry) {.slot.} =
|
||||
if self.highlight:
|
||||
self.highlight = false
|
||||
self.highlightChanged()
|
||||
|
||||
QtProperty[bool] highlight:
|
||||
read = getHighlight
|
||||
notify = highlightChanged
|
||||
|
@ -20,13 +20,7 @@ QtObject:
|
||||
eventHandlers: Table[string, EventCallbackProc]
|
||||
walletEventHandlers: Table[string, WalletEventCallbackProc]
|
||||
|
||||
# Ignore events older than this relevantTimestamp
|
||||
relevantTimestamp: int
|
||||
subscribedAddresses: HashSet[string]
|
||||
subscribedChainIDs: HashSet[int]
|
||||
newDataAvailableFn: proc()
|
||||
|
||||
requestId: int
|
||||
sessionId: Option[int32]
|
||||
|
||||
proc setup(self: EventsHandler) =
|
||||
self.QObject.setup
|
||||
@ -40,6 +34,9 @@ QtObject:
|
||||
proc onFilteringUpdateDone*(self: EventsHandler, handler: EventCallbackProc) =
|
||||
self.eventHandlers[backend_activity.eventActivityFilteringUpdate] = handler
|
||||
|
||||
proc onFilteringSessionUpdated*(self: EventsHandler, handler: EventCallbackProc) =
|
||||
self.eventHandlers[backend_activity.eventActivitySessionUpdated] = handler
|
||||
|
||||
proc onGetRecipientsDone*(self: EventsHandler, handler: EventCallbackProc) =
|
||||
self.eventHandlers[backend_activity.eventActivityGetRecipientsDone] = handler
|
||||
|
||||
@ -49,13 +46,10 @@ QtObject:
|
||||
proc onGetCollectiblesDone*(self: EventsHandler, handler: EventCallbackProc) =
|
||||
self.eventHandlers[backend_activity.eventActivityGetCollectiblesDone] = handler
|
||||
|
||||
proc onNewDataAvailable*(self: EventsHandler, handler: proc()) =
|
||||
self.newDataAvailableFn = handler
|
||||
|
||||
proc handleApiEvents(self: EventsHandler, e: Args) =
|
||||
var data = WalletSignal(e)
|
||||
|
||||
if data.requestId.isSome and data.requestId.get() != self.requestId:
|
||||
if not data.requestId.isSome() or not self.sessionId.isSome() or data.requestId.get() != self.sessionId.get():
|
||||
return
|
||||
|
||||
if self.walletEventHandlers.hasKey(data.eventType):
|
||||
@ -66,71 +60,29 @@ QtObject:
|
||||
let responseJson = parseJson(data.message)
|
||||
callback(responseJson)
|
||||
|
||||
proc setupWalletEventHandlers(self: EventsHandler) =
|
||||
let newDataAvailableCallback = proc (data: WalletSignal) =
|
||||
if self.newDataAvailableFn == nil:
|
||||
return
|
||||
|
||||
if data.at > 0 and self.relevantTimestamp > 0 and data.at < self.relevantTimestamp:
|
||||
return
|
||||
|
||||
# Check chain, if any was reported
|
||||
if len(self.subscribedChainIDs) > 0 and data.chainID > 0:
|
||||
var contains = false
|
||||
for chainID in self.subscribedChainIDs:
|
||||
if data.chainID == chainID:
|
||||
contains = true
|
||||
break
|
||||
if not contains:
|
||||
return
|
||||
|
||||
var contains = data.accounts.len == 0
|
||||
# Check addresses if any was reported
|
||||
for address in data.accounts:
|
||||
if address in self.subscribedAddresses:
|
||||
contains = true
|
||||
break
|
||||
|
||||
if not contains:
|
||||
return
|
||||
|
||||
self.newDataAvailableFn()
|
||||
|
||||
# TODO #12120: Replace these specific events with incremental updates events
|
||||
self.walletEventHandlers[EventNewTransfers] = newDataAvailableCallback
|
||||
self.walletEventHandlers[EventPendingTransactionUpdate] = newDataAvailableCallback
|
||||
self.walletEventHandlers[EventMTTransactionUpdate] = newDataAvailableCallback
|
||||
|
||||
proc newEventsHandler*(requestId: int, events: EventEmitter): EventsHandler =
|
||||
proc newEventsHandler*(events: EventEmitter): EventsHandler =
|
||||
new(result, delete)
|
||||
|
||||
result.events = events
|
||||
result.eventHandlers = initTable[string, EventCallbackProc]()
|
||||
|
||||
result.subscribedAddresses = initHashSet[string]()
|
||||
result.subscribedChainIDs = initHashSet[int]()
|
||||
|
||||
result.requestId = requestId
|
||||
|
||||
result.setup()
|
||||
|
||||
result.setupWalletEventHandlers()
|
||||
|
||||
# Register for wallet events
|
||||
let eventsHandler = result
|
||||
result.events.on(SignalType.Wallet.event, proc(e: Args) =
|
||||
eventsHandler.handleApiEvents(e)
|
||||
)
|
||||
|
||||
proc updateRelevantTimestamp*(self: EventsHandler, timestamp: int) =
|
||||
self.relevantTimestamp = timestamp
|
||||
proc getSessionId*(self: EventsHandler): int32 =
|
||||
self.sessionId.get(-1)
|
||||
|
||||
proc updateSubscribedAddresses*(self: EventsHandler, addresses: seq[string]) =
|
||||
self.subscribedAddresses.clear()
|
||||
for address in addresses:
|
||||
self.subscribedAddresses.incl(address)
|
||||
proc setSessionId*(self: EventsHandler, sessionId: int32) =
|
||||
self.sessionId = some(sessionId)
|
||||
|
||||
proc hasSessionId*(self: EventsHandler): bool =
|
||||
self.sessionId.isSome()
|
||||
|
||||
proc clearSessionId*(self: EventsHandler) =
|
||||
self.sessionId = none(int32)
|
||||
|
||||
proc updateSubscribedChainIDs*(self: EventsHandler, chainIDs: seq[int]) =
|
||||
self.subscribedChainIDs.clear()
|
||||
for chainID in chainIDs:
|
||||
self.subscribedChainIDs.incl(chainID)
|
||||
|
@ -111,6 +111,19 @@ QtObject:
|
||||
|
||||
return m.getTransactionIdentity().isSome() and d.transaction.isSome() and m.getTransactionIdentity().get() == d.transaction.get()
|
||||
|
||||
proc addNewEntries*(self: Model, newEntries: seq[entry.ActivityEntry], insertPositions: seq[int]) =
|
||||
let parentModelIndex = newQModelIndex()
|
||||
defer: parentModelIndex.delete
|
||||
|
||||
for j in countdown(newEntries.high, 0):
|
||||
let ae = newEntries[j]
|
||||
let pos = insertPositions[j]
|
||||
self.beginInsertRows(parentModelIndex, pos, pos)
|
||||
self.entries.insert(ae, pos)
|
||||
self.endInsertRows()
|
||||
|
||||
self.countChanged()
|
||||
|
||||
proc updateEntries*(self: Model, updates: seq[backend.Data]) =
|
||||
for i in countdown(self.entries.high, 0):
|
||||
for j in countdown(updates.high, 0):
|
||||
|
@ -9,7 +9,7 @@ import backend/activity as backend_activity
|
||||
QtObject:
|
||||
type
|
||||
Status* = ref object of QObject
|
||||
loadingData: Atomic[int]
|
||||
loadingData: bool
|
||||
errorCode: backend_activity.ErrorCode
|
||||
|
||||
loadingRecipients: Atomic[int]
|
||||
@ -36,7 +36,7 @@ QtObject:
|
||||
proc loadingDataChanged*(self: Status) {.signal.}
|
||||
|
||||
proc setLoadingData*(self: Status, loadingData: bool) =
|
||||
discard fetchAdd(self.loadingData, if loadingData: 1 else: -1)
|
||||
self.loadingData = loadingData
|
||||
self.loadingDataChanged()
|
||||
|
||||
proc loadingRecipientsChanged*(self: Status) {.signal.}
|
||||
@ -72,7 +72,7 @@ QtObject:
|
||||
result.setup()
|
||||
|
||||
proc getLoadingData*(self: Status): bool {.slot.} =
|
||||
return load(self.loadingData) > 0
|
||||
return self.loadingData
|
||||
|
||||
QtProperty[bool] loadingData:
|
||||
read = getLoadingData
|
||||
|
@ -50,11 +50,6 @@ logScope:
|
||||
export io_interface
|
||||
|
||||
type
|
||||
ActivityID = enum
|
||||
History
|
||||
Temporary0
|
||||
Temporary1
|
||||
|
||||
Module* = ref object of io_interface.AccessInterface
|
||||
delegate: delegate_interface.AccessInterface
|
||||
events: EventEmitter
|
||||
@ -145,7 +140,6 @@ proc newModule*(
|
||||
result.transactionService = transactionService
|
||||
result.activityDetailsController = activity_detailsc.newController(currencyService)
|
||||
result.activityController = activityc.newController(
|
||||
int32(ActivityID.History),
|
||||
result.activityDetailsController,
|
||||
currencyService,
|
||||
tokenService,
|
||||
@ -153,20 +147,19 @@ proc newModule*(
|
||||
events)
|
||||
result.tmpActivityControllers = [
|
||||
activityc.newController(
|
||||
int32(ActivityID.Temporary0),
|
||||
result.activityDetailsController,
|
||||
currencyService,
|
||||
tokenService,
|
||||
savedAddressService,
|
||||
events),
|
||||
activityc.newController(
|
||||
int32(ActivityID.Temporary1),
|
||||
result.activityDetailsController,
|
||||
currencyService,
|
||||
tokenService,
|
||||
savedAddressService,
|
||||
events)
|
||||
]
|
||||
|
||||
result.collectibleDetailsController = collectible_detailsc.newController(int32(backend_collectibles.CollectiblesRequestID.WalletAccount), networkService, events)
|
||||
result.filter = initFilter(result.controller)
|
||||
|
||||
|
@ -23,6 +23,8 @@ const eventActivityGetOldestTimestampDone*: string = "wallet-activity-get-oldest
|
||||
const eventActivityFetchTransactionDetails*: string = "wallet-activity-fetch-transaction-details-result"
|
||||
const eventActivityGetCollectiblesDone*: string = "wallet-activity-get-collectibles"
|
||||
|
||||
const eventActivitySessionUpdated*: string = "wallet-activity-session-updated"
|
||||
|
||||
type
|
||||
Period* = object
|
||||
startTimestamp*: int
|
||||
@ -62,6 +64,27 @@ type
|
||||
filterOutAssets*: bool
|
||||
filterOutCollectibles*: bool
|
||||
|
||||
proc `$`*(p: Period): string =
|
||||
if p.startTimestamp == noLimitTimestampForPeriod and p.endTimestamp == noLimitTimestampForPeriod:
|
||||
return "Period(UNLIMITED)"
|
||||
|
||||
return fmt"""Period(
|
||||
startTimestamp: {p.startTimestamp},
|
||||
endTimestamp: {p.endTimestamp}
|
||||
)"""
|
||||
|
||||
proc `$`*(t: ActivityFilter): string =
|
||||
return fmt"""ActivityFilter(
|
||||
period: {t.period},
|
||||
types: {t.types},
|
||||
statuses: {t.statuses},
|
||||
counterpartyAddresses: {t.counterpartyAddresses},
|
||||
assets: {t.assets},
|
||||
collectibles: {t.collectibles},
|
||||
filterOutAssets: {t.filterOutAssets},
|
||||
filterOutCollectibles: {t.filterOutCollectibles}
|
||||
)"""
|
||||
|
||||
proc toJson[T](obj: Option[T]): JsonNode =
|
||||
if obj.isSome:
|
||||
toJson(obj.get())
|
||||
@ -74,6 +97,17 @@ proc fromJson[T](jsonObj: JsonNode, TT: typedesc[Option[T]]): Option[T] =
|
||||
else:
|
||||
return none(T)
|
||||
|
||||
proc fromJson[T](jsonObj: JsonNode, TT: typedesc[seq[T]]): seq[T] =
|
||||
if jsonObj.kind != JArray:
|
||||
error "Expected array, got: ", jsonObj.kind
|
||||
return @[]
|
||||
|
||||
result = newSeq[T](jsonObj.len)
|
||||
for i, elem in jsonObj.getElems():
|
||||
result[i] = fromJson(elem, T)
|
||||
|
||||
return result
|
||||
|
||||
proc `%`*(at: ActivityType): JsonNode {.inline.} =
|
||||
return newJInt(ord(at))
|
||||
|
||||
@ -241,8 +275,6 @@ proc `$`*(pt: ProtocolType): string {.inline.} =
|
||||
return "Hop"
|
||||
of Uniswap:
|
||||
return "Uniswap"
|
||||
else:
|
||||
return ""
|
||||
|
||||
# Mirrors status-go/services/wallet/activity/activity.go TransferType
|
||||
type
|
||||
@ -286,6 +318,7 @@ type
|
||||
transferType*: Option[TransferType]
|
||||
|
||||
communityId*: Option[string]
|
||||
isNew*: bool
|
||||
|
||||
# Mirrors status-go/services/wallet/activity/activity.go EntryData
|
||||
Data* = object
|
||||
@ -312,6 +345,8 @@ type
|
||||
chainIdIn*: Option[ChainId]
|
||||
transferType*: Option[TransferType]
|
||||
|
||||
isNew*: bool
|
||||
|
||||
nftName*: Option[string]
|
||||
nftUrl*: Option[string]
|
||||
|
||||
@ -330,6 +365,18 @@ type
|
||||
hasMore*: bool
|
||||
errorCode*: ErrorCode
|
||||
|
||||
|
||||
# Mirrors services/wallet/activity/session.go EntryUpdate
|
||||
EntryUpdate* = object
|
||||
pos*: int
|
||||
entry*: ActivityEntry
|
||||
|
||||
# Mirrors services/wallet/activity/session.go SessionUpdate
|
||||
SessionUpdate* = object
|
||||
hasNewOnTop*: bool
|
||||
`new`*: seq[EntryUpdate]
|
||||
removed*: seq[TransactionIdentity]
|
||||
|
||||
proc getPayloadType*(ae: ActivityEntry): PayloadType =
|
||||
return ae.payloadType
|
||||
|
||||
@ -366,6 +413,7 @@ proc fromJson*(e: JsonNode, T: typedesc[Data]): Data {.inline.} =
|
||||
const nftNameField = "nftName"
|
||||
const nftUrlField = "nftUrl"
|
||||
const communityIdField = "communityId"
|
||||
const isNewField = "isNew"
|
||||
result = T(
|
||||
payloadType: fromJson(e["payloadType"], PayloadType),
|
||||
transaction: if e.hasKey(transactionField):
|
||||
@ -419,9 +467,11 @@ proc fromJson*(e: JsonNode, T: typedesc[Data]): Data {.inline.} =
|
||||
result.chainIdIn = some(fromJson(e[chainIdInField], ChainId))
|
||||
if e.hasKey(transferTypeField) and e[transferTypeField].kind != JNull:
|
||||
result.transferType = some(fromJson(e[transferTypeField], TransferType))
|
||||
result.isNew = e.hasKey(isNewField) and e[isNewField].getBool()
|
||||
|
||||
proc fromJson*(e: JsonNode, T: typedesc[ActivityEntry]): ActivityEntry {.inline.} =
|
||||
let data = fromJson(e, Data)
|
||||
let zeroValue: UInt256 = "0x0".parse(UInt256, 16)
|
||||
result = T(
|
||||
payloadType: data.payloadType,
|
||||
transaction: data.transaction,
|
||||
@ -429,8 +479,8 @@ proc fromJson*(e: JsonNode, T: typedesc[ActivityEntry]): ActivityEntry {.inline.
|
||||
activityType: data.activityType.get(),
|
||||
activityStatus: data.activityStatus.get(),
|
||||
timestamp: data.timestamp.get(),
|
||||
amountOut: data.amountOut.get(),
|
||||
amountIn: data.amountIn.get(),
|
||||
amountOut: if data.amountOut.isSome: data.amountOut.get() else: zeroValue,
|
||||
amountIn: if data.amountIn.isSome: data.amountIn.get() else: zeroValue,
|
||||
tokenOut: data.tokenOut,
|
||||
tokenIn: data.tokenIn,
|
||||
symbolOut: data.symbolOut,
|
||||
@ -440,7 +490,8 @@ proc fromJson*(e: JsonNode, T: typedesc[ActivityEntry]): ActivityEntry {.inline.
|
||||
chainIdOut: data.chainIdOut,
|
||||
chainIdIn: data.chainIdIn,
|
||||
transferType: data.transferType,
|
||||
communityId: data.communityId
|
||||
communityId: data.communityId,
|
||||
isNew: data.isNew
|
||||
)
|
||||
|
||||
proc `$`*(self: ActivityEntry): string =
|
||||
@ -464,7 +515,8 @@ proc `$`*(self: ActivityEntry): string =
|
||||
chainIdOut* {$self.chainIdOut},
|
||||
chainIdIn* {$self.chainIdIn},
|
||||
transferType* {$self.transferType},
|
||||
communityId* {$self.communityId}
|
||||
communityId* {$self.communityId},
|
||||
isNew* {$self.isNew},
|
||||
)"""
|
||||
|
||||
proc fromJson*(e: JsonNode, T: typedesc[FilterResponse]): FilterResponse {.inline.} =
|
||||
@ -486,14 +538,77 @@ proc fromJson*(e: JsonNode, T: typedesc[FilterResponse]): FilterResponse {.inlin
|
||||
errorCode: ErrorCode(e["errorCode"].getInt())
|
||||
)
|
||||
|
||||
rpc(filterActivityAsync, "wallet"):
|
||||
requestId: int32
|
||||
proc fromJson*(e: JsonNode, T: typedesc[EntryUpdate]): T {.inline.} =
|
||||
const posField = "pos"
|
||||
const entryField = "entry"
|
||||
result = T(
|
||||
pos: if e.hasKey(posField): e[posField].getInt() else: -1,
|
||||
entry: if e.hasKey(entryField): fromJson(e[entryField], ActivityEntry) else: ActivityEntry()
|
||||
)
|
||||
|
||||
proc fromJson*(e: JsonNode, T: typedesc[SessionUpdate]): T {.inline.} =
|
||||
const hasNewOnTopField = "hasNewOnTop"
|
||||
const newField = "new"
|
||||
const removedField = "removed"
|
||||
let hasNewOnTop = e.hasKey(hasNewOnTopField) and e[hasNewOnTopField].getBool()
|
||||
let newEntries = if e.hasKey(newField): fromJson(e[newField], seq[EntryUpdate]) else: @[]
|
||||
let removed = if e.hasKey(removedField): fromJson(e[removedField], seq[TransactionIdentity]) else: @[]
|
||||
result = T(
|
||||
hasNewOnTop: hasNewOnTop,
|
||||
`new`: newEntries,
|
||||
removed: removed
|
||||
)
|
||||
|
||||
rpc(startActivityFilterSession, "wallet"):
|
||||
addresses: seq[string]
|
||||
allAddresses: bool
|
||||
chainIds: seq[ChainId]
|
||||
filter: ActivityFilter
|
||||
offset: int
|
||||
limit: int
|
||||
count: int
|
||||
|
||||
rpc(updateActivityFilterForSession, "wallet"):
|
||||
sessionId: int32
|
||||
filter: ActivityFilter
|
||||
count: int
|
||||
|
||||
rpc(resetActivityFilterSession, "wallet"):
|
||||
sessionId: int32
|
||||
count: int
|
||||
|
||||
rpc(getMoreForActivityFilterSession, "wallet"):
|
||||
sessionId: int32
|
||||
count: int
|
||||
|
||||
rpc(stopActivityFilterSession, "wallet"):
|
||||
sessionId: int32
|
||||
|
||||
# returns (sessionId, success)
|
||||
proc newActivityFilterSession*(
|
||||
addresses: seq[string],
|
||||
allAddresses: bool,
|
||||
chainIds: seq[ChainId],
|
||||
filter: ActivityFilter,
|
||||
count: int,
|
||||
): (int32, bool) {.inline.} =
|
||||
try:
|
||||
let res = startActivityFilterSession(addresses, allAddresses, chainIds, filter, count)
|
||||
if res.error != nil:
|
||||
error "error starting a new session of activity fitlering: ", res.error
|
||||
return (int32(-1), false)
|
||||
return (int32(res.result.getInt()), true)
|
||||
except:
|
||||
return (int32(-1), false)
|
||||
|
||||
proc updateFilterForSession*(sessionId: int32, filter: ActivityFilter, count: int): bool {.inline.} =
|
||||
try:
|
||||
let res = updateActivityFilterForSession(sessionId, filter, count)
|
||||
if res.error != nil:
|
||||
error "error updating fitler for session: ", res.error
|
||||
return false
|
||||
except:
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
# see services/wallet/activity/service.go GetRecipientsResponse
|
||||
type GetRecipientsResponse* = object
|
||||
|
@ -111,6 +111,13 @@ type
|
||||
hash*: string
|
||||
address*: string
|
||||
|
||||
proc fromJson*(t: JsonNode, T: typedesc[TransactionIdentity]): T {.inline.} =
|
||||
result = TransactionIdentity(
|
||||
chainId: if t.hasKey("chainId"): t["chainId"].getInt() else: 0,
|
||||
hash: if t.hasKey("hash"): t["hash"].getStr() else: "",
|
||||
address: if t.hasKey("address"): t["address"].getStr() else: "",
|
||||
)
|
||||
|
||||
proc hash*(ti: TransactionIdentity): Hash =
|
||||
var h: Hash = 0
|
||||
h = h !& hash(ti.chainId)
|
||||
|
@ -33,7 +33,6 @@ type
|
||||
multiTxType* {.serializedFieldName("type").}: MultiTransactionType
|
||||
|
||||
# Mirrors the transfer events from status-go, services/wallet/transfer/commands.go
|
||||
const EventNewTransfers*: string = "new-transfers"
|
||||
const EventFetchingRecentHistory*: string = "recent-history-fetching"
|
||||
const EventRecentHistoryReady*: string = "recent-history-ready"
|
||||
const EventFetchingHistoryError*: string = "fetching-history-error"
|
||||
@ -41,7 +40,6 @@ const EventNonArchivalNodeDetected*: string = "non-archival-node-detected"
|
||||
|
||||
# Mirrors the pending transfer event from status-go, status-go/services/wallet/transfer/transaction.go
|
||||
const EventPendingTransactionUpdate*: string = "pending-transaction-update"
|
||||
const EventMTTransactionUpdate*: string = "multi-transaction-update"
|
||||
|
||||
proc `$`*(self: MultiTransactionDto): string =
|
||||
return fmt"""MultiTransactionDto(
|
||||
|
@ -11,6 +11,7 @@ import shared.controls 1.0
|
||||
SplitView {
|
||||
id: root
|
||||
|
||||
// mirrors ActivityEntry defined in src/app/modules/main/wallet_section/activity/entry.nim
|
||||
readonly property QtObject mockupModelData: QtObject {
|
||||
readonly property int timestamp: Date.now() / 1000
|
||||
readonly property int status: ctrlStatus.currentValue
|
||||
@ -38,6 +39,12 @@ SplitView {
|
||||
readonly property string chainId: "NETWORKID"
|
||||
readonly property string chainIdIn: "NETWORKID-IN"
|
||||
readonly property string chainIdOut: "NETWORKID-OUT"
|
||||
|
||||
readonly property bool highlight: _highlight
|
||||
function doneHighlighting() {
|
||||
_highlight = false
|
||||
}
|
||||
property bool _highlight: false
|
||||
}
|
||||
|
||||
SplitView {
|
||||
@ -175,6 +182,13 @@ SplitView {
|
||||
id: ctrlMultiTrans
|
||||
text: "Multi transaction"
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "New transaction"
|
||||
onClicked: {
|
||||
mockupModelData._highlight = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
54
test/nim/activity_tests.nim
Normal file
54
test/nim/activity_tests.nim
Normal file
@ -0,0 +1,54 @@
|
||||
import unittest, json, options
|
||||
|
||||
import backend/activity
|
||||
|
||||
const testOneNewJsonData = "{\"new\": [{\"pos\": 3, \"entry\": {\"payloadType\": 1, \"id\": 12, \"activityType\": 1, \"activityStatus\": 2, \"timestamp\": 1234567890, \"isNew\": true}}]}"
|
||||
const testOneNewJsonDataMissingIsNew = "{\"new\": [{\"pos\": 3, \"entry\": {\"payloadType\": 1, \"id\": 12, \"activityType\": 1, \"activityStatus\": 2, \"timestamp\": 1234567890}}]}"
|
||||
const oneRemovedJsonTestData = "{\"removed\":[{\"chainId\": 7, \"hash\": \"0x5\", \"address\": \"0x6\"}]}"
|
||||
const testAllSetJsonData = "{\"hasNewOnTop\": true, \"new\": [{\"pos\": 3, \"entry\": {\"payloadType\": 1, \"id\": 12, \"activityType\": 1, \"activityStatus\": 2, \"timestamp\": 1234567890, \"isNew\": true}}], \"removed\":[{\"chainId\": 7, \"hash\": \"0x5\", \"address\": \"0x6\"}]}"
|
||||
|
||||
suite "activity filter API json parsing":
|
||||
|
||||
test "just hasNewOnTop":
|
||||
const jsonData = "{\"hasNewOnTop\": true}"
|
||||
let jsonNode = json.parseJson(jsonData)
|
||||
|
||||
let parsed = fromJson(jsonNode, activity.SessionUpdate)
|
||||
check(parsed.hasNewOnTop == true)
|
||||
check(len(parsed.new) == 0)
|
||||
|
||||
test "just new":
|
||||
let jsonNode = json.parseJson(testOneNewJsonData)
|
||||
|
||||
let parsed = fromJson(jsonNode, activity.SessionUpdate)
|
||||
check(len(parsed.new) == 1)
|
||||
let update = parsed.new[0]
|
||||
check(update.pos == 3)
|
||||
check(update.entry.isNew == true)
|
||||
check(update.entry.getMultiTransactionId().get(-1) == 12)
|
||||
check(update.entry.timestamp == 1234567890)
|
||||
|
||||
test "just isNew optional":
|
||||
let jsonNode = json.parseJson(testOneNewJsonDataMissingIsNew)
|
||||
|
||||
let parsed = fromJson(jsonNode, activity.SessionUpdate)
|
||||
check(len(parsed.new) == 1)
|
||||
check(parsed.new[0].entry.isNew == false)
|
||||
|
||||
test "just removed":
|
||||
let jsonNode = json.parseJson(oneRemovedJsonTestData)
|
||||
|
||||
let parsed = fromJson(jsonNode, activity.SessionUpdate)
|
||||
check(len(parsed.removed) == 1)
|
||||
let removed = parsed.removed[0]
|
||||
check(removed.chainId == 7)
|
||||
check(removed.hash == "0x5")
|
||||
check(removed.address == "0x6")
|
||||
|
||||
test "all set":
|
||||
let jsonNode = json.parseJson(testAllSetJsonData)
|
||||
|
||||
let parsed = fromJson(jsonNode, activity.SessionUpdate)
|
||||
check(parsed.hasNewOnTop == true)
|
||||
check(len(parsed.new) == 1)
|
||||
check(len(parsed.removed) == 1)
|
@ -114,6 +114,7 @@ func WaitForWalletEvents(eventQueue chan GoEvent, eventNames []walletevent.Event
|
||||
|
||||
// WaitForWalletEvents waits for the given events to be received on the eventQueue.
|
||||
// It returns the wallet events in the order they are received.
|
||||
// If a condition is provided, only returning true on the respective call will discard that event.
|
||||
func WaitForWalletEventsWithOptionals(eventQueue chan GoEvent, eventNames []walletevent.EventType, timeout time.Duration, condition func(walletEvent *walletevent.Event) bool, optionalEventNames []walletevent.EventType) (walletEvents []*walletevent.Event, err error) {
|
||||
if len(eventNames) == 0 {
|
||||
return nil, errors.New("no event names provided")
|
||||
|
@ -4,7 +4,6 @@
|
||||
package wallet
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -42,13 +41,14 @@ func TestActivityIncrementalUpdates_NoFilterNewPendingTransactions(t *testing.T)
|
||||
// Wait for EventActivitySessionUpdated signal triggered by the first EventPendingTransactionUpdate
|
||||
update, err := helpers.WaitForWalletEventGetPayload[activity.SessionUpdate](td.eventQueue, activity.EventActivitySessionUpdated, 60*time.Second)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, update.HasNewEntries)
|
||||
require.True(t, *update.HasNewEntries)
|
||||
require.NotNil(t, update.HasNewOnTop)
|
||||
require.True(t, *update.HasNewOnTop)
|
||||
|
||||
// TODO #12120 check EventActivitySessionUpdated due to transactions.EventPendingTransactionStatusChanged
|
||||
// statusPayload, err := helpers.WaitForWalletEventGetPayload[transactions.StatusChangedPayload](td.eventQueue, , 60*time.Second)
|
||||
// TODO #12120 check EventActivitySessionUpdated due to EventPendingTransactionStatusChanged
|
||||
// statusPayload, err := helpers.WaitForWalletEventGetPayload[transactions.StatusChangedPayload](td.eventQueue, activity.EventActivitySessionUpdated, 60*time.Second)
|
||||
// require.NoError(t, err)
|
||||
// require.Equal(t, transactions.Success, statusPayload.Status)
|
||||
// require.NotNil(t, update.HasNewOnTop)
|
||||
// require.True(t, *update.HasNewOnTop)
|
||||
|
||||
// Start history download to cleanup pending transactions
|
||||
_, err = helpers.CallPrivateMethod("wallet_checkRecentHistoryForChainIDs", []interface{}{[]uint64{5}, []types.Address{td.sender.Address, td.recipient.Address}})
|
||||
@ -65,14 +65,11 @@ func TestActivityIncrementalUpdates_NoFilterNewPendingTransactions(t *testing.T)
|
||||
120*time.Second,
|
||||
func(e *walletevent.Event) bool {
|
||||
if e.Type == activity.EventActivitySessionUpdated {
|
||||
var parsedPayload activity.SessionUpdate
|
||||
err := json.Unmarshal(([]byte)(e.Message), &parsedPayload)
|
||||
update, err = walletevent.GetPayload[activity.SessionUpdate](*e)
|
||||
require.NoError(t, err)
|
||||
update = &parsedPayload
|
||||
|
||||
// TODO #12120 enable after implementing remove and update
|
||||
// require.NotNil(t, update.HasNewEntries)
|
||||
// require.True(t, *update.HasNewEntries)
|
||||
require.NotNil(t, update.HasNewOnTop)
|
||||
require.True(t, *update.HasNewOnTop)
|
||||
//require.NotNil(t, update.Removed)
|
||||
//require.True(t, *update.Removed)
|
||||
return false
|
||||
@ -87,12 +84,12 @@ func TestActivityIncrementalUpdates_NoFilterNewPendingTransactions(t *testing.T)
|
||||
[]walletevent.EventType{activity.EventActivitySessionUpdated, transfer.EventFetchingHistoryError},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, update, "EventActivitySessionUpdated signal WASN'T triggered by the second EventPendingTransactionUpdate during history download")
|
||||
require.NotNil(t, update.HasNewEntries)
|
||||
require.True(t, *update.HasNewEntries)
|
||||
require.NotNil(t, update, "EventActivitySessionUpdated signal was triggered by the second EventPendingTransactionUpdate during history download")
|
||||
require.NotNil(t, update.HasNewOnTop)
|
||||
require.True(t, *update.HasNewOnTop)
|
||||
|
||||
// Start history download to cleanup pending transactions
|
||||
_, err = helpers.CallPrivateMethodAndGetT[interface{}]("wallet_resetFilterSession", []interface{}{sessionID, 3})
|
||||
_, err = helpers.CallPrivateMethodAndGetT[interface{}]("wallet_resetActivityFilterSession", []interface{}{sessionID, 3})
|
||||
require.NoError(t, err)
|
||||
|
||||
updatedRes, err := helpers.WaitForWalletEventsGetMap(td.eventQueue, []walletevent.EventType{activity.EventActivityFilteringDone}, 1*time.Second)
|
||||
|
@ -158,7 +158,7 @@ StatusListItem {
|
||||
bgWidth: width + 2
|
||||
bgHeight: height + 2
|
||||
bgRadius: bgWidth / 2
|
||||
bgColor: Style.current.name === Constants.lightThemeName && Constants.isDefaultTokenIcon(root.tokenImage) ?
|
||||
bgColor: d.lightTheme && Constants.isDefaultTokenIcon(root.tokenImage) ?
|
||||
Theme.palette.white : "transparent"
|
||||
color: "transparent"
|
||||
isImage: !loading
|
||||
@ -174,6 +174,9 @@ StatusListItem {
|
||||
property int titlePixelSize: 15
|
||||
property int subtitlePixelSize: 13
|
||||
property bool showRetryButton: false
|
||||
|
||||
readonly property bool isLightTheme: Style.current.name === Constants.lightThemeName
|
||||
property color animatedBgColor
|
||||
}
|
||||
|
||||
function getDetailsString(detailsObj) {
|
||||
@ -530,6 +533,12 @@ StatusListItem {
|
||||
rightPadding: 16
|
||||
enabled: !loading
|
||||
loading: !isModelDataValid
|
||||
color: {
|
||||
if (bgColorAnimation.running) {
|
||||
return d.animatedBgColor
|
||||
}
|
||||
return sensor.containsMouse ? Theme.palette.baseColor5 : Style.current.transparent
|
||||
}
|
||||
|
||||
statusListItemIcon.active: (loading || root.asset.name)
|
||||
asset {
|
||||
@ -863,4 +872,29 @@ StatusListItem {
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
ColorAnimation {
|
||||
id: bgColorAnimation
|
||||
|
||||
target: d
|
||||
property: "animatedBgColor"
|
||||
from: d.isLightTheme ? "#33869eff" : "#1a4360df"
|
||||
to: "transparent"
|
||||
duration: 1000
|
||||
alwaysRunToEnd: true
|
||||
|
||||
onStopped: {
|
||||
modelData.doneHighlighting()
|
||||
}
|
||||
}
|
||||
// Add a delay before the animation to make it easier to notice when scrolling
|
||||
Timer {
|
||||
id: delayAnimation
|
||||
interval: 250
|
||||
running: root.visible && isModelDataValid && modelData.highlight
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
bgColorAnimation.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,8 +32,8 @@ QtObject {
|
||||
property var marketValueStore: TokenMarketValuesStore{}
|
||||
property var allNetworks: networksModule.all
|
||||
|
||||
function resetFilter() {
|
||||
walletSectionInst.activityController.updateFilter()
|
||||
function resetActivityData() {
|
||||
walletSectionInst.activityController.resetActivityData()
|
||||
}
|
||||
|
||||
// TODO remove all these by linking chainId for networks and activity using LeftJoinModel
|
||||
@ -160,7 +160,7 @@ QtObject {
|
||||
walletSectionInst.activityController.loadMoreItems()
|
||||
}
|
||||
|
||||
function updateTransactionFilter() {
|
||||
function updateTransactionFilterIfDirty() {
|
||||
if (transactionActivityStatus.isFilterDirty)
|
||||
walletSectionInst.activityController.updateFilter()
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ ColumnLayout {
|
||||
target: RootStore.transactionActivityStatus
|
||||
enabled: root.visible
|
||||
function onIsFilterDirtyChanged() {
|
||||
RootStore.updateTransactionFilter()
|
||||
RootStore.updateTransactionFilterIfDirty()
|
||||
}
|
||||
function onFilterChainsChanged() {
|
||||
WalletStores.RootStore.currentActivityFiltersStore.updateCollectiblesModel()
|
||||
@ -90,13 +90,7 @@ ColumnLayout {
|
||||
|
||||
property bool firstSectionHeaderLoaded: false
|
||||
|
||||
property double lastRefreshTime
|
||||
readonly property int maxSecondsBetweenRefresh: 3
|
||||
function refreshData() {
|
||||
RootStore.resetFilter()
|
||||
d.lastRefreshTime = Date.now()
|
||||
newTransactions.visible = false
|
||||
}
|
||||
|
||||
property string openTxDetailsHash
|
||||
|
||||
@ -272,8 +266,8 @@ ColumnLayout {
|
||||
|
||||
text: qsTr("New transactions")
|
||||
|
||||
visible: false
|
||||
onClicked: d.refreshData()
|
||||
visible: RootStore.newDataAvailable && !RootStore.loadingHistoryTransactions
|
||||
onClicked: RootStore.resetActivityData()
|
||||
|
||||
icon.name: "arrow-up"
|
||||
|
||||
@ -281,41 +275,6 @@ ColumnLayout {
|
||||
type: StatusButton.Primary
|
||||
size: StatusBaseButton.Size.Tiny
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: RootStore
|
||||
|
||||
function onNewDataAvailableChanged() {
|
||||
if (!d.lastRefreshTime || ((Date.now() - d.lastRefreshTime) > (1000 * d.maxSecondsBetweenRefresh))) {
|
||||
// Show `New transactions` button only when filter is applied
|
||||
if (!WalletStores.RootStore.currentActivityFiltersStore.filtersSet) {
|
||||
d.refreshData()
|
||||
return
|
||||
}
|
||||
|
||||
newTransactions.visible = RootStore.newDataAvailable
|
||||
return
|
||||
}
|
||||
|
||||
if (showRefreshButtonTimer.running) {
|
||||
if (!RootStore.newDataAvailable) {
|
||||
showRefreshButtonTimer.stop()
|
||||
newTransactions.visible = false
|
||||
}
|
||||
} else if(RootStore.newDataAvailable) {
|
||||
showRefreshButtonTimer.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: showRefreshButtonTimer
|
||||
|
||||
interval: 2000
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: newTransactions.visible = RootStore.newDataAvailable
|
||||
}
|
||||
}
|
||||
|
||||
StatusMenu {
|
||||
|
2
vendor/status-go
vendored
2
vendor/status-go
vendored
@ -1 +1 @@
|
||||
Subproject commit 6522d52016eae3d848c228a82d79dde902d69688
|
||||
Subproject commit e1c7c715aa3701791c5280e5c7869af7675bc1ee
|
Loading…
x
Reference in New Issue
Block a user