bash: q: command not found
feat(@desktop/wallet): New design for Transaction History fixes #7213
This commit is contained in:
parent
b2485603de
commit
e9d3d7a0b0
|
@ -127,6 +127,12 @@ QtObject:
|
|||
return item.getIconURL()
|
||||
return ""
|
||||
|
||||
proc getNetworkShortName*(self: Model, chainId: int): string {.slot.} =
|
||||
for item in self.items:
|
||||
if(item.getChainId() == chainId):
|
||||
return item.getShortName()
|
||||
return ""
|
||||
|
||||
proc getNetworkIconUrl*(self: Model, shortName: string): string {.slot.} =
|
||||
for item in self.items:
|
||||
if(item.getShortName() == toLowerAscii(shortName)):
|
||||
|
|
|
@ -62,3 +62,6 @@ proc removeCustomToken*(self: Controller, chainId: int, address: string) =
|
|||
|
||||
proc getTokenDetails*(self: Controller, address: string) =
|
||||
self.tokenService.getTokenDetails(address)
|
||||
|
||||
method findTokenSymbolByAddress*(self: Controller, address: string): string =
|
||||
return self.walletAccountService.findTokenSymbolByAddress(address)
|
||||
|
|
|
@ -29,6 +29,9 @@ method getTokenDetails*(self: AccessInterface, address: string) {.base.} =
|
|||
method tokenDetailsWereResolved*(self: AccessInterface, tokenDetails: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method findTokenSymbolByAddress*(self: AccessInterface, address: string): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
# View Delegate Interface
|
||||
# Delegate for the view must be declared here due to use of QtObject and multi
|
||||
# inheritance, which is not well supported in Nim.
|
||||
|
|
|
@ -88,3 +88,6 @@ method getTokenDetails*(self: Module, address: string) =
|
|||
|
||||
method tokenDetailsWereResolved*(self: Module, tokenDetails: string) =
|
||||
self.view.tokenDetailsWereResolved(tokenDetails)
|
||||
|
||||
method findTokenSymbolByAddress*(self: Module, address: string): string =
|
||||
return self.controller.findTokenSymbolByAddress(address)
|
||||
|
|
|
@ -74,3 +74,6 @@ QtObject:
|
|||
|
||||
proc getTokenDetails*(self: View, address: string) {.slot.} =
|
||||
self.delegate.getTokenDetails(address)
|
||||
|
||||
proc findTokenSymbolByAddress*(self: View, address: string): string {.slot.} =
|
||||
return self.delegate.findTokenSymbolByAddress(address)
|
||||
|
|
|
@ -74,3 +74,9 @@ QtObject:
|
|||
self.items = items
|
||||
self.endResetModel()
|
||||
self.countChanged()
|
||||
|
||||
proc getNameByAddress*(self: Model, address: string): string =
|
||||
for item in self.items:
|
||||
if(item.getAddress() == address):
|
||||
return item.getName()
|
||||
return ""
|
||||
|
|
|
@ -42,3 +42,6 @@ QtObject:
|
|||
|
||||
proc deleteSavedAddress*(self: View, address: string): string {.slot.} =
|
||||
return self.delegate.deleteSavedAddress(address)
|
||||
|
||||
proc getNameByAddress*(self: View, address: string): string {.slot.} =
|
||||
return self.model.getNameByAddress(address)
|
||||
|
|
|
@ -55,6 +55,10 @@ proc init*(self: Controller) =
|
|||
let addresses = accounts.map(account => account.address)
|
||||
self.delegate.setHistoryFetchState(addresses, false)
|
||||
self.delegate.setIsNonArchivalNode(true)
|
||||
of "fetching-history-error":
|
||||
let accounts = self.getWalletAccounts()
|
||||
let addresses = accounts.map(account => account.address)
|
||||
self.delegate.setHistoryFetchState(addresses, false)
|
||||
else:
|
||||
echo "Unhandled wallet signal: ", data.eventType
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import strformat, stint
|
||||
import strformat
|
||||
|
||||
type
|
||||
Item* = object
|
||||
|
@ -7,7 +7,7 @@ type
|
|||
address: string
|
||||
blockNumber: string
|
||||
blockHash: string
|
||||
timestamp: UInt256
|
||||
timestamp: int
|
||||
gasPrice: string
|
||||
gasLimit: string
|
||||
gasUsed: string
|
||||
|
@ -17,23 +17,37 @@ type
|
|||
fro: string
|
||||
to: string
|
||||
contract: string
|
||||
chainId: int
|
||||
maxFeePerGas: string
|
||||
maxPriorityFeePerGas: string
|
||||
input: string
|
||||
txHash: string
|
||||
multiTransactionID: int
|
||||
isTimeStamp: bool
|
||||
|
||||
proc initItem*(
|
||||
id,
|
||||
typ,
|
||||
address,
|
||||
blockNumber,
|
||||
id: string,
|
||||
typ: string,
|
||||
address: string,
|
||||
blockNumber: string,
|
||||
blockHash: string,
|
||||
timestamp: UInt256,
|
||||
gasPrice,
|
||||
gasLimit,
|
||||
gasUsed,
|
||||
nonce,
|
||||
txStatus,
|
||||
value,
|
||||
fro,
|
||||
to,
|
||||
contract: string
|
||||
timestamp: int,
|
||||
gasPrice: string,
|
||||
gasLimit: string,
|
||||
gasUsed: string,
|
||||
nonce: string,
|
||||
txStatus: string,
|
||||
value: string,
|
||||
fro: string,
|
||||
to: string,
|
||||
contract: string,
|
||||
chainId: int,
|
||||
maxFeePerGas: string,
|
||||
maxPriorityFeePerGas: string,
|
||||
input: string,
|
||||
txHash: string,
|
||||
multiTransactionID: int,
|
||||
isTimeStamp: bool
|
||||
): Item =
|
||||
result.id = id
|
||||
result.typ = typ
|
||||
|
@ -50,6 +64,13 @@ proc initItem*(
|
|||
result.fro = fro
|
||||
result.to = to
|
||||
result.contract = contract
|
||||
result.chainId = chainId
|
||||
result.maxFeePerGas = maxFeePerGas
|
||||
result.maxPriorityFeePerGas = maxPriorityFeePerGas
|
||||
result.input = input
|
||||
result.txHash = txHash
|
||||
result.multiTransactionID = multiTransactionID
|
||||
result.isTimeStamp = isTimeStamp
|
||||
|
||||
proc `$`*(self: Item): string =
|
||||
result = fmt"""AllTokensItem(
|
||||
|
@ -68,6 +89,13 @@ proc `$`*(self: Item): string =
|
|||
fro: {self.fro},
|
||||
to: {self.to},
|
||||
contract: {self.contract},
|
||||
chainId: {self.chainId},
|
||||
maxFeePerGas: {self.maxFeePerGas},
|
||||
maxPriorityFeePerGas: {self.maxPriorityFeePerGas},
|
||||
input: {self.input},
|
||||
txHash: {self.txHash},
|
||||
multiTransactionID: {self.multiTransactionID},
|
||||
isTimeStamp: {self.isTimeStamp},
|
||||
]"""
|
||||
|
||||
proc getId*(self: Item): string =
|
||||
|
@ -85,8 +113,8 @@ proc getBlockNumber*(self: Item): string =
|
|||
proc getBlockHash*(self: Item): string =
|
||||
return self.blockHash
|
||||
|
||||
proc getTimestamp*(self: Item): string =
|
||||
return $self.timestamp
|
||||
proc getTimestamp*(self: Item): int =
|
||||
return self.timestamp
|
||||
|
||||
proc getGasPrice*(self: Item): string =
|
||||
return self.gasPrice
|
||||
|
@ -114,3 +142,24 @@ proc getTo*(self: Item): string =
|
|||
|
||||
proc getContract*(self: Item): string =
|
||||
return self.contract
|
||||
|
||||
proc getChainId*(self: Item): int =
|
||||
return self.chainId
|
||||
|
||||
proc getMaxFeePerGas*(self: Item): string =
|
||||
return self.maxFeePerGas
|
||||
|
||||
proc getMaxPriorityFeePerGas*(self: Item): string =
|
||||
return self.maxPriorityFeePerGas
|
||||
|
||||
proc getInput*(self: Item): string =
|
||||
return self.input
|
||||
|
||||
proc getTxHash*(self: Item): string =
|
||||
return self.txHash
|
||||
|
||||
proc getMultiTransactionID*(self: Item): int =
|
||||
return self.multiTransactionID
|
||||
|
||||
proc getIsTimeStamp*(self: Item): bool =
|
||||
return self.isTimeStamp
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import NimQml, Tables, strutils, strformat, sequtils, tables, sugar, algorithm
|
||||
import NimQml, Tables, strutils, strformat, sequtils, tables, sugar, algorithm, std/[times, os], stint
|
||||
|
||||
import ./item
|
||||
import ../../../../../app_service/service/eth/utils as eth_service_utils
|
||||
|
@ -21,15 +21,24 @@ type
|
|||
From
|
||||
To
|
||||
Contract
|
||||
ChainID
|
||||
MaxFeePerGas
|
||||
MaxPriorityFeePerGas
|
||||
Input
|
||||
TxHash
|
||||
MultiTransactionID
|
||||
IsTimeStamp
|
||||
|
||||
QtObject:
|
||||
type
|
||||
Model* = ref object of QAbstractListModel
|
||||
items: seq[Item]
|
||||
itemsWithDateHeaders: seq[Item]
|
||||
hasMore: bool
|
||||
|
||||
proc delete(self: Model) =
|
||||
self.items = @[]
|
||||
self.itemsWithDateHeaders = @[]
|
||||
self.QAbstractListModel.delete
|
||||
|
||||
proc setup(self: Model) =
|
||||
|
@ -41,20 +50,20 @@ QtObject:
|
|||
result.hasMore = true
|
||||
|
||||
proc `$`*(self: Model): string =
|
||||
for i in 0 ..< self.items.len:
|
||||
result &= fmt"""[{i}]:({$self.items[i]})"""
|
||||
for i in 0 ..< self.itemsWithDateHeaders.len:
|
||||
result &= fmt"""[{i}]:({$self.itemsWithDateHeaders[i]})"""
|
||||
|
||||
proc countChanged(self: Model) {.signal.}
|
||||
|
||||
proc getCount*(self: Model): int {.slot.} =
|
||||
self.items.len
|
||||
self.itemsWithDateHeaders.len
|
||||
|
||||
QtProperty[int] count:
|
||||
read = getCount
|
||||
notify = countChanged
|
||||
|
||||
method rowCount(self: Model, index: QModelIndex = nil): int =
|
||||
return self.items.len
|
||||
return self.itemsWithDateHeaders.len
|
||||
|
||||
method roleNames(self: Model): Table[int, string] =
|
||||
{
|
||||
|
@ -73,16 +82,23 @@ QtObject:
|
|||
ModelRole.From.int:"from",
|
||||
ModelRole.To.int:"to",
|
||||
ModelRole.Contract.int:"contract",
|
||||
ModelRole.ChainID.int:"chainId",
|
||||
ModelRole.MaxFeePerGas.int:"maxFeePerGas",
|
||||
ModelRole.MaxPriorityFeePerGas.int:"maxPriorityFeePerGas",
|
||||
ModelRole.Input.int:"input",
|
||||
ModelRole.TxHash.int:"txHash",
|
||||
ModelRole.MultiTransactionID.int:"multiTransactionID",
|
||||
ModelRole.IsTimeStamp.int: "isTimeStamp"
|
||||
}.toTable
|
||||
|
||||
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
||||
if (not index.isValid):
|
||||
return
|
||||
|
||||
if (index.row < 0 or index.row >= self.items.len):
|
||||
if (index.row < 0 or index.row >= self.itemsWithDateHeaders.len):
|
||||
return
|
||||
|
||||
let item = self.items[index.row]
|
||||
let item = self.itemsWithDateHeaders[index.row]
|
||||
let enumRole = role.ModelRole
|
||||
|
||||
case enumRole:
|
||||
|
@ -116,17 +132,31 @@ QtObject:
|
|||
result = newQVariant(item.getTo())
|
||||
of ModelRole.Contract:
|
||||
result = newQVariant(item.getContract())
|
||||
of ModelRole.ChainID:
|
||||
result = newQVariant(item.getChainId())
|
||||
of ModelRole.MaxFeePerGas:
|
||||
result = newQVariant(item.getMaxFeePerGas())
|
||||
of ModelRole.MaxPriorityFeePerGas:
|
||||
result = newQVariant(item.getMaxPriorityFeePerGas())
|
||||
of ModelRole.Input:
|
||||
result = newQVariant(item.getInput())
|
||||
of ModelRole.TxHash:
|
||||
result = newQVariant(item.getTxHash())
|
||||
of ModelRole.MultiTransactionID:
|
||||
result = newQVariant(item.getMultiTransactionID())
|
||||
of ModelRole.IsTimeStamp:
|
||||
result = newQVariant(item.getIsTimeStamp())
|
||||
|
||||
proc setItems*(self: Model, items: seq[Item]) =
|
||||
self.beginResetModel()
|
||||
self.items = items
|
||||
self.itemsWithDateHeaders = items
|
||||
self.endResetModel()
|
||||
self.countChanged()
|
||||
|
||||
proc getLastTxBlockNumber*(self: Model): string {.slot.} =
|
||||
if (self.items.len == 0):
|
||||
if (self.itemsWithDateHeaders.len == 0):
|
||||
return "0x0"
|
||||
return self.items[^1].getBlockNumber()
|
||||
return self.itemsWithDateHeaders[^1].getBlockNumber()
|
||||
|
||||
proc hasMoreChanged*(self: Model) {.signal.}
|
||||
|
||||
|
@ -160,7 +190,7 @@ QtObject:
|
|||
t.address,
|
||||
t.blockNumber,
|
||||
t.blockHash,
|
||||
t.timestamp,
|
||||
toInt(t.timestamp),
|
||||
t.gasPrice,
|
||||
t.gasLimit,
|
||||
t.gasUsed,
|
||||
|
@ -169,14 +199,32 @@ QtObject:
|
|||
t.value,
|
||||
t.fromAddress,
|
||||
t.to,
|
||||
t.contract
|
||||
t.contract,
|
||||
t.chainId,
|
||||
t.maxFeePerGas,
|
||||
t.maxPriorityFeePerGas,
|
||||
t.input,
|
||||
t.txHash,
|
||||
t.multiTransactionID,
|
||||
false
|
||||
))
|
||||
|
||||
var allTxs = self.items.concat(newTxItems)
|
||||
allTxs.sort(cmpTransactions, SortOrder.Descending)
|
||||
eth_service_utils.deduplicate(allTxs, tx => tx.getId())
|
||||
|
||||
self.setItems(allTxs)
|
||||
# add day headers to the transaction list
|
||||
var itemsWithDateHeaders: seq[Item] = @[]
|
||||
var tempTimeStamp: Time
|
||||
for tx in allTxs:
|
||||
let duration = fromUnix(tx.getTimestamp()) - tempTimeStamp
|
||||
if(duration.inDays != 0):
|
||||
itemsWithDateHeaders.add(initItem("", "", "", "", "", tx.getTimestamp(), "", "", "", "", "", "", "", "", "", 0, "", "", "", "", 0, true))
|
||||
itemsWithDateHeaders.add(tx)
|
||||
tempTimeStamp = fromUnix(tx.getTimestamp())
|
||||
|
||||
self.items = allTxs
|
||||
self.setItems(itemsWithDateHeaders)
|
||||
self.setHasMore(true)
|
||||
else:
|
||||
self.setHasMore(false)
|
||||
|
|
|
@ -43,7 +43,12 @@ type
|
|||
value*: string
|
||||
fromAddress*: string
|
||||
to*: string
|
||||
chainId*: int
|
||||
chainId*: int
|
||||
maxFeePerGas*: string
|
||||
maxPriorityFeePerGas*: string
|
||||
input*: string
|
||||
txHash*: string
|
||||
multiTransactionID*: int
|
||||
|
||||
proc toTransactionDto*(jsonObj: JsonNode): TransactionDto =
|
||||
result = TransactionDto()
|
||||
|
@ -63,7 +68,11 @@ proc toTransactionDto*(jsonObj: JsonNode): TransactionDto =
|
|||
discard jsonObj.getProp("from", result.fromAddress)
|
||||
discard jsonObj.getProp("to", result.to)
|
||||
discard jsonObj.getProp("networkId", result.chainId)
|
||||
|
||||
discard jsonObj.getProp("maxFeePerGas", result.maxFeePerGas)
|
||||
discard jsonObj.getProp("maxPriorityFeePerGas", result.maxPriorityFeePerGas)
|
||||
discard jsonObj.getProp("input", result.input)
|
||||
discard jsonObj.getProp("txHash", result.txHash)
|
||||
discard jsonObj.getProp("multiTransactionID", result.multiTransactionID)
|
||||
|
||||
proc cmpTransactions*(x, y: TransactionDto): int =
|
||||
# Sort proc to compare transactions from a single account.
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit f6424328d87a4cac571f3b1c424f2dc125ced7b3
|
||||
Subproject commit 7108ac87cffe529d011ec1c14473630e52980282
|
|
@ -13,6 +13,7 @@ Control {
|
|||
property alias tagPrimaryLabel: tagPrimaryLabel
|
||||
property alias tagSecondaryLabel: tagSecondaryLabel
|
||||
property alias controlBackground: controlBackground
|
||||
property alias rightComponent: rightComponent.sourceComponent
|
||||
|
||||
horizontalPadding: Style.current.halfPadding
|
||||
verticalPadding: 5
|
||||
|
@ -62,5 +63,9 @@ Control {
|
|||
visible: text !== ""
|
||||
elide: Text.ElideMiddle
|
||||
}
|
||||
Loader {
|
||||
id: rightComponent
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,148 +1,95 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Core 0.1
|
||||
|
||||
import utils 1.0
|
||||
import shared 1.0
|
||||
import shared.panels 1.0
|
||||
import shared.stores 1.0
|
||||
|
||||
StatusListItem {
|
||||
id: root
|
||||
|
||||
Rectangle {
|
||||
id: transactionListItem
|
||||
property var modelData
|
||||
property string symbol
|
||||
property bool isIncoming
|
||||
property int transferStatus
|
||||
property string currentCurrency
|
||||
property string cryptoValue
|
||||
property string fiatValue
|
||||
property string networkIcon
|
||||
property string networkColor
|
||||
property string networkName
|
||||
property string shortTimeStamp
|
||||
property string resolvedSymbol: root.symbol != "" ? root.symbol : "ETH"
|
||||
property string savedAddressName
|
||||
|
||||
property var tokens
|
||||
property string currentAccountAddress: ""
|
||||
property string ethValue: ""
|
||||
property bool isHovered: false
|
||||
property string symbol: ""
|
||||
property string locale: ""
|
||||
property bool isIncoming: to === currentAccountAddress
|
||||
|
||||
signal launchTransactionModal()
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
height: 64
|
||||
color: isHovered ? Style.current.secondaryBackground : Style.current.transparent
|
||||
radius: 8
|
||||
|
||||
Component.onCompleted: {
|
||||
const count = transactionListItem.tokens.length
|
||||
for (var i = 0; i < count; i++) {
|
||||
let token = transactionListItem.tokens[i]
|
||||
if (token.address === contract) {
|
||||
transactionListItem.symbol = token.symbol
|
||||
break
|
||||
}
|
||||
asset.isImage: true
|
||||
asset.name: Style.png("tokens/%1".arg(resolvedSymbol))
|
||||
statusListItemTitle.font.weight: Font.Medium
|
||||
title: isIncoming ? qsTr("Receive %1").arg(resolvedSymbol) : !!savedAddressName ?
|
||||
qsTr("Send %1 to %2").arg(resolvedSymbol).arg(savedAddressName) :
|
||||
qsTr("Send %1 to %2").arg(resolvedSymbol).arg(Utils.compactAddress(modelData.to, 4))
|
||||
subTitle: shortTimeStamp
|
||||
inlineTagModel: 1
|
||||
inlineTagDelegate: InformationTag {
|
||||
tagPrimaryLabel.text: networkName
|
||||
tagPrimaryLabel.color: networkColor
|
||||
image.source: Style.svg("tiny/%1".arg(networkIcon))
|
||||
background: Rectangle {
|
||||
id: controlBackground
|
||||
implicitWidth: 51
|
||||
implicitHeight: 24
|
||||
color: "transparent"
|
||||
border.width: 1
|
||||
border.color: Theme.palette.baseColor2
|
||||
radius: 36
|
||||
}
|
||||
rightComponent: transferStatus === Constants.TransactionStatus.Success ? completedIcon : loadingIndicator
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: launchTransactionModal()
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
transactionListItem.isHovered = true
|
||||
}
|
||||
onExited: {
|
||||
transactionListItem.isHovered = false
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.smallPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 5
|
||||
|
||||
Image {
|
||||
id: assetIcon
|
||||
width: 40
|
||||
height: 40
|
||||
source: Style.png("tokens/"
|
||||
+ (transactionListItem.symbol
|
||||
!= "" ? transactionListItem.symbol : "ETH"))
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onStatusChanged: {
|
||||
if (assetIcon.status == Image.Error) {
|
||||
assetIcon.source = Style.png("tokens/DEFAULT-TOKEN@3x")
|
||||
components: [
|
||||
ColumnLayout {
|
||||
Row {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
spacing: 4
|
||||
StatusIcon {
|
||||
color: isIncoming ? Theme.palette.successColor1 : Theme.palette.dangerColor1
|
||||
icon: "arrow-up"
|
||||
rotation: isIncoming ? 135 : 45
|
||||
height: 18
|
||||
}
|
||||
StatusBaseText {
|
||||
text: "%1 %2".arg(cryptoValue).arg(resolvedSymbol)
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
}
|
||||
|
||||
anchors.leftMargin: Style.current.padding
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: "%1 %2".arg(fiatValue).arg(currentCurrency.toUpperCase())
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.baseColor1
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
StyledText {
|
||||
id: transferIcon
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: 15
|
||||
width: 15
|
||||
color: isIncoming ? Style.current.success : Style.current.danger
|
||||
text: isIncoming ? "↓" : "↑"
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: transactionValue
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pixelSize: Style.current.primaryTextFontSize
|
||||
text: ethValue + " " + transactionListItem.symbol
|
||||
Component {
|
||||
id: loadingIndicator
|
||||
StatusLoadingIndicator {
|
||||
height: 10
|
||||
width: 10
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.right: timeInfo.left
|
||||
anchors.rightMargin: Style.current.smallPadding
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Style.current.bigPadding
|
||||
spacing: 5
|
||||
|
||||
StyledText {
|
||||
text: isIncoming ?
|
||||
qsTr("From ") :
|
||||
qsTr("To ")
|
||||
color: Style.current.secondaryText
|
||||
font.pixelSize: Style.current.primaryTextFontSize
|
||||
font.strikeout: false
|
||||
}
|
||||
|
||||
Address {
|
||||
id: addressValue
|
||||
text: isIncoming ? from : to
|
||||
maxWidth: 120
|
||||
width: 120
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pixelSize: Style.current.primaryTextFontSize
|
||||
color: Style.current.textColor
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: timeInfo
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.smallPadding
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Style.current.bigPadding
|
||||
spacing: 5
|
||||
|
||||
StyledText {
|
||||
text: " • "
|
||||
font.weight: Font.Bold
|
||||
color: Style.current.secondaryText
|
||||
font.pixelSize: Style.current.primaryTextFontSize
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: timeIndicator
|
||||
text: qsTr("At ")
|
||||
color: Style.current.secondaryText
|
||||
font.pixelSize: Style.current.primaryTextFontSize
|
||||
font.strikeout: false
|
||||
}
|
||||
StyledText {
|
||||
id: timeValue
|
||||
text: Utils.formatLongDateTime(timestamp * 1000, RootStore.accountSensitiveSettings.isDDMMYYDateFormat, RootStore.accountSensitiveSettings.is24hTimeFormat)
|
||||
font.pixelSize: Style.current.primaryTextFontSize
|
||||
anchors.rightMargin: Style.current.smallPadding
|
||||
Component {
|
||||
id: completedIcon
|
||||
StatusIcon {
|
||||
visible: icon !== ""
|
||||
icon: "checkmark"
|
||||
color: Theme.palette.baseColor1
|
||||
width: 10
|
||||
height: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,10 @@ QtObject {
|
|||
return networksModule.all.getIconUrl(chainId)
|
||||
}
|
||||
|
||||
function getNetworkShortName(chainId) {
|
||||
return networksModule.all.getNetworkShortName(chainId)
|
||||
}
|
||||
|
||||
function getNetworkIconUrl(symbol) {
|
||||
return networksModule.all.getNetworkIconUrl(symbol)
|
||||
}
|
||||
|
@ -58,6 +62,14 @@ QtObject {
|
|||
return networksModule.all.getNetworkName(symbol)
|
||||
}
|
||||
|
||||
function getFiatValue(balance, cryptoSymbol, fiatSymbol) {
|
||||
return profileSectionModule.ensUsernamesModule.getFiatValue(balance, cryptoSymbol, fiatSymbol)
|
||||
}
|
||||
|
||||
function hex2Dec(value) {
|
||||
return globalUtils.hex2Dec(value)
|
||||
}
|
||||
|
||||
readonly property var formationChars: (["*", "`", "~"])
|
||||
function getSelectedTextWithFormationChars(messageInputField) {
|
||||
let i = 1
|
||||
|
@ -157,4 +169,13 @@ QtObject {
|
|||
function hex2Eth(value) {
|
||||
return globalUtils.hex2Eth(value)
|
||||
}
|
||||
|
||||
function findTokenSymbolByAddress(address) {
|
||||
return walletSectionAllTokens.findTokenSymbolByAddress(address)
|
||||
|
||||
}
|
||||
|
||||
function getNameForSavedWalletAddress(address) {
|
||||
return walletSectionSavedAddresses.getNameByAddress(address)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import QtQuick.Layouts 1.3
|
|||
import StatusQ.Core 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import utils 1.0
|
||||
|
||||
|
@ -13,48 +14,43 @@ import "../popups"
|
|||
import "../stores"
|
||||
import "../controls"
|
||||
|
||||
Item {
|
||||
ColumnLayout {
|
||||
id: historyView
|
||||
|
||||
property var account
|
||||
property int pageSize: 20 // number of transactions per page
|
||||
property bool isLoading: false
|
||||
|
||||
function fetchHistory() {
|
||||
if (RootStore.isFetchingHistory(historyView.account.address)) {
|
||||
loadingImg.active = true
|
||||
isLoading = true
|
||||
} else {
|
||||
RootStore.loadTransactionsForAccount(historyView.account.address, pageSize)
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: loadingImg
|
||||
active: false
|
||||
sourceComponent: loadingImageComponent
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.padding
|
||||
anchors.top: parent.top
|
||||
}
|
||||
|
||||
Component {
|
||||
id: loadingImageComponent
|
||||
StatusLoadingIndicator {}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: RootStore.history
|
||||
onLoadingTrxHistoryChanged: function(isLoading, address) {
|
||||
if (historyView.account.address.toLowerCase() === address.toLowerCase()) {
|
||||
loadingImg.active = isLoading
|
||||
isLoading = isLoading
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: loadingImg
|
||||
active: isLoading
|
||||
sourceComponent: loadingImageComponent
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
Layout.rightMargin: Style.current.padding
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: nonArchivalNodeError
|
||||
Layout.alignment: Qt.AlignTop
|
||||
|
||||
visible: RootStore.isNonArchivalNode
|
||||
height: visible ? implicitHeight : 0
|
||||
anchors.top: parent.top
|
||||
text: qsTr("Status Desktop is connected to a non-archival node. Transaction history may be incomplete.")
|
||||
font.pixelSize: Style.current.primaryTextFontSize
|
||||
color: Style.current.danger
|
||||
|
@ -62,59 +58,80 @@ Item {
|
|||
|
||||
StyledText {
|
||||
id: noTxs
|
||||
anchors.top: nonArchivalNodeError.bottom
|
||||
visible: transactionListRoot.count === 0
|
||||
height: visible ? implicitHeight : 0
|
||||
text: qsTr("No transactions found")
|
||||
font.pixelSize: Style.current.primaryTextFontSize
|
||||
}
|
||||
|
||||
StatusListView {
|
||||
id: transactionListRoot
|
||||
anchors.top: noTxs.bottom
|
||||
anchors.topMargin: Style.current.padding
|
||||
anchors.bottom: loadMoreButton.top
|
||||
anchors.bottomMargin: Style.current.padding
|
||||
width: parent.width
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.topMargin: nonArchivalNodeError.visible || noTxs.visible ? Style.current.padding : 0
|
||||
Layout.bottomMargin: Style.current.padding
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
model: RootStore.historyTransactions
|
||||
delegate: TransactionDelegate {
|
||||
tokens: RootStore.tokens
|
||||
locale: RootStore.locale
|
||||
currentAccountAddress: account.address
|
||||
ethValue: RootStore.hex2Eth(value)
|
||||
onLaunchTransactionModal: {
|
||||
transactionModal.transaction = model
|
||||
transactionModal.open()
|
||||
delegate: Loader {
|
||||
width: parent.width
|
||||
sourceComponent: isTimeStamp ? dateHeader : transactionDelegate
|
||||
onLoaded: {
|
||||
item.modelData = model
|
||||
}
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
id: scrollBar
|
||||
}
|
||||
ScrollBar.vertical: StatusScrollBar {}
|
||||
|
||||
onCountChanged: {
|
||||
if (loadMoreButton.loadedMore)
|
||||
transactionListRoot.positionViewAtEnd();
|
||||
footer: StatusButton {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
id: loadMoreButton
|
||||
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: !loadingImg.active && RootStore.historyTransactions.hasMore
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Style.current.padding
|
||||
property bool loadedMore: false
|
||||
|
||||
onClicked: {
|
||||
fetchHistory()
|
||||
loadMoreButton.loadedMore = true
|
||||
Component {
|
||||
id: dateHeader
|
||||
StatusListItem {
|
||||
property var modelData
|
||||
height: 40
|
||||
title: Utils.formatShortDate(modelData.timestamp * 1000, RootStore.accountSensitiveSettings.is24hTimeFormat)
|
||||
statusListItemTitle.color: Theme.palette.baseColor1
|
||||
color: Theme.palette.statusListItem.backgroundColor
|
||||
sensor.enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: transactionDelegate
|
||||
TransactionDelegate {
|
||||
isIncoming: modelData !== undefined ? modelData.to === account.address: false
|
||||
currentCurrency: RootStore.currentCurrency
|
||||
cryptoValue: modelData !== undefined ? RootStore.hex2Eth(modelData.value) : ""
|
||||
fiatValue: RootStore.getFiatValue(cryptoValue, resolvedSymbol, RootStore.currentCurrency)
|
||||
networkIcon: modelData !== undefined ? RootStore.getNetworkIcon(modelData.chainId) : ""
|
||||
networkColor: modelData !== undefined ? RootStore.getNetworkColor(modelData.chainId) : ""
|
||||
networkName: modelData !== undefined ? RootStore.getNetworkShortName(modelData.chainId) : ""
|
||||
symbol: modelData !== undefined ? RootStore.findTokenSymbolByAddress(modelData.contract) : ""
|
||||
transferStatus: modelData !== undefined ? RootStore.hex2Dec(modelData.txStatus) : ""
|
||||
shortTimeStamp: modelData !== undefined ? Utils.formatShortTime(modelData.timestamp * 1000, RootStore.accountSensitiveSettings.is24hTimeFormat) : ""
|
||||
savedAddressName: modelData !== undefined ? RootStore.getNameForSavedWalletAddress(modelData.to) : ""
|
||||
onClicked: {
|
||||
transactionModal.transaction = modelData
|
||||
transactionModal.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: loadingImageComponent
|
||||
StatusLoadingIndicator {}
|
||||
}
|
||||
|
||||
TransactionModal {
|
||||
id: transactionModal
|
||||
}
|
||||
|
|
|
@ -546,4 +546,9 @@ QtObject {
|
|||
readonly property bool isCppApp: typeof cppApp !== "undefined" ? cppApp : false
|
||||
|
||||
readonly property string existingAccountError: "account already exists"
|
||||
|
||||
enum TransactionStatus {
|
||||
Failure = 0,
|
||||
Success = 1
|
||||
}
|
||||
}
|
||||
|
|
|
@ -295,6 +295,15 @@ QtObject {
|
|||
return qsTr("%1D").arg(diffDay)
|
||||
}
|
||||
|
||||
function formatShortDate(value, isDDMMYYDateFormat) {
|
||||
const formatDDMMYY = "d MMMM yyyy"
|
||||
const formatMMDDYY = "MMMM d yyyy"
|
||||
const currentFormat = isDDMMYYDateFormat ? formatDDMMYY : formatMMDDYY
|
||||
var timeStamp = checkTimestamp(value, "formatLongDate") ? Qt.formatDate(new Date(value), currentFormat) :
|
||||
Qt.formatDate(new Date(), currentFormat)
|
||||
return formatShortDateStr(timeStamp)
|
||||
}
|
||||
|
||||
// To-do move to Wallet Store, this should not be under Utils.
|
||||
function findAssetByChainAndSymbol(chainIdToFind, assets, symbolToFind) {
|
||||
for(var i=0; i<assets.rowCount(); i++) {
|
||||
|
|
Loading…
Reference in New Issue