mirror of
https://github.com/status-im/status-desktop.git
synced 2025-02-16 16:47:24 +00:00
feat(wallet): extend API to continue loading activity
Bump status-go with the refactoring of hasMore and add offset Add support for continuously loading activity in the wallet API. Extend the debugging demo with continuously loading Closes #10994
This commit is contained in:
parent
be6fe0633e
commit
4bcbe51154
@ -34,7 +34,7 @@ QtObject:
|
||||
loadingData: bool
|
||||
errorCode: backend_activity.ErrorCode
|
||||
|
||||
# TODO remove chains and addresses to use the app one
|
||||
# TODO remove chains and addresses after using ground truth
|
||||
addresses: seq[string]
|
||||
chainIds: seq[int]
|
||||
|
||||
@ -138,15 +138,9 @@ QtObject:
|
||||
self.errorCode = backend_activity.ErrorCode(errorCode)
|
||||
self.errorCodeChanged()
|
||||
|
||||
proc refreshData(self: Controller) =
|
||||
let response = backend_activity.filterActivityAsync(self.addresses, self.chainIds, self.currentActivityFilter, 0, FETCH_BATCH_COUNT_DEFAULT)
|
||||
if response.error != nil:
|
||||
error "error fetching activity entries: ", response.error
|
||||
return
|
||||
|
||||
self.setLoadingData(true)
|
||||
|
||||
proc processResponse(self: Controller, response: JsonNode) =
|
||||
defer: self.setLoadingData(false)
|
||||
|
||||
let res = fromJson(response, backend_activity.FilterResponse)
|
||||
|
||||
defer: self.setErrorCode(res.errorCode.int)
|
||||
@ -156,11 +150,23 @@ QtObject:
|
||||
return
|
||||
|
||||
let entries = self.backendToPresentation(res.activities)
|
||||
self.model.setEntries(entries, res.thereMightBeMore)
|
||||
self.setLoadingData(false)
|
||||
self.model.setEntries(entries, res.offset, res.hasMore)
|
||||
|
||||
proc updateFilter*(self: Controller) {.slot.} =
|
||||
self.refreshData()
|
||||
self.setLoadingData(true)
|
||||
let response = backend_activity.filterActivityAsync(self.addresses, self.chainIds, self.currentActivityFilter, 0, FETCH_BATCH_COUNT_DEFAULT)
|
||||
if response.error != nil:
|
||||
error "error fetching activity entries: ", response.error
|
||||
self.setLoadingData(false)
|
||||
return
|
||||
|
||||
proc loadMoreItems(self: Controller) {.slot.} =
|
||||
let response = backend_activity.filterActivityAsync(self.addresses, self.chainIds, self.currentActivityFilter, self.model.getCount(), FETCH_BATCH_COUNT_DEFAULT)
|
||||
if response.error != nil:
|
||||
error "error fetching activity entries: ", response.error
|
||||
return
|
||||
|
||||
self.setLoadingData(true)
|
||||
|
||||
proc setFilterTime*(self: Controller, startTimestamp: int, endTimestamp: int) {.slot.} =
|
||||
self.currentActivityFilter.period = backend_activity.newPeriod(startTimestamp, endTimestamp)
|
||||
|
@ -78,7 +78,7 @@ QtObject:
|
||||
return self.transaction
|
||||
|
||||
proc getSender*(self: ActivityEntry): string {.slot.} =
|
||||
# TODO: lookup sender's name from addressbook and cache it or in advance
|
||||
# TODO: lookup sender's name
|
||||
if self.isMultiTransaction():
|
||||
return self.multi_transaction.fromAddress
|
||||
|
||||
@ -88,7 +88,7 @@ QtObject:
|
||||
read = getSender
|
||||
|
||||
proc getRecipient*(self: ActivityEntry): string {.slot.} =
|
||||
# TODO: lookup recipient name from addressbook and cache it or in advance
|
||||
# TODO: lookup recipient name
|
||||
if self.isMultiTransaction():
|
||||
return self.multi_transaction.toAddress
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
import NimQml, Tables, strutils, strformat, sequtils
|
||||
import NimQml, Tables, strutils, strformat, sequtils, logging
|
||||
|
||||
import ./entry
|
||||
|
||||
# TODO - DEV: remove this
|
||||
# TODO - DEV: remove these imports and associated code after all the metadata is returned by the filter API
|
||||
import app_service/service/transaction/dto
|
||||
import app/modules/shared_models/currency_amount
|
||||
import ../transactions/item as transaction
|
||||
@ -71,15 +71,24 @@ QtObject:
|
||||
self.hasMore = hasMore
|
||||
self.hasMoreChanged()
|
||||
|
||||
proc setEntries*(self: Model, entries: seq[ActivityEntry], hasMore: bool) =
|
||||
self.beginResetModel()
|
||||
self.entries = entries
|
||||
self.endResetModel()
|
||||
proc setEntries*(self: Model, newEntries: seq[ActivityEntry], offset: int, hasMore: bool) =
|
||||
if offset == 0:
|
||||
self.beginResetModel()
|
||||
self.entries = newEntries
|
||||
self.endResetModel()
|
||||
else:
|
||||
let parentModelIndex = newQModelIndex()
|
||||
defer: parentModelIndex.delete
|
||||
|
||||
if offset != self.entries.len:
|
||||
error "offset != self.entries.len"
|
||||
return
|
||||
self.beginInsertRows(parentModelIndex, self.entries.len, self.entries.len + newEntries.len - 1)
|
||||
self.entries.add(newEntries)
|
||||
self.endInsertRows()
|
||||
self.countChanged()
|
||||
self.setHasMore(hasMore)
|
||||
|
||||
# TODO: fetch more
|
||||
|
||||
proc getHasMore*(self: Model): bool {.slot.} =
|
||||
return self.hasMore
|
||||
|
||||
|
@ -157,7 +157,8 @@ type
|
||||
|
||||
FilterResponse* = object
|
||||
activities*: seq[ActivityEntry]
|
||||
thereMightBeMore*: bool
|
||||
offset*: int
|
||||
hasMore*: bool
|
||||
errorCode*: ErrorCode
|
||||
|
||||
# Define toJson proc for PayloadType
|
||||
@ -198,7 +199,8 @@ proc fromJson*(e: JsonNode, T: typedesc[FilterResponse]): FilterResponse {.inlin
|
||||
|
||||
result = T(
|
||||
activities: backendEntities,
|
||||
thereMightBeMore: if e.hasKey("thereMightBeMore"): e["thereMightBeMore"].getBool()
|
||||
offset: e["offset"].getInt(),
|
||||
hasMore: if e.hasKey("hasMore"): e["hasMore"].getBool()
|
||||
else: false,
|
||||
errorCode: ErrorCode(e["errorCode"].getInt())
|
||||
)
|
||||
|
@ -321,24 +321,19 @@ Control {
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: loadingText
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
text: qsTr("Loading...")
|
||||
visible: controller.loadingData
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
ListView {
|
||||
id: listView
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
Component.onCompleted: {
|
||||
if(controller.model.hasMore) {
|
||||
controller.loadMoreItems();
|
||||
}
|
||||
}
|
||||
|
||||
model: controller.model
|
||||
visible: !controller.loadingData
|
||||
|
||||
delegate: Item {
|
||||
width: parent ? parent.width : 0
|
||||
@ -346,55 +341,92 @@ Control {
|
||||
|
||||
readonly property var entry: model.activityEntry
|
||||
|
||||
RowLayout {
|
||||
ColumnLayout {
|
||||
id: itemLayout
|
||||
anchors.fill: parent
|
||||
spacing: 5
|
||||
|
||||
Label { text: entry.isMultiTransaction ? qsTr("MT") : entry.isPendingTransaction ? qsTr("PT") : qsTr(" T") }
|
||||
Label { text: `[${root.epochToDateStr(entry.timestamp)}] ` }
|
||||
Label { text: entry.isMultiTransaction ? entry.fromAmount : entry.amount }
|
||||
Label { text: qsTr("from"); Layout.leftMargin: 5; Layout.rightMargin: 5 }
|
||||
Label { text: entry.sender; Layout.maximumWidth: 200; elide: Text.ElideMiddle }
|
||||
Label { text: qsTr("to"); Layout.leftMargin: 5; Layout.rightMargin: 5 }
|
||||
Label { text: entry.recipient; Layout.maximumWidth: 200; elide: Text.ElideMiddle }
|
||||
Label { text: qsTr("got"); Layout.leftMargin: 5; Layout.rightMargin: 5; visible: entry.isMultiTransaction }
|
||||
Label { text: entry.toAmount; Layout.leftMargin: 5; Layout.rightMargin: 5; visible: entry.isMultiTransaction }
|
||||
Label {
|
||||
text: `{${
|
||||
function() {
|
||||
switch (entry.status) {
|
||||
case Constants.TransactionStatus.Failed: return qsTr("F");
|
||||
case Constants.TransactionStatus.Pending: return qsTr("P");
|
||||
case Constants.TransactionStatus.Complete: return qsTr("C");
|
||||
case Constants.TransactionStatus.Finalized: return qsTr("FZ");
|
||||
}
|
||||
return qsTr("-")
|
||||
}()}}`
|
||||
Layout.leftMargin: 5;
|
||||
RowLayout {
|
||||
Label { text: entry.isMultiTransaction ? entry.fromAmount : entry.amount }
|
||||
Label { text: qsTr("from"); Layout.leftMargin: 5; Layout.rightMargin: 5 }
|
||||
Label { text: entry.sender; Layout.maximumWidth: 200; elide: Text.ElideMiddle }
|
||||
Label { text: qsTr("to"); Layout.leftMargin: 5; Layout.rightMargin: 5 }
|
||||
Label { text: entry.recipient; Layout.maximumWidth: 200; elide: Text.ElideMiddle }
|
||||
Label { text: qsTr("got"); Layout.leftMargin: 5; Layout.rightMargin: 5; visible: entry.isMultiTransaction }
|
||||
Label { text: entry.toAmount; Layout.leftMargin: 5; Layout.rightMargin: 5; visible: entry.isMultiTransaction }
|
||||
RowLayout {} // Spacer
|
||||
}
|
||||
RowLayout {
|
||||
Label { text: entry.isMultiTransaction ? qsTr("MT") : entry.isPendingTransaction ? qsTr("PT") : qsTr(" T") }
|
||||
Label { text: `[${root.epochToDateStr(entry.timestamp)}] ` }
|
||||
Label {
|
||||
text: `{${
|
||||
function() {
|
||||
switch (entry.status) {
|
||||
case Constants.TransactionStatus.Failed: return qsTr("Failed");
|
||||
case Constants.TransactionStatus.Pending: return qsTr("Pending");
|
||||
case Constants.TransactionStatus.Complete: return qsTr("Complete");
|
||||
case Constants.TransactionStatus.Finalized: return qsTr("Finalized");
|
||||
}
|
||||
return qsTr("-")
|
||||
}()}}`
|
||||
Layout.leftMargin: 5;
|
||||
}
|
||||
RowLayout {} // Spacer
|
||||
}
|
||||
Label { text: entry.toAmount; Layout.leftMargin: 5; Layout.rightMargin: 5; visible: entry.isMultiTransaction }
|
||||
RowLayout {} // Spacer
|
||||
}
|
||||
}
|
||||
|
||||
footer: Component {
|
||||
Item {
|
||||
width: listView.width
|
||||
height: footerText.implicitHeight
|
||||
onContentYChanged: checkIfFooterVisible()
|
||||
onHeightChanged: checkIfFooterVisible()
|
||||
onContentHeightChanged: checkIfFooterVisible()
|
||||
Connections {
|
||||
target: listView.footerItem
|
||||
function onHeightChanged() {
|
||||
listView.checkIfFooterVisible()
|
||||
}
|
||||
}
|
||||
|
||||
function checkIfFooterVisible() {
|
||||
if((contentY + height) > (contentHeight - footerItem.height) && controller.model.hasMore && !controller.loadingData) {
|
||||
controller.loadMoreItems();
|
||||
}
|
||||
}
|
||||
|
||||
footer: Column {
|
||||
id: loadingItems
|
||||
|
||||
width: listView.width
|
||||
visible: controller.model.hasMore
|
||||
|
||||
Repeater {
|
||||
model: controller.model.hasMore ? 10 : 0
|
||||
|
||||
Text {
|
||||
id: footerText
|
||||
text: qsTr("Loading more items...")
|
||||
anchors.centerIn: parent
|
||||
text: loadingItems.loadingPattern
|
||||
}
|
||||
}
|
||||
|
||||
visible: controller.model.hasMore
|
||||
property string loadingPattern: ""
|
||||
property int glanceOffset: 0
|
||||
Timer {
|
||||
interval: 25; repeat: true; running: true
|
||||
|
||||
// Load more items when this footer comes into view.
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
controller.loadMoreItems();
|
||||
onTriggered: {
|
||||
let offset = loadingItems.glanceOffset
|
||||
let length = 100
|
||||
let slashCount = 3;
|
||||
|
||||
let pattern = new Array(length).fill(' ');
|
||||
|
||||
for (let i = 0; i < slashCount; i++) {
|
||||
let position = (offset + i) % length;
|
||||
pattern[position] = '/';
|
||||
}
|
||||
pattern = '[' + pattern.join('') + ']';
|
||||
|
||||
loadingItems.loadingPattern = pattern;
|
||||
loadingItems.glanceOffset = (offset + 1) % length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user