fix(@desktop/wallet): Remove the 'Load More' button from the Activity view and replace with automatic loading when the user scrolls down using a skeleton loading state

fixes #8987
This commit is contained in:
Khushboo Mehta 2023-01-13 00:26:48 +01:00 committed by Khushboo-dev-cpp
parent e8ed91b3ba
commit 2dbf2d4635
13 changed files with 204 additions and 81 deletions

View File

@ -82,7 +82,7 @@ proc init*(self: Controller) =
self.events.on(SIGNAL_TRANSACTION_LOADING_COMPLETED_FOR_ALL_NETWORKS) do(e:Args): self.events.on(SIGNAL_TRANSACTION_LOADING_COMPLETED_FOR_ALL_NETWORKS) do(e:Args):
let args = TransactionsLoadedArgs(e) let args = TransactionsLoadedArgs(e)
self.delegate.setHistoryFetchState(args.address, isFetching = false) self.delegate.setHistoryFetchState(args.address, args.allTxLoaded, isFetching = false)
self.events.on(SIGNAL_CURRENCY_FORMATS_UPDATED) do(e:Args): self.events.on(SIGNAL_CURRENCY_FORMATS_UPDATED) do(e:Args):
# TODO: Rebuild Transaction items # TODO: Rebuild Transaction items

View File

@ -34,7 +34,7 @@ method setTrxHistoryResult*(self: AccessInterface, transactions: seq[Transaction
method setHistoryFetchState*(self: AccessInterface, addresses: seq[string], isFetching: bool) {.base.} = method setHistoryFetchState*(self: AccessInterface, addresses: seq[string], isFetching: bool) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method setHistoryFetchState*(self: AccessInterface, address: string, isFetching: bool) {.base.} = method setHistoryFetchState*(self: AccessInterface, address: string, allTxLoaded: bool, isFetching: bool) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method setIsNonArchivalNode*(self: AccessInterface, isNonArchivalNode: bool) {.base.} = method setIsNonArchivalNode*(self: AccessInterface, isNonArchivalNode: bool) {.base.} =

View File

@ -29,6 +29,7 @@ type
totalFees: CurrencyAmount totalFees: CurrencyAmount
maxTotalFees: CurrencyAmount maxTotalFees: CurrencyAmount
symbol: string symbol: string
loadingTransaction: bool
proc initItem*( proc initItem*(
id: string, id: string,
@ -56,7 +57,8 @@ proc initItem*(
baseGasFees: CurrencyAmount, baseGasFees: CurrencyAmount,
totalFees: CurrencyAmount, totalFees: CurrencyAmount,
maxTotalFees: CurrencyAmount, maxTotalFees: CurrencyAmount,
symbol: string symbol: string,
loadingTransaction: bool = false
): Item = ): Item =
result.id = id result.id = id
result.typ = typ result.typ = typ
@ -84,6 +86,7 @@ proc initItem*(
result.totalFees = totalFees result.totalFees = totalFees
result.maxTotalFees = maxTotalFees result.maxTotalFees = maxTotalFees
result.symbol = symbol result.symbol = symbol
result.loadingTransaction = loadingTransaction
proc initTimestampItem*(timestamp: int): Item = proc initTimestampItem*(timestamp: int): Item =
result.timestamp = timestamp result.timestamp = timestamp
@ -98,6 +101,20 @@ proc initTimestampItem*(timestamp: int): Item =
result.totalFees = newCurrencyAmount() result.totalFees = newCurrencyAmount()
result.maxTotalFees = newCurrencyAmount() result.maxTotalFees = newCurrencyAmount()
proc initLoadingItem*(): Item =
result.timestamp = 0
result.gasPrice = newCurrencyAmount()
result.value = newCurrencyAmount()
result.chainId = 0
result.maxFeePerGas = newCurrencyAmount()
result.maxPriorityFeePerGas = newCurrencyAmount()
result.multiTransactionID = 0
result.isTimeStamp = false
result.baseGasFees = newCurrencyAmount()
result.totalFees = newCurrencyAmount()
result.maxTotalFees = newCurrencyAmount()
result.loadingTransaction = true
proc `$`*(self: Item): string = proc `$`*(self: Item): string =
result = fmt"""AllTokensItem( result = fmt"""AllTokensItem(
id: {self.id}, id: {self.id},
@ -126,6 +143,7 @@ proc `$`*(self: Item): string =
totalFees: {self.totalFees}, totalFees: {self.totalFees},
maxTotalFees: {self.maxTotalFees}, maxTotalFees: {self.maxTotalFees},
symbol: {self.symbol}, symbol: {self.symbol},
loadingTransaction: {self.loadingTransaction},
]""" ]"""
proc getId*(self: Item): string = proc getId*(self: Item): string =
@ -205,3 +223,6 @@ proc getMaxTotalFees*(self: Item): CurrencyAmount =
proc getSymbol*(self: Item): string = proc getSymbol*(self: Item): string =
return self.symbol return self.symbol
proc getLoadingTransaction*(self: Item): bool =
return self.loadingTransaction

View File

@ -31,6 +31,7 @@ type
TotalFees TotalFees
MaxTotalFees MaxTotalFees
Symbol Symbol
LoadingTransaction
QtObject: QtObject:
type type
@ -95,7 +96,8 @@ QtObject:
ModelRole.BaseGasFees.int: "baseGasFees", ModelRole.BaseGasFees.int: "baseGasFees",
ModelRole.TotalFees.int: "totalFees", ModelRole.TotalFees.int: "totalFees",
ModelRole.MaxTotalFees.int: "maxTotalFees", ModelRole.MaxTotalFees.int: "maxTotalFees",
ModelRole.Symbol.int: "symbol" ModelRole.Symbol.int: "symbol",
ModelRole.LoadingTransaction.int: "loadingTransaction"
}.toTable }.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant = method data(self: Model, index: QModelIndex, role: int): QVariant =
@ -161,6 +163,8 @@ QtObject:
result = newQVariant(item.getMaxTotalFees()) result = newQVariant(item.getMaxTotalFees())
of ModelRole.Symbol: of ModelRole.Symbol:
result = newQVariant(item.getSymbol()) result = newQVariant(item.getSymbol())
of ModelRole.LoadingTransaction:
result = newQVariant(item.getLoadingTransaction())
proc setItems*(self: Model, items: seq[Item]) = proc setItems*(self: Model, items: seq[Item]) =
self.beginResetModel() self.beginResetModel()
@ -185,7 +189,7 @@ QtObject:
QtProperty[bool] hasMore: QtProperty[bool] hasMore:
read = getHasMore read = getHasMore
write = setHasMore write = setHasMore
notify = currentTransactionsChanged notify = hasMoreChanged
proc cmpTransactions*(x, y: Item): int = proc cmpTransactions*(x, y: Item): int =
# Sort proc to compare transactions from a single account. # Sort proc to compare transactions from a single account.
@ -218,3 +222,22 @@ QtObject:
self.items = allTxs self.items = allTxs
self.setItems(itemsWithDateHeaders) self.setItems(itemsWithDateHeaders)
self.setHasMore(true) self.setHasMore(true)
proc addPageSizeBuffer*(self: Model, pageSize: int) =
if pageSize > 0:
var itemsWithDateHeaders: seq[Item] = @[]
itemsWithDateHeaders.add(initTimestampItem(0))
for i in 0 ..< pageSize:
self.beginInsertRows(newQModelIndex(), self.itemsWithDateHeaders.len, self.itemsWithDateHeaders.len)
self.itemsWithDateHeaders.add(initLoadingItem())
self.endInsertRows()
self.countChanged()
proc removePageSizeBuffer*(self: Model) =
for i in 0 ..< self.itemsWithDateHeaders.len:
if self.itemsWithDateHeaders[i].getLoadingTransaction():
self.beginRemoveRows(newQModelIndex(), i, self.itemsWithDateHeaders.len-1)
self.itemsWithDateHeaders.delete(i, self.itemsWithDateHeaders.len-1)
self.endRemoveRows()
self.countChanged()
return

View File

@ -115,8 +115,8 @@ method setTrxHistoryResult*(self: Module, transactions: seq[TransactionDto], add
method setHistoryFetchState*(self: Module, addresses: seq[string], isFetching: bool) = method setHistoryFetchState*(self: Module, addresses: seq[string], isFetching: bool) =
self.view.setHistoryFetchStateForAccounts(addresses, isFetching) self.view.setHistoryFetchStateForAccounts(addresses, isFetching)
method setHistoryFetchState*(self: Module, address: string, isFetching: bool) = method setHistoryFetchState*(self: Module, address: string, allTxLoaded: bool, isFetching: bool) =
self.view.setHistoryFetchState(address, isFetching) self.view.setHistoryFetchState(address, allTxLoaded, isFetching)
method setIsNonArchivalNode*(self: Module, isNonArchivalNode: bool) = method setIsNonArchivalNode*(self: Module, isNonArchivalNode: bool) =
self.view.setIsNonArchivalNode(isNonArchivalNode) self.view.setIsNonArchivalNode(isNonArchivalNode)

View File

@ -48,15 +48,16 @@ QtObject:
proc loadingTrxHistoryChanged*(self: View, isLoading: bool, address: string) {.signal.} proc loadingTrxHistoryChanged*(self: View, isLoading: bool, address: string) {.signal.}
proc setHistoryFetchState*(self: View, address: string, isFetching: bool) = proc setHistoryFetchState*(self: View, address: string, allTxLoaded: bool, isFetching: bool) =
if self.models.hasKey(address):
if not isFetching:
self.models[address].removePageSizeBuffer()
elif isFetching and self.models[address].getCount() == 0:
self.models[address].addPageSizeBuffer(20)
self.models[address].setHasMore(not allTxLoaded)
self.fetchingHistoryState[address] = isFetching self.fetchingHistoryState[address] = isFetching
self.loadingTrxHistoryChanged(isFetching, address) self.loadingTrxHistoryChanged(isFetching, address)
proc setHistoryFetchState*(self: View, accounts: seq[string], isFetching: bool) =
for acc in accounts:
self.fetchingHistoryState[acc] = isFetching
self.loadingTrxHistoryChanged(isFetching, acc)
proc isFetchingHistory*(self: View, address: string): bool {.slot.} = proc isFetchingHistory*(self: View, address: string): bool {.slot.} =
if self.fetchingHistoryState.hasKey(address): if self.fetchingHistoryState.hasKey(address):
return self.fetchingHistoryState[address] return self.fetchingHistoryState[address]
@ -66,7 +67,9 @@ QtObject:
return self.model.getCount() > 0 return self.model.getCount() > 0
proc loadTransactionsForAccount*(self: View, address: string, toBlock: string = "0x0", limit: int = 20, loadMore: bool = false) {.slot.} = proc loadTransactionsForAccount*(self: View, address: string, toBlock: string = "0x0", limit: int = 20, loadMore: bool = false) {.slot.} =
self.setHistoryFetchState(address, true) if self.models.hasKey(address):
self.setHistoryFetchState(address, allTxLoaded=not self.models[address].getHasMore(), isFetching=true)
self.models[address].addPageSizeBuffer(limit)
self.delegate.loadTransactions(address, toBlock, limit, loadMore) self.delegate.loadTransactions(address, toBlock, limit, loadMore)
proc setTrxHistoryResult*(self: View, transactions: seq[Item], address: string, wasFetchMore: bool) = proc setTrxHistoryResult*(self: View, transactions: seq[Item], address: string, wasFetchMore: bool) =
@ -74,10 +77,15 @@ QtObject:
self.models[address] = newModel() self.models[address] = newModel()
self.models[address].addNewTransactions(transactions, wasFetchMore) self.models[address].addNewTransactions(transactions, wasFetchMore)
if self.fetchingHistoryState.hasKey(address) and self.fetchingHistoryState[address] and wasFetchMore:
self.models[address].addPageSizeBuffer(transactions.len)
proc setHistoryFetchStateForAccounts*(self: View, addresses: seq[string], isFetching: bool) = proc setHistoryFetchStateForAccounts*(self: View, addresses: seq[string], isFetching: bool) =
for address in addresses: for address in addresses:
self.setHistoryFetchState(address, isFetching) if self.models.hasKey(address):
self.setHistoryFetchState(address, allTxLoaded = not self.models[address].getHasMore(), isFetching)
else:
self.setHistoryFetchState(address, allTxLoaded = false, isFetching)
proc setModel*(self: View, address: string) {.slot.} = proc setModel*(self: View, address: string) {.slot.} =
if not self.models.hasKey(address): if not self.models.hasKey(address):
@ -165,4 +173,4 @@ QtObject:
let fromAddress = tx.getfrom() let fromAddress = tx.getfrom()
if not self.models.hasKey(fromAddress): if not self.models.hasKey(fromAddress):
self.models[fromAddress] = newModel() self.models[fromAddress] = newModel()
self.models[fromAddress].addNewTransactions(@[tx], false) self.models[fromAddress].addNewTransactions(@[tx], wasFetchMore=false)

View File

@ -17,16 +17,19 @@ type
toBlock: Uint256 toBlock: Uint256
limit: int limit: int
loadMore: bool loadMore: bool
allTxLoaded: bool
const loadTransactionsTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = const loadTransactionsTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let let
arg = decode[LoadTransactionsTaskArg](argEncoded) arg = decode[LoadTransactionsTaskArg](argEncoded)
limitAsHex = "0x" & eth_utils.stripLeadingZeros(arg.limit.toHex) limitAsHex = "0x" & eth_utils.stripLeadingZeros(arg.limit.toHex)
response = transactions.getTransfersByAddress(arg.chainId, arg.address, arg.toBlock, limitAsHex, arg.loadMore).result
output = %*{ output = %*{
"address": arg.address, "address": arg.address,
"chainId": arg.chainId, "chainId": arg.chainId,
"history": transactions.getTransfersByAddress(arg.chainId, arg.address, arg.toBlock, limitAsHex, arg.loadMore), "history": response,
"loadMore": arg.loadMore, "loadMore": arg.loadMore,
"allTxLoaded": response.getElems().len < arg.limit
} }
arg.finish(output) arg.finish(output)

View File

@ -70,6 +70,8 @@ type
transactions*: seq[TransactionDto] transactions*: seq[TransactionDto]
address*: string address*: string
wasFetchMore*: bool wasFetchMore*: bool
allTxLoaded*: bool
tempLoadingTx*: int
type type
TransactionSentArgs* = ref object of Args TransactionSentArgs* = ref object of Args
@ -96,6 +98,7 @@ QtObject:
settingsService: settings_service.Service settingsService: settings_service.Service
tokenService: token_service.Service tokenService: token_service.Service
txCounter: Table[string, seq[int]] txCounter: Table[string, seq[int]]
allTxLoaded: Table[string, bool]
# Forward declaration # Forward declaration
proc loadTransactions*(self: Service, address: string, toBlock: Uint256, limit: int = 20, loadMore: bool = false) proc loadTransactions*(self: Service, address: string, toBlock: Uint256, limit: int = 20, loadMore: bool = false)
@ -118,6 +121,7 @@ QtObject:
result.settingsService = settingsService result.settingsService = settingsService
result.tokenService = tokenService result.tokenService = tokenService
result.txCounter = initTable[string, seq[int]]() result.txCounter = initTable[string, seq[int]]()
result.allTxLoaded = initTable[string, bool]()
proc init*(self: Service) = proc init*(self: Service) =
signalConnect(singletonInstance.localAccountSensitiveSettings, "isWalletEnabledChanged()", self, "onIsWalletEnabledChanged()", 2) signalConnect(singletonInstance.localAccountSensitiveSettings, "isWalletEnabledChanged()", self, "onIsWalletEnabledChanged()", 2)
@ -202,10 +206,16 @@ QtObject:
let address = historyData["address"].getStr let address = historyData["address"].getStr
let chainID = historyData["chainId"].getInt let chainID = historyData["chainId"].getInt
let wasFetchMore = historyData["loadMore"].getBool let wasFetchMore = historyData["loadMore"].getBool
let allTxLoaded = historyData["allTxLoaded"].getBool
var transactions: seq[TransactionDto] = @[] var transactions: seq[TransactionDto] = @[]
for tx in historyData["history"]["result"].getElems(): for tx in historyData["history"].getElems():
transactions.add(tx.toTransactionDto()) transactions.add(tx.toTransactionDto())
if self.allTxLoaded.hasKey(address):
self.allTxLoaded[address] = self.allTxLoaded[address] and allTxLoaded
else:
self.allTxLoaded[address] = allTxLoaded
# emit event # emit event
self.events.emit(SIGNAL_TRANSACTIONS_LOADED, TransactionsLoadedArgs( self.events.emit(SIGNAL_TRANSACTIONS_LOADED, TransactionsLoadedArgs(
transactions: transactions, transactions: transactions,
@ -214,16 +224,18 @@ QtObject:
)) ))
# when requests for all networks are completed then set loading state as completed # when requests for all networks are completed then set loading state as completed
if self.txCounter.hasKey(address): if self.txCounter.hasKey(address) and self.allTxLoaded.hasKey(address) :
var chainIDs = self.txCounter[address] var chainIDs = self.txCounter[address]
chainIDs.del(chainIDs.find(chainID)) chainIDs.del(chainIDs.find(chainID))
self.txCounter[address] = chainIDs self.txCounter[address] = chainIDs
if self.txCounter[address].len == 0: if self.txCounter[address].len == 0:
self.txCounter.del(address) self.txCounter.del(address)
self.events.emit(SIGNAL_TRANSACTION_LOADING_COMPLETED_FOR_ALL_NETWORKS, TransactionsLoadedArgs(address: address)) self.events.emit(SIGNAL_TRANSACTION_LOADING_COMPLETED_FOR_ALL_NETWORKS, TransactionsLoadedArgs(address: address, allTxLoaded: self.allTxLoaded[address]))
proc loadTransactions*(self: Service, address: string, toBlock: Uint256, limit: int = 20, loadMore: bool = false) = proc loadTransactions*(self: Service, address: string, toBlock: Uint256, limit: int = 20, loadMore: bool = false) =
let networks = self.networkService.getNetworks() let networks = self.networkService.getNetworks()
self.allTxLoaded.del(address)
if not self.txCounter.hasKey(address): if not self.txCounter.hasKey(address):
var networkChains: seq[int] = @[] var networkChains: seq[int] = @[]
self.txCounter[address] = networkChains self.txCounter[address] = networkChains

View File

@ -281,7 +281,6 @@ Rectangle {
lineHeight: 24 lineHeight: 24
visible: inlineTagModelRepeater.count > 0 visible: inlineTagModelRepeater.count > 0
loading: statusListItem.loading
} }
StatusScrollView { StatusScrollView {

View File

@ -369,9 +369,15 @@ Item {
Layout.alignment: detailsFlow.isOverflowing ? Qt.AlignLeft : Qt.AlignRight Layout.alignment: detailsFlow.isOverflowing ? Qt.AlignLeft : Qt.AlignRight
iconAsset.icon: "browser" iconAsset.icon: "browser"
tagPrimaryLabel.text: qsTr("Website") tagPrimaryLabel.text: qsTr("Website")
controlBackground.color: Theme.palette.baseColor2
controlBackground.border.color: "transparent"
visible: typeof token != "undefined" && token && token.assetWebsiteUrl !== "" visible: typeof token != "undefined" && token && token.assetWebsiteUrl !== ""
customBackground: Component {
Rectangle {
color: Theme.palette.baseColor2
border.width: 1
border.color: "transparent"
radius: 36
}
}
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
@ -385,9 +391,15 @@ Item {
image.source: token && token.builtOn !== "" ? Style.svg("tiny/" + RootStore.getNetworkIconUrl(token.builtOn)) : "" image.source: token && token.builtOn !== "" ? Style.svg("tiny/" + RootStore.getNetworkIconUrl(token.builtOn)) : ""
tagPrimaryLabel.text: token && token.builtOn !== "" ? RootStore.getNetworkName(token.builtOn) : "---" tagPrimaryLabel.text: token && token.builtOn !== "" ? RootStore.getNetworkName(token.builtOn) : "---"
tagSecondaryLabel.text: token && token.address !== "" ? token.address : "---" tagSecondaryLabel.text: token && token.address !== "" ? token.address : "---"
controlBackground.color: Theme.palette.baseColor2
controlBackground.border.color: "transparent"
visible: typeof token != "undefined" && token && token.builtOn !== "" && token.address !== "" visible: typeof token != "undefined" && token && token.builtOn !== "" && token.address !== ""
customBackground: Component {
Rectangle {
color: Theme.palette.baseColor2
border.width: 1
border.color: "transparent"
radius: 36
}
}
} }
} }
} }

View File

@ -4,30 +4,44 @@ import QtQuick.Controls 2.14
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import utils 1.0 import utils 1.0
Control { Control {
id: root
property alias image : image property alias image : image
property alias iconAsset : iconAsset property alias iconAsset : iconAsset
property alias tagPrimaryLabel: tagPrimaryLabel property alias tagPrimaryLabel: tagPrimaryLabel
property alias tagSecondaryLabel: tagSecondaryLabel property alias tagSecondaryLabel: tagSecondaryLabel
property alias controlBackground: controlBackground
property alias rightComponent: rightComponent.sourceComponent property alias rightComponent: rightComponent.sourceComponent
property bool loading: false
horizontalPadding: Style.current.halfPadding property Component customBackground: Component {
verticalPadding: 5 Rectangle {
background: Rectangle {
id: controlBackground
color: "transparent" color: "transparent"
border.width: 1 border.width: 1
border.color: Theme.palette.baseColor2 border.color: Theme.palette.baseColor2
radius: 36 radius: 36
} }
}
QtObject {
id: d
property var loadingComponent: Component { LoadingComponent {}}
}
horizontalPadding: Style.current.halfPadding
verticalPadding: 5
background: Loader {
sourceComponent: root.loading ? d.loadingComponent : root.customBackground
}
contentItem: RowLayout { contentItem: RowLayout {
spacing: 4 spacing: 4
visible: !root.loading
// FIXME this could be StatusIcon but it can't load images from an arbitrary URL // FIXME this could be StatusIcon but it can't load images from an arbitrary URL
Image { Image {
id: image id: image

View File

@ -4,6 +4,7 @@ import QtQuick.Layouts 1.3
import StatusQ.Components 0.1 import StatusQ.Components 0.1
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import utils 1.0 import utils 1.0
import shared 1.0 import shared 1.0
@ -11,6 +12,9 @@ import shared 1.0
StatusListItem { StatusListItem {
id: root id: root
property alias cryptoValueText: cryptoValueText
property alias fiatValueText: fiatValueText
property var modelData property var modelData
property string symbol property string symbol
property bool isIncoming property bool isIncoming
@ -24,8 +28,9 @@ StatusListItem {
property string savedAddressName property string savedAddressName
state: "normal" state: "normal"
asset.isImage: true asset.isImage: !loading
asset.name: root.symbol ? Style.png("tokens/%1".arg(root.symbol)) : "" asset.name: root.symbol ? Style.png("tokens/%1".arg(root.symbol)) : ""
asset.isLetterIdenticon: loading
title: modelData !== undefined && !!modelData ? title: modelData !== undefined && !!modelData ?
isIncoming ? qsTr("Receive %1").arg(root.symbol) : !!savedAddressName ? isIncoming ? qsTr("Receive %1").arg(root.symbol) : !!savedAddressName ?
qsTr("Send %1 to %2").arg(root.symbol).arg(savedAddressName) : qsTr("Send %1 to %2").arg(root.symbol).arg(savedAddressName) :
@ -36,16 +41,23 @@ StatusListItem {
tagPrimaryLabel.text: networkName tagPrimaryLabel.text: networkName
tagPrimaryLabel.color: networkColor tagPrimaryLabel.color: networkColor
image.source: !!networkIcon ? Style.svg("tiny/%1".arg(networkIcon)) : "" image.source: !!networkIcon ? Style.svg("tiny/%1".arg(networkIcon)) : ""
background: Rectangle { customBackground: Component {
id: controlBackground Rectangle {
implicitWidth: 51
implicitHeight: 24
color: "transparent" color: "transparent"
border.width: 1 border.width: 1
border.color: Theme.palette.baseColor2 border.color: Theme.palette.baseColor2
radius: 36 radius: 36
} }
}
width: 51
height: root.loading ? textMetrics.tightBoundingRect.height : 24
rightComponent: transferStatus === Constants.TransactionStatus.Success ? completedIcon : loadingIndicator rightComponent: transferStatus === Constants.TransactionStatus.Success ? completedIcon : loadingIndicator
loading: root.loading
}
TextMetrics {
id: textMetrics
font: statusListItemSubTitle.font
text: statusListItemSubTitle.text
} }
components: [ components: [
ColumnLayout { ColumnLayout {
@ -57,18 +69,23 @@ StatusListItem {
icon: "arrow-up" icon: "arrow-up"
rotation: isIncoming ? 135 : 45 rotation: isIncoming ? 135 : 45
height: 18 height: 18
visible: !root.loading
} }
StatusBaseText { StatusTextWithLoadingState {
id: cryptoValueText id: cryptoValueText
text: LocaleUtils.currencyAmountToLocaleString(cryptoValue) text: LocaleUtils.currencyAmountToLocaleString(cryptoValue)
color: Theme.palette.directColor1 customColor: Theme.palette.directColor1
loading: root.loading
} }
} }
StatusBaseText { StatusTextWithLoadingState {
id: fiatValueText
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
text: LocaleUtils.currencyAmountToLocaleString(fiatValue) text: LocaleUtils.currencyAmountToLocaleString(fiatValue)
font.pixelSize: 15 font.pixelSize: 15
color: Theme.palette.baseColor1 customColor: Theme.palette.baseColor1
loading: root.loading
} }
} }
] ]

View File

@ -15,39 +15,34 @@ import "../stores"
import "../controls" import "../controls"
ColumnLayout { ColumnLayout {
id: historyView id: root
property var account property var account
property int pageSize: 20 // number of transactions per page property int pageSize: 20 // number of transactions per page
property bool isLoading: false
signal launchTransactionDetail(var transaction) signal launchTransactionDetail(var transaction)
function fetchHistory() { function fetchHistory() {
if (RootStore.isFetchingHistory(historyView.account.address)) { if (!RootStore.isFetchingHistory(root.account.address)) {
isLoading = true d.isLoading = true
} else { RootStore.loadTransactionsForAccount(root.account.address, pageSize)
RootStore.loadTransactionsForAccount(historyView.account.address, pageSize)
} }
} }
QtObject {
id: d
property bool isLoading: false
}
Connections { Connections {
target: RootStore.history target: RootStore.history
function onLoadingTrxHistoryChanged(isLoading: bool, address: string) { function onLoadingTrxHistoryChanged(isLoading: bool, address: string) {
if (historyView.account.address.toLowerCase() === address.toLowerCase()) { if (root.account.address.toLowerCase() === address.toLowerCase()) {
historyView.isLoading = isLoading d.isLoading = isLoading
} }
} }
} }
Loader {
id: loadingImg
active: isLoading
sourceComponent: loadingImageComponent
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
Layout.rightMargin: Style.current.padding
}
StyledText { StyledText {
id: nonArchivalNodeError id: nonArchivalNodeError
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
@ -60,7 +55,7 @@ ColumnLayout {
StyledText { StyledText {
id: noTxs id: noTxs
visible: !isLoading && transactionListRoot.count === 0 visible: !d.isLoading && transactionListRoot.count === 0
text: qsTr("No transactions found") text: qsTr("No transactions found")
font.pixelSize: Style.current.primaryTextFontSize font.pixelSize: Style.current.primaryTextFontSize
} }
@ -76,26 +71,17 @@ ColumnLayout {
model: RootStore.historyTransactions model: RootStore.historyTransactions
delegate: Loader { delegate: Loader {
width: parent.width width: ListView.view.width
sourceComponent: isTimeStamp ? dateHeader : transactionDelegate sourceComponent: isTimeStamp ? dateHeader : transactionDelegate
onLoaded: { onLoaded: {
item.modelData = model item.modelData = model
} }
} }
footer: footerComp
ScrollBar.vertical: StatusScrollBar {} ScrollBar.vertical: StatusScrollBar {}
footer: StatusButton { onAtYEndChanged: if(atYEnd && RootStore.historyTransactions.hasMore) fetchHistory()
id: loadMoreButton
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Load More")
// TODO: handle case when requested limit === transaction count -- there
// is currently no way to know that there are no more results
enabled: !isLoading && RootStore.historyTransactions.hasMore
onClicked: fetchHistory()
loading: isLoading
}
} }
Component { Component {
@ -113,23 +99,51 @@ ColumnLayout {
Component { Component {
id: transactionDelegate id: transactionDelegate
TransactionDelegate { TransactionDelegate {
property bool modelDataValid: modelData !== undefined && !!modelData property bool modelDataValid: !!modelData
isIncoming: modelDataValid ? modelData.to === account.address: false isIncoming: modelDataValid ? modelData.to === account.address: false
cryptoValue: modelDataValid ? modelData.value : undefined cryptoValue: modelDataValid ? modelData.value : undefined
fiatValue: modelDataValid ? RootStore.getFiatValue(cryptoValue.amount, symbol, RootStore.currentCurrency) : undefined fiatValue: modelDataValid && !!cryptoValue ? RootStore.getFiatValue(cryptoValue.amount, symbol, RootStore.currentCurrency) : undefined
networkIcon: modelDataValid ? RootStore.getNetworkIcon(modelData.chainId) : "" networkIcon: modelDataValid ? RootStore.getNetworkIcon(modelData.chainId) : ""
networkColor: modelDataValid ? RootStore.getNetworkColor(modelData.chainId) : "" networkColor: modelDataValid ? RootStore.getNetworkColor(modelData.chainId) : ""
networkName: modelDataValid ? RootStore.getNetworkShortName(modelData.chainId) : "" networkName: modelDataValid ? RootStore.getNetworkShortName(modelData.chainId) : ""
symbol: modelDataValid ? modelData.symbol : "" symbol: modelDataValid && !!modelData.symbol ? modelData.symbol : ""
transferStatus: modelDataValid ? RootStore.hex2Dec(modelData.txStatus) : "" transferStatus: modelDataValid ? RootStore.hex2Dec(modelData.txStatus) : ""
shortTimeStamp: modelDataValid ? LocaleUtils.formatTime(modelData.timestamp * 1000, Locale.ShortFormat) : "" shortTimeStamp: modelDataValid ? LocaleUtils.formatTime(modelData.timestamp * 1000, Locale.ShortFormat) : ""
savedAddressName: modelDataValid ? RootStore.getNameForSavedWalletAddress(modelData.to) : "" savedAddressName: modelDataValid ? RootStore.getNameForSavedWalletAddress(modelData.to) : ""
onClicked: launchTransactionDetail(modelData) onClicked: launchTransactionDetail(modelData)
loading: modelDataValid ? modelData.loadingTransaction : false
} }
} }
Component { Component {
id: loadingImageComponent id: footerComp
StatusLoadingIndicator {} ColumnLayout {
width: root.width
visible: !RootStore.historyTransactions.hasMore && transactionListRoot.count !== 0
spacing: 12
Rectangle {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Style.curent.padding
Layout.preferredWidth: parent.width - 100
Layout.preferredHeight: 1
color: Theme.palette.directColor8
visible: !RootStore.historyTransactions.hasMore
}
StatusBaseText {
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
text: qsTr("You have reached the beginning of the activity for this account")
font.pixelSize: 13
color: Theme.palette.baseColor1
visible: !RootStore.historyTransactions.hasMore
horizontalAlignment: Text.AlignHCenter
}
StatusButton {
Layout.alignment: Qt.AlignHCenter
text: qsTr("Back to most recent transaction")
visible: !RootStore.historyTransactions.hasMore
onClicked: transactionListRoot.positionViewAtBeginning()
}
}
} }
} }