feat(wallet) reload on new transaction downloaded quick win

Show a reload hint button for user to refresh the filter

Updates #11233
This commit is contained in:
Stefan 2023-07-04 23:29:34 +01:00 committed by Stefan Dunca
parent d17f2c70f1
commit 7a9c76966d
8 changed files with 121 additions and 12 deletions

View File

@ -181,6 +181,8 @@ QtObject:
proc updateFilter*(self: Controller) {.slot.} =
self.status.setLoadingData(true)
self.model.resetModel(@[])
self.eventsHandler.updateSubscribedAddresses(self.addresses)
self.status.setNewDataAvailable(false)
let response = backend_activity.filterActivityAsync(self.addresses, seq[backend_activity.ChainId](self.chainIds), self.currentActivityFilter, 0, FETCH_BATCH_COUNT_DEFAULT)
if response.error != nil:
@ -250,6 +252,10 @@ QtObject:
self.status.setStartTimestamp(res.timestamp)
)
self.eventsHandler.onNewDataAvailable(proc () =
self.status.setNewDataAvailable(true)
)
proc newController*(transactionsModule: transactions_module.AccessInterface,
currencyService: currency_service.Service,
tokenService: token_service.Service,

View File

@ -11,16 +11,21 @@ import app/core/eventemitter
import app/core/signals/types
import backend/activity as backend_activity
import backend/transactions
type EventCallbackProc = proc (eventObject: JsonNode)
type WalletEventCallbackProc = proc (data: WalletSignal)
# 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]
walletEventHandlers: Table[string, WalletEventCallbackProc]
subscribedAddresses: HashSet[string]
newDataAvailableFn: proc()
proc setup(self: EventsHandler) =
self.QObject.setup
@ -37,10 +42,16 @@ QtObject:
proc onGetOldestTimestampDone*(self: EventsHandler, handler: EventCallbackProc) =
self.eventHandlers[backend_activity.eventActivityGetOldestTimestampDone] = handler
proc onNewDataAvailable*(self: EventsHandler, handler: proc()) =
self.newDataAvailableFn = handler
proc handleApiEvents(self: EventsHandler, e: Args) =
var data = WalletSignal(e)
if self.eventHandlers.hasKey(data.eventType):
if self.walletEventHandlers.hasKey(data.eventType):
let callback = self.walletEventHandlers[data.eventType]
callback(data)
elif self.eventHandlers.hasKey(data.eventType):
var responseJson: JsonNode
responseJson = parseJson(data.message)
@ -52,14 +63,40 @@ QtObject:
else:
discard
proc setupWalletEventHandlers(self: EventsHandler) =
self.walletEventHandlers[EventNewTransfers] = proc (data: WalletSignal) =
if self.newDataAvailableFn == nil:
return
var contains = false
for address in data.accounts:
if address in self.subscribedAddresses:
contains = true
break
if contains:
# TODO: throttle down the number of events to one per 1 seconds until the backend supports subscription
self.newDataAvailableFn()
proc newEventsHandler*(events: EventEmitter): EventsHandler =
new(result, delete)
result.events = events
result.eventHandlers = initTable[string, EventCallbackProc]()
result.subscribedAddresses = initHashSet[string]()
result.setup()
result.setupWalletEventHandlers()
# Register for wallet events
let eventsHandler = result
result.events.on(SignalType.Wallet.event, proc(e: Args) =
eventsHandler.handleApiEvents(e)
)
)
proc updateSubscribedAddresses*(self: EventsHandler, addresses: seq[string]) =
self.subscribedAddresses.clear()
for address in addresses:
self.subscribedAddresses.incl(address)

View File

@ -19,6 +19,8 @@ QtObject:
startTimestamp: int
newDataAvailable: bool
proc setup(self: Status) =
self.QObject.setup
@ -96,4 +98,17 @@ QtObject:
QtProperty[int] startTimestamp:
read = getStartTimestamp
notify = startTimestampChanged
notify = startTimestampChanged
proc newDataAvailableChanged*(self: Status) {.signal.}
proc setNewDataAvailable*(self: Status, newDataAvailable: bool) =
self.newDataAvailable = newDataAvailable
self.newDataAvailableChanged()
proc getNewDataAvailable*(self: Status): bool {.slot.} =
return self.newDataAvailable
QtProperty[bool] newDataAvailable:
read = getNewDataAvailable
notify = newDataAvailableChanged

View File

@ -42,6 +42,7 @@ const SIGNAL_TRANSACTIONS_LOADED* = "transactionsLoaded"
const SIGNAL_TRANSACTION_SENT* = "transactionSent"
const SIGNAL_SUGGESTED_ROUTES_READY* = "suggestedRoutesReady"
const SIGNAL_TRANSACTION_LOADING_COMPLETED_FOR_ALL_NETWORKS* = "transactionsLoadingCompleteForAllNetworks"
# TODO: soon to be removed with old transactions module
const SIGNAL_HISTORY_FETCHING* = "historyFetching"
const SIGNAL_HISTORY_READY* = "historyReady"
const SIGNAL_HISTORY_NON_ARCHIVAL_NODE* = "historyNonArchivalNode"
@ -148,15 +149,13 @@ QtObject:
self.events.on(SignalType.Wallet.event) do(e:Args):
var data = WalletSignal(e)
case data.eventType:
of "recent-history-fetching":
self.events.emit(SIGNAL_HISTORY_FETCHING, HistoryArgs(addresses: data.accounts))
of "recent-history-ready":
of transactions.EventRecentHistoryReady:
for account in data.accounts:
self.loadTransactions(account, stint.fromHex(Uint256, "0x0"))
self.events.emit(SIGNAL_HISTORY_READY, HistoryArgs(addresses: data.accounts))
of "non-archival-node-detected":
of transactions.EventNonArchivalNodeDetected:
self.events.emit(SIGNAL_HISTORY_NON_ARCHIVAL_NODE, Args())
of "fetching-history-error":
of transactions.EventFetchingHistoryError:
self.events.emit(SIGNAL_HISTORY_ERROR, Args())
proc getPendingTransactions*(self: Service): seq[TransactionDto] =

View File

@ -7,7 +7,9 @@ import ../app_service/common/utils
# mirrors the MultiTransactionType from status-go, services/wallet/transfer/transaction.go
type
MultiTransactionType* = enum
MultiTransactionSend = 0, MultiTransactionSwap = 1, MultiTransactionBridge = 2
MultiTransactionSend = 0,
MultiTransactionSwap = 1,
MultiTransactionBridge = 2
MultiTransactionCommandDto* = ref object of RootObj
fromAddress* {.serializedFieldName("fromAddress").}: string
@ -28,6 +30,13 @@ type
toAmount* {.serializedFieldName("toAmount").}: string
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"
const EventNonArchivalNodeDetected*: string = "non-archival-node-detected"
proc getTransactionByHash*(chainId: int, hash: string): RpcResponse[JsonNode] {.raises: [Exception].} =
core.callPrivateRPCWithChainId("eth_getTransactionByHash", chainId, %* [hash])

View File

@ -104,6 +104,9 @@ QtObject {
property color white: getColor('white')
property color transparent: "#00000000"
property color blue: getColor('blue')
property color darkBlue: getColor('blue2')
property color dropShadow: getColor('black', 0.12)
property color dropShadow2
property color backdropColor: getColor('black', 0.4)

View File

@ -39,10 +39,15 @@ QtObject {
: null
property var historyTransactions: Global.appIsReady? walletSection.activityController.model : null
readonly property bool loadingHistoryTransactions: Global.appIsReady && walletSection.activityController.status.loadingData
readonly property bool newDataAvailable: Global.appIsReady && walletSection.activityController.status.newDataAvailable
property bool isNonArchivalNode: history ? history.isNonArchivalNode
: false
property var marketValueStore: TokenMarketValuesStore{}
function resetFilter() {
walletSection.activityController.updateFilter()
}
function getNetworkColor(chainId) {
return networksModule.all.getChainColor(chainId)
}

View File

@ -35,6 +35,7 @@ ColumnLayout {
readonly property bool isInitialLoading: RootStore.loadingHistoryTransactions && transactionListRoot.count === 0
property var activityFiltersStore: WalletStores.ActivityFiltersStore{}
readonly property int loadingSectionWidth: 56
readonly property int topSectionMargin: 20
}
StyledText {
@ -124,12 +125,16 @@ ColumnLayout {
delegate: transactionDelegate
headerPositioning: ListView.OverlayHeader
header: headerComp
footer: footerComp
ScrollBar.vertical: StatusScrollBar {}
section.property: "date"
topMargin: d.isInitialLoading ? 0 : -20 // Top margin for first section. Section cannot have different sizes
// Adding some magic number to align the top headerComp with the top of the list.
// TODO: have to be fixed properly and match the design
topMargin: d.isInitialLoading ? 0 : -(2 * d.topSectionMargin + 9) // Top margin for first section. Section cannot have different sizes
section.delegate: ColumnLayout {
id: sectionDelegate
@ -141,7 +146,7 @@ ColumnLayout {
Separator {
Layout.fillWidth: true
Layout.topMargin: 20
Layout.topMargin: d.topSectionMargin
implicitHeight: 1
}
@ -329,4 +334,34 @@ ColumnLayout {
}
}
}
Component {
id: headerComp
Item {
width: root.width
height: dataUpdatedButton.implicitHeight
StatusButton {
id: dataUpdatedButton
anchors.centerIn: parent
text: qsTr("New transactions")
visible: RootStore.newDataAvailable
onClicked: RootStore.resetFilter()
icon.name: "arrow-up"
radius: 36
textColor: Theme.palette.indirectColor1
normalColor: Theme.palette.primaryColor1
hoverColor: Theme.palette.miscColor1
size: StatusBaseButton.Size.Tiny
}
z: 3
}
}
}