feat(@desktop/wallet): Handle multitransactions (#11124)

closes #11071
This commit is contained in:
Cuteivist 2023-06-30 17:07:53 +02:00 committed by GitHub
parent bade10c5e0
commit 7a7394628e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 198 additions and 233 deletions

View File

@ -21,7 +21,6 @@ import app_service/service/currency/service as currency_service
import app_service/service/transaction/service as transaction_service
import app_service/service/token/service as token_service
proc toRef*[T](obj: T): ref T =
new(result)
result[] = obj

View File

@ -93,38 +93,27 @@ QtObject:
return self.transaction
proc getSender*(self: ActivityEntry): string {.slot.} =
# TODO: lookup sender's name
if self.isMultiTransaction():
return self.multi_transaction.fromAddress
if self.transaction == nil:
error "getSender: ActivityEntry is not an transaction.Item"
return ""
return self.transaction[].getfrom()
QtProperty[string] sender:
read = getSender
proc getRecipient*(self: ActivityEntry): string {.slot.} =
# TODO: lookup recipient name
if self.isMultiTransaction():
return self.multi_transaction.toAddress
if self.transaction == nil:
error "getRecipient: ActivityEntry is not an transaction.Item"
return ""
return self.transaction[].getTo()
QtProperty[string] recipient:
read = getRecipient
proc getInAmount*(self: ActivityEntry): float {.slot.} =
return float(self.extradata.inAmount)
QtProperty[float] inAmount:
read = getInAmount
proc getOutAmount*(self: ActivityEntry): float {.slot.} =
return float(self.extradata.outAmount)
QtProperty[float] outAmount:
read = getOutAmount
proc getInSymbol*(self: ActivityEntry): string {.slot.} =
return self.extradata.inSymbol
@ -137,9 +126,20 @@ QtObject:
QtProperty[string] outSymbol:
read = getOutSymbol
proc getSymbol*(self: ActivityEntry): string {.slot.} =
if self.metadata.activityType == backend.ActivityType.Receive:
return self.getInSymbol()
return self.getOutSymbol()
QtProperty[string] symbol:
read = getSymbol
proc getTimestamp*(self: ActivityEntry): int {.slot.} =
if self.isMultiTransaction():
return self.multi_transaction.timestamp
if self.transaction == nil:
error "getTimestamp: ActivityEntry is not an transaction.Item"
return 0
# TODO: should we account for self.transaction[].isTimeStamp?
return self.transaction[].getTimestamp()
@ -191,7 +191,7 @@ QtObject:
proc getTotalFees*(self: ActivityEntry): QVariant {.slot.} =
if self.transaction == nil:
error "getTotalFees: ActivityEntry is not an transaction.Item"
return newQVariant(0)
return newQVariant(newCurrencyAmount())
return newQVariant(self.transaction[].getTotalFees())
QtProperty[QVariant] totalFees:
@ -257,47 +257,31 @@ QtObject:
QtProperty[string] nonce:
read = getNonce
# TODO: Replaced usage of these for in/out versions in the QML modules
proc getSymbol*(self: ActivityEntry): string {.slot.} =
proc getBlockNumber*(self: ActivityEntry): string {.slot.} =
if self.transaction == nil:
error "getSymbol: ActivityEntry is not an transaction.Item"
error "getBlockNumber: ActivityEntry is not an transaction.Item"
return ""
return $self.transaction[].getBlockNumber()
if self.metadata.activityType == backend.ActivityType.Receive:
return self.getInSymbol()
QtProperty[string] blockNumber:
read = getBlockNumber
return self.getOutSymbol()
proc getOutAmount*(self: ActivityEntry): float {.slot.} =
return float(self.extradata.outAmount)
QtProperty[string] symbol:
read = getSymbol
QtProperty[float] outAmount:
read = getOutAmount
proc getFromAmount*(self: ActivityEntry): float {.slot.} =
if self.isMultiTransaction():
return self.getOutAmount()
error "getFromAmount: ActivityEntry is not a MultiTransaction"
return 0.0
proc getInAmount*(self: ActivityEntry): float {.slot.} =
return float(self.extradata.inAmount)
QtProperty[float] fromAmount:
read = getFromAmount
proc getToAmount*(self: ActivityEntry): float {.slot.} =
if self.isMultiTransaction():
return self.getInAmount()
error "getToAmount: ActivityEntry is not a MultiTransaction"
return 0.0
QtProperty[float] toAmount:
read = getToAmount
proc getValue*(self: ActivityEntry): float {.slot.} =
if self.isMultiTransaction():
error "getToAmount: ActivityEntry is a MultiTransaction"
return 0.0
QtProperty[float] inAmount:
read = getInAmount
proc getAmount*(self: ActivityEntry): float {.slot.} =
if self.metadata.activityType == backend.ActivityType.Receive:
return self.getInAmount()
return self.getOutAmount()
QtProperty[float] value:
read = getValue
QtProperty[float] amount:
read = getAmount

View File

@ -39,33 +39,25 @@ Item {
QtObject {
id: d
readonly property bool isIncoming: root.isTransactionValid ? root.transaction.recipient.toLowerCase() === root.overview.mixedcaseAddress.toLowerCase() : false
readonly property bool isNFT: root.isTransactionValid ? root.transaction.isNFT : false
readonly property string savedAddressNameTo: root.isTransactionValid ? d.getNameForSavedWalletAddress(transaction.recipient) : ""
readonly property string savedAddressNameFrom: root.isTransactionValid ? d.getNameForSavedWalletAddress(transaction.sender): ""
readonly property string from: root.isTransactionValid ? !!savedAddressNameFrom ? savedAddressNameFrom : Utils.compactAddress(transaction.sender, 4): ""
readonly property string to: root.isTransactionValid ? !!savedAddressNameTo ? savedAddressNameTo : Utils.compactAddress(transaction.recipient, 4): ""
readonly property string savedAddressEns: root.isTransactionValid ? RootStore.getEnsForSavedWalletAddress(isIncoming ? transaction.sender : transaction.recipient) : ""
readonly property string savedAddressChains: root.isTransactionValid ? RootStore.getChainShortNamesForSavedWalletAddress(isIncoming ? transaction.sender : transaction.recipient) : ""
readonly property bool isIncoming: transactionType === Constants.TransactionType.Received
readonly property string networkShortName: root.isTransactionValid ? RootStore.getNetworkShortName(transaction.chainId) : ""
readonly property string networkFullName: root.isTransactionValid ? RootStore.getNetworkFullName(transaction.chainId): ""
readonly property string networkIcon: root.isTransactionValid ? RootStore.getNetworkIcon(transaction.chainId): ""
readonly property string networkIcon: isTransactionValid ? RootStore.getNetworkIcon(transaction.chainId) : ""
readonly property int blockNumber: root.isTransactionValid ? RootStore.hex2Dec(root.transaction.blockNumber) : 0
readonly property string bridgeNetworkIcon: "" // TODO fill when bridge data is implemented
readonly property string bridgeNetworkFullname: "" // TODO fill when bridge data is implemented
readonly property string bridgeNetworkShortName: "" // TODO fill when bridge data is implemented
readonly property int bridgeBlockNumber: 0 // TODO fill when bridge data is implemented
readonly property double swapCryptoValue: 0 // TODO fill when swap data is implemented
readonly property string swapSymbol: "" // TODO fill when swap data is implemented
readonly property string symbol: root.isTransactionValid ? transaction.symbol : ""
readonly property int toBlockNumber: 0 // TODO fill when bridge data is implemented
readonly property string toNetworkIcon: "" // TODO fill when bridge data is implemented
readonly property string toNetworkShortName: "" // TODO fill when bridge data is implemented
readonly property string symbol: isTransactionValid ? transaction.symbol : ""
readonly property string inSymbol: isTransactionValid ? transaction.inSymbol : ""
readonly property string outSymbol: isTransactionValid ? transaction.outSymbol : ""
readonly property var multichainNetworks: [] // TODO fill icon for networks for multichain
readonly property double cryptoValue: root.isTransactionValid ? transaction.value : 0.0
readonly property double fiatValue: root.isTransactionValid ? RootStore.getFiatValue(cryptoValue, symbol, RootStore.currentCurrency): 0.0
readonly property string fiatValueFormatted: root.isTransactionValid ? RootStore.formatCurrencyAmount(d.fiatValue, RootStore.currentCurrency) : ""
readonly property string cryptoValueFormatted: root.isTransactionValid ? RootStore.formatCurrencyAmount(d.cryptoValue, symbol) : ""
readonly property real feeEthValue: root.isTransactionValid && transaction.totalFees ? RootStore.getGasEthValue(transaction.totalFees.amount, 1) : 0
readonly property real feeFiatValue: root.isTransactionValid ? RootStore.getFiatValue(d.feeEthValue, "ETH", RootStore.currentCurrency) : 0
readonly property string fiatValueFormatted: root.isTransactionValid && !transactionHeader.isMultiTransaction ? RootStore.formatCurrencyAmount(transactionHeader.fiatValue, RootStore.currentCurrency) : ""
readonly property string cryptoValueFormatted: root.isTransactionValid && !transactionHeader.isMultiTransaction ? RootStore.formatCurrencyAmount(transaction.amount, transaction.symbol) : ""
readonly property string outFiatValueFormatted: root.isTransactionValid && transactionHeader.isMultiTransaction ? RootStore.formatCurrencyAmount(transactionHeader.outFiatValue, RootStore.currentCurrency) : ""
readonly property string outCryptoValueFormatted: root.isTransactionValid && transactionHeader.isMultiTransaction ? RootStore.formatCurrencyAmount(transaction.outAmount, transaction.outSymbol) : ""
readonly property real feeEthValue: root.isTransactionValid ? RootStore.getGasEthValue(transaction.totalFees.amount, 1) : 0 // TODO use directly?
readonly property real feeFiatValue: root.isTransactionValid ? RootStore.getFiatValue(d.feeEthValue, "ETH", RootStore.currentCurrency) : 0 // TODO use directly?
readonly property int transactionType: root.isTransactionValid ? transaction.txType : Constants.TransactionType.Send
readonly property string toNetworkName: "" // TODO fill network name for bridge
property string decodedInputData: ""
@ -116,26 +108,15 @@ Item {
objectName: "transactionDetailHeader"
width: parent.width
leftPadding: 0
modelData: transaction
currentCurrency: RootStore.currentCurrency
cryptoValue: d.cryptoValue
fiatValue: d.fiatValue
networkIcon: d.networkIcon
networkColor: root.isTransactionValid ? RootStore.getNetworkColor(transaction.chainId): ""
networkName: d.networkFullName
swapSymbol: d.swapSymbol
bridgeNetworkName: d.bridgeNetworkFullname
symbol: d.symbol
transactionStatus: root.isTransactionValid ? transaction.status : Constants.TransactionStatus.Pending
timeStampText: root.isTransactionValid ? qsTr("Signed at %1").arg(LocaleUtils.formatDateTime(transaction.timestamp * 1000, Locale.LongFormat)): ""
addressNameTo: root.isTransactionValid ? WalletStores.RootStore.getNameForAddress(transaction.recipient): ""
addressNameFrom: root.isTransactionValid ? WalletStores.RootStore.getNameForAddress(transaction.sender): ""
sensor.enabled: false
rootStore: RootStore
walletRootStore: WalletStores.RootStore
color: Theme.palette.transparent
state: "header"
modelData: transaction
timeStampText: root.isTransactionValid ? qsTr("Signed at %1").arg(LocaleUtils.formatDateTime(transaction.timestamp * 1000, Locale.LongFormat)): ""
rootStore: RootStore
walletRootStore: WalletStores.RootStore
onRetryClicked: d.retryTransaction()
}
@ -148,7 +129,7 @@ Item {
error: transactionHeader.transactionStatus === Constants.TransactionStatus.Failed
isLayer1: root.isTransactionValid && RootStore.getNetworkLayer(root.transaction.chainId) == 1
confirmations: root.isTransactionValid ? Math.abs(WalletStores.RootStore.getLatestBlockNumber(root.transaction.chainId) - d.blockNumber): 0
chainName: d.networkFullName
chainName: transactionHeader.networkName
timeStamp: root.isTransactionValid ? transaction.timestamp: ""
}
@ -157,7 +138,7 @@ Item {
}
WalletNftPreview {
visible: root.isTransactionValid && d.isNFT && !!transaction.nftImageUrl
visible: root.isTransactionValid && transactionHeader.isNFT && !!transaction.nftImageUrl
width: Math.min(304, progressBlock.width)
nftName: root.isTransactionValid ? transaction.nftName : ""
nftUrl: root.isTransactionValid && !!transaction.nftImageUrl ? transaction.nftImageUrl : ""
@ -187,8 +168,7 @@ Item {
RowLayout {
spacing: 0
width: parent.width
height: opacity > 0 ? Math.max(implicitHeight, 85) : 0
opacity: fromNetworkTile.visible || toNetworkTile.visible ? 1 : 0
height: fromNetworkTile.visible || toNetworkTile.visible ? 85 : 0
TransactionDataTile {
id: fromNetworkTile
Layout.fillWidth: true
@ -197,9 +177,9 @@ Item {
subTitle: {
switch(d.transactionType) {
case Constants.TransactionType.Swap:
return d.symbol
return d.outSymbol
case Constants.TransactionType.Bridge:
return d.networkFullName
return transactionHeader.networkName
default:
return ""
}
@ -207,7 +187,7 @@ Item {
asset.name: {
switch(d.transactionType) {
case Constants.TransactionType.Swap:
return !!d.symbol ? Constants.tokenIcon(d.symbol) : ""
return !!d.outSymbol ? Constants.tokenIcon(d.outSymbol) : ""
case Constants.TransactionType.Bridge:
return !!d.networkIcon ? Style.svg(d.networkIcon) : ""
default:
@ -224,9 +204,9 @@ Item {
subTitle: {
switch(d.transactionType) {
case Constants.TransactionType.Swap:
return d.swapSymbol
return d.inSymbol
case Constants.TransactionType.Bridge:
return d.bridgeNetworkFullname
return d.toNetworkName
default:
return ""
}
@ -234,9 +214,9 @@ Item {
asset.name: {
switch(d.transactionType) {
case Constants.TransactionType.Swap:
return !!d.swapSymbol ? Constants.tokenIcon(d.swapSymbol) : ""
return !!d.inSymbol ? Constants.tokenIcon(d.inSymbol) : ""
case Constants.TransactionType.Bridge:
return !!d.bridgeNetworkIcon ? Style.svg(d.bridgeNetworkIcon) : ""
return !!d.toNetworkIcon ? Style.svg(d.toNetworkIcon) : ""
default:
return ""
}
@ -262,7 +242,7 @@ Item {
TransactionAddressTile {
width: parent.width
title: qsTr("To")
addresses: root.isTransactionValid ? [root.transaction.recipient] : []
addresses: root.isTransactionValid && visible ? [root.transaction.recipient] : []
contactsStore: root.contactsStore
rootStore: WalletStores.RootStore
onButtonClicked: addressMenu.openReceiverMenu(this, addresses[0], d.networkShortName)
@ -281,7 +261,7 @@ Item {
}
TransactionDataTile {
width: parent.width
title: qsTr("%1 Tx hash").arg(d.networkFullName)
title: qsTr("%1 Tx hash").arg(transactionHeader.networkName)
subTitle: root.isTransactionValid ? root.transaction.txHash : ""
visible: !!subTitle
buttonIconName: "more"
@ -289,17 +269,17 @@ Item {
}
TransactionDataTile {
width: parent.width
title: qsTr("%1 Tx hash").arg(d.bridgeNetworkFullname)
title: qsTr("%1 Tx hash").arg(d.toNetworkName)
subTitle: "" // TODO fill tx hash for Bridge
visible: !!subTitle
buttonIconName: "more"
onButtonClicked: addressMenu.openTxMenu(this, subTitle, d.bridgeNetworkShortName)
onButtonClicked: addressMenu.openTxMenu(this, subTitle, d.toNetworkShortName)
}
TransactionContractTile {
// Used for Bridge and Swap to display 'From' network Protocol contract address
address: "" // TODO fill protocol contract address for 'from' network for Bridge and Swap
symbol: "" // TODO fill protocol name for Bridge and Swap
networkName: d.networkFullName
networkName: transactionHeader.networkName
shortNetworkName: d.networkShortName
visible: !!subTitle && (d.transactionType === Constants.TransactionType.Bridge || d.transactionType === Constants.TransactionType.Swap)
}
@ -307,15 +287,15 @@ Item {
// Used to display contract address for any network
address: root.isTransactionValid ? transaction.contract : ""
symbol: root.isTransactionValid ? d.symbol : ""
networkName: d.networkFullName
networkName: transactionHeader.networkName
shortNetworkName: d.networkShortName
}
TransactionContractTile {
// Used for Bridge to display 'To' network Protocol contract address
address: "" // TODO fill protocol contract address for 'to' network for Bridge
symbol: "" // TODO fill protocol name for Bridge
networkName: d.bridgeNetworkFullname
shortNetworkName: d.bridgeNetworkShortName
networkName: d.toNetworkName
shortNetworkName: d.toNetworkShortName
visible: !!subTitle && d.transactionType === Constants.TransactionType.Bridge
}
TransactionContractTile {
@ -337,15 +317,15 @@ Item {
return ""
switch(d.transactionType) {
case Constants.TransactionType.Swap:
return d.swapSymbol
return d.inSymbol
case Constants.TransactionType.Bridge:
return d.symbol
return d.outSymbol
default:
return ""
}
}
networkName: d.bridgeNetworkFullname
shortNetworkName: d.bridgeNetworkShortName
networkName: d.toNetworkName
shortNetworkName: d.toNetworkShortName
visible: root.isTransactionValid && !!subTitle
}
}
@ -393,7 +373,7 @@ Item {
Layout.fillHeight: true
Layout.fillWidth: true
title: qsTr("Network")
subTitle: d.networkFullName
subTitle: transactionHeader.networkName
asset.name: !!d.networkIcon ? Style.svg("%1".arg(d.networkIcon)) : ""
smallIcon: true
visible: d.transactionType !== Constants.TransactionType.Bridge
@ -446,17 +426,17 @@ Item {
}
TransactionDataTile {
width: parent.width
title: !!d.networkFullName ? qsTr("Included in Block on %1").arg(d.networkFullName) : qsTr("Included on Block")
title: !!transactionHeader.networkName ? qsTr("Included in Block on %1").arg(transactionHeader.networkName) : qsTr("Included on Block")
subTitle: d.blockNumber
tertiaryTitle: root.isTransactionValid ? LocaleUtils.formatDateTime(transaction.timestamp * 1000, Locale.LongFormat) : ""
visible: d.blockNumber > 0
}
TransactionDataTile {
width: parent.width
title: !!d.bridgeNetworkFullname ? qsTr("Included in Block on %1").arg(d.bridgeNetworkFullname) : qsTr("Included on Block")
subTitle: d.bridgeBlockNumber
title: !!d.toNetworkName ? qsTr("Included in Block on %1").arg(d.toNetworkName) : qsTr("Included on Block")
subTitle: d.toBlockNumber
tertiaryTitle: root.isTransactionValid ? LocaleUtils.formatDateTime(transaction.timestamp * 1000, Locale.LongFormat) : ""
visible: d.bridgeBlockNumber > 0
visible: d.toBlockNumber > 0
}
}
}
@ -464,7 +444,7 @@ Item {
Column {
width: progressBlock.width
spacing: Style.current.smallPadding
visible: !(d.isNFT && d.isIncoming)
visible: !(transactionHeader.isNFT && d.isIncoming)
RowLayout {
width: parent.width
@ -489,10 +469,10 @@ Item {
TransactionDataTile {
width: parent.width
title: qsTr("Amount sent")
subTitle: d.cryptoValueFormatted
tertiaryTitle: d.fiatValueFormatted
subTitle: transactionHeader.isMultiTransaction ? d.outCryptoValueFormatted : d.cryptoValueFormatted
tertiaryTitle: transactionHeader.isMultiTransaction ? d.outFiatValueFormatted : d.fiatValueFormatted
visible: {
if (d.isNFT)
if (transactionHeader.isNFT)
return false
switch(d.transactionType) {
case Constants.TransactionType.Send:
@ -508,24 +488,24 @@ Item {
width: parent.width
title: transactionHeader.transactionStatus === Constants.TransactionType.Pending ? qsTr("Amount to receive") : qsTr("Amount received")
subTitle: {
if (d.isNFT)
if (!root.isTransactionValid || transactionHeader.isNFT)
return ""
const type = d.transactionType
if (type === Constants.TransactionType.Swap) {
return RootStore.formatCurrencyAmount(d.swapCryptoValue, d.swapSymbol)
return RootStore.formatCurrencyAmount(transactionHeader.inCryptoValue, d.inSymbol)
} else if (type === Constants.TransactionType.Bridge) {
// Reduce crypto value by fee value
const valueInCrypto = RootStore.getCryptoValue(d.fiatValue - d.feeFiatValue, d.symbol, RootStore.currentCurrency)
return RootStore.formatCurrencyAmount(valueInCrypto, d.symbol)
const valueInCrypto = RootStore.getCryptoValue(transactionHeader.fiatValue - d.feeFiatValue, d.inSymbol, RootStore.currentCurrency)
return RootStore.formatCurrencyAmount(valueInCrypto, d.inSymbol)
}
return ""
}
tertiaryTitle: {
const type = d.transactionType
if (type === Constants.TransactionType.Swap) {
return RootStore.formatCurrencyAmount(d.swapCryptoValue, d.swapSymbol)
return RootStore.formatCurrencyAmount(transactionHeader.inFiatValue, RootStore.currentCurrency)
} else if (type === Constants.TransactionType.Bridge) {
return RootStore.formatCurrencyAmount(d.fiatValue - d.feeFiatValue, RootStore.currentCurrency)
return RootStore.formatCurrencyAmount(transactionHeader.fiatValue - d.feeFiatValue, RootStore.currentCurrency)
}
return ""
}
@ -535,7 +515,7 @@ Item {
width: parent.width
title: qsTr("Fees")
subTitle: {
if (!root.isTransactionValid || d.isNFT)
if (!root.isTransactionValid || transactionHeader.isNFT)
return ""
switch(d.transactionType) {
case Constants.TransactionType.Send:
@ -552,28 +532,30 @@ Item {
TransactionDataTile {
width: parent.width
// Using fees in this tile because of same higlight and color settings as Total
title: d.transactionType === Constants.TransactionType.Destroy || d.isNFT ? qsTr("Fees") : qsTr("Total")
title: d.transactionType === Constants.TransactionType.Destroy || transactionHeader.isNFT ? qsTr("Fees") : qsTr("Total")
subTitle: {
if (d.isNFT && d.isIncoming)
if (transactionHeader.isNFT && d.isIncoming)
return ""
const type = d.transactionType
if (type === Constants.TransactionType.Destroy || d.isNFT) {
if (type === Constants.TransactionType.Destroy || transactionHeader.isNFT) {
return RootStore.formatCurrencyAmount(d.feeEthValue, "ETH")
} else if (type === Constants.TransactionType.Receive || (type === Constants.TransactionType.Buy && progressBlock.isLayer1)) {
return d.cryptoValueFormatted
}
return "%1 + %2".arg(d.cryptoValueFormatted).arg(RootStore.formatCurrencyAmount(d.feeEthValue, "ETH"))
const cryptoValue = transactionHeader.isMultiTransaction ? d.outCryptoValueFormatted : d.cryptoValueFormatted
return "%1 + %2".arg(cryptoValue).arg(RootStore.formatCurrencyAmount(d.feeEthValue, "ETH"))
}
tertiaryTitle: {
if (d.isNFT && d.isIncoming)
if (transactionHeader.isNFT && d.isIncoming)
return ""
const type = d.transactionType
if (type === Constants.TransactionType.Destroy || d.isNFT) {
if (type === Constants.TransactionType.Destroy || transactionHeader.isNFT) {
return RootStore.formatCurrencyAmount(d.feeFiatValue, RootStore.currentCurrency)
} else if (type === Constants.TransactionType.Receive || (type === Constants.TransactionType.Buy && progressBlock.isLayer1)) {
return d.fiatValueFormatted
}
return RootStore.formatCurrencyAmount(d.fiatValue + d.feeFiatValue, RootStore.currentCurrency)
const fiatValue = transactionHeader.isMultiTransaction ? transactionHeader.outFiatValue : transactionHeader.fiatValue
return RootStore.formatCurrencyAmount(fiatValue + d.feeFiatValue, RootStore.currentCurrency)
}
visible: !!subTitle
highlighted: true
@ -624,7 +606,22 @@ Item {
component DetailsPanel: Item {
width: parent.width
height: detailsColumn.childrenRect.height
height: {
// Using childrenRect and transactionvalid properties to refresh this binding
if (!isTransactionValid || detailsColumn.childrenRect.height === 0)
return 0
// Height is calculated from visible children because Column doesn't handle
// visibility change properly and childrenRect.height gives different values
// comparing to manual check
var visibleHeight = 0
for (var i = 0 ; i < detailsColumn.children.length ; i++) {
if (detailsColumn.children[i].visible)
visibleHeight += detailsColumn.children[i].height
}
return visibleHeight
}
default property alias content: detailsColumn.children
Rectangle {
@ -659,7 +656,7 @@ Item {
property string address: ""
property string shortNetworkName: ""
width: parent.width
title: qsTr("%1 %2 contract address").arg(networkName).arg(symbol)
title: visible ? qsTr("%1 %2 contract address").arg(networkName).arg(symbol) : ""
subTitle: !!address && !/0x0+$/.test(address) ? address : ""
buttonIconName: "more"
visible: !!subTitle

View File

@ -64,7 +64,7 @@ Item {
charactersLen: 2
}
implicitHeight: Math.max(identicon.height, contentColumn.height) + 12
implicitHeight: Math.max(44, contentColumn.height) + 12
QtObject {
id: d

View File

@ -62,7 +62,7 @@ StatusListItem {
height: visible ? implicitHeight + bottomPadding : 0
radius: 0
sensor.cursorShape: Qt.ArrowCursor
color: sensor.containsMouse ? Theme.palette.baseColor5 : Style.current.transparent
color: sensor.containsMouse || highlighted ? Theme.palette.baseColor5 : Style.current.transparent
// Title
statusListItemTitle.customColor: Theme.palette.directColor5

View File

@ -23,23 +23,10 @@ import shared 1.0
TransactionDelegate {
id: delegate
width: ListView.view.width
modelData: model
swapCryptoValue: 0.18
swapFiatValue: 340
swapSymbol: "SNT"
timeStampText: LocaleUtils.formatRelativeTimestamp(modelData.timestamp * 1000)
cryptoValue: 0.1234
fiatValue: 123123
currentCurrency: "USD"
networkName: "Optimism"
symbol: "ETH"
bridgeNetworkName: "Mainnet"
feeFiatValue: 10.34
feeCryptoValue: 0.013
transactionStatus: TransactionDelegate.Pending
transactionType: TransactionDelegate.Send
modelData: model.activityEntry
rootStore: RootStore
loading: isModelDataValid && modelData.loadingTransaction
walletRootStore: WalletStore.RootStore
loading: isModelDataValid
}
\endqml
@ -52,59 +39,53 @@ StatusListItem {
signal retryClicked()
property var modelData
property string symbol
property string swapSymbol // TODO fill when swap data is implemented
property int transactionStatus
property string currentCurrency
property double cryptoValue
property double swapCryptoValue // TODO fill when swap data is implemented
property double fiatValue
property double swapFiatValue // TODO fill when swap data is implemented
property double feeCryptoValue // TODO fill when bridge data is implemented
property double feeFiatValue // TODO fill when bridge data is implemented
property string networkIcon
property string networkColor
property string networkName
property string bridgeNetworkName // TODO fill when bridge data is implemented
property string timeStampText
property string addressNameTo
property string addressNameFrom
property var rootStore
property var walletRootStore
property string timeStampText: isModelDataValid ? LocaleUtils.formatRelativeTimestamp(modelData.timestamp * 1000) : ""
required property var rootStore
required property var walletRootStore
readonly property bool isModelDataValid: modelData !== undefined && !!modelData
readonly property int transactionStatus: isModelDataValid ? modelData.status : Constants.TransactionStatus.Pending
readonly property bool isMultiTransaction: isModelDataValid && modelData.isMultiTransaction
readonly property string currentCurrency: rootStore.currentCurrency
readonly property double cryptoValue: isModelDataValid && !isMultiTransaction ? modelData.amount : 0.0
readonly property double fiatValue: isModelDataValid && !isMultiTransaction ? rootStore.getFiatValue(cryptoValue, modelData.symbol, currentCurrency) : 0.0
readonly property double inCryptoValue: isModelDataValid ? modelData.inAmount : 0.0
readonly property double inFiatValue: isModelDataValid && isMultiTransaction ? rootStore.getFiatValue(inCryptoValue, modelData.inSymbol, currentCurrency): 0.0
readonly property double outCryptoValue: isModelDataValid ? modelData.outAmount : 0.0
readonly property double outFiatValue: isModelDataValid && isMultiTransaction ? rootStore.getFiatValue(outCryptoValue, modelData.outSymbol, currentCurrency): 0.0
readonly property double feeCryptoValue: 0.0 // TODO fill when bridge data is implemented
readonly property double feeFiatValue: 0.0 // TODO fill when bridge data is implemented
readonly property string networkColor: isModelDataValid ? rootStore.getNetworkColor(modelData.chainId) : ""
readonly property string networkName: isModelDataValid ? rootStore.getNetworkFullName(modelData.chainId) : ""
readonly property string addressNameTo: isModelDataValid ? walletRootStore.getNameForAddress(modelData.recipient) : ""
readonly property string addressNameFrom: isModelDataValid ? walletRootStore.getNameForAddress(modelData.sender) : ""
readonly property bool isNFT: isModelDataValid && modelData.isNFT
readonly property string transactionValue: {
if (!isModelDataValid)
return qsTr("N/A")
if (root.isNFT) {
return modelData.nftName ? modelData.nftName : "#" + modelData.tokenID
} else {
return root.rootStore.formatCurrencyAmount(cryptoValue, symbol)
}
}
readonly property string swapTransactionValue: {
if (!isModelDataValid) {
return qsTr("N/A")
} else if (root.isNFT) {
return modelData.nftName ? modelData.nftName : "#" + modelData.tokenID
}
return root.rootStore.formatCurrencyAmount(swapCryptoValue, swapSymbol)
return root.rootStore.formatCurrencyAmount(cryptoValue, modelData.symbol)
}
readonly property string inTransactionValue: isModelDataValid && isMultiTransaction ? rootStore.formatCurrencyAmount(inCryptoValue, modelData.inSymbol) : qsTr("N/A")
readonly property string outTransactionValue: isModelDataValid && isMultiTransaction ? rootStore.formatCurrencyAmount(outCryptoValue, modelData.outSymbol) : qsTr("N/A")
readonly property string tokenImage: {
if (!isModelDataValid)
return ""
if (root.isNFT) {
return modelData.nftImageUrl ? modelData.nftImageUrl : ""
} else {
return Constants.tokenIcon(root.symbol)
return Constants.tokenIcon(isMultiTransaction ? modelData.outSymbol : modelData.symbol)
}
}
readonly property string swapTokenImage: {
if (!isModelDataValid)
return ""
return Constants.tokenIcon(root.swapSymbol)
}
readonly property string inTokenImage: isModelDataValid ? Constants.tokenIcon(modelData.inSymbol) : ""
readonly property string toAddress: !!addressNameTo ?
addressNameTo :
@ -168,7 +149,7 @@ StatusListItem {
const endl = "\n"
const endl2 = endl + endl
const type = modelData.txType
const feeEthValue = modelData.totalFees ? rootStore.getGasEthValue(modelData.totalFees.amount, 1) : 0
const feeEthValue = rootStore.getGasEthValue(modelData.totalFees.amount, 1)
// TITLE
switch (type) {
@ -258,20 +239,21 @@ StatusListItem {
}
// SUMMARY ADRESSES
const toNetworkName = "" // TODO fill when bridge data is implemented
switch (type) {
case Constants.TransactionType.Swap:
details += qsTr("From") + endl + root.symbol + endl2
details += qsTr("To") + endl + root.swapSymbol + endl2
details += qsTr("From") + endl + modelData.outSymbol + endl2
details += qsTr("To") + endl + modelData.inSymbol + endl2
details += qsTr("In") + endl + root.fromAddress + endl2
break
case Constants.TransactionType.Bridge:
details += qsTr("From") + endl + root.networkName + endl2
details += qsTr("To") + endl + root.bridgeNetworkName + endl2
details += qsTr("To") + endl + toNetworkName + endl2
details += qsTr("In") + endl + modelData.from + endl2
break
default:
details += qsTr("From") + endl + modelData.from + endl2
details += qsTr("To") + endl + modelData.to + endl2
details += qsTr("From") + endl + modelData.sender + endl2
details += qsTr("To") + endl + modelData.recipient + endl2
break
}
const protocolName = "" // TODO fill protocol name for Bridge and Swap
@ -283,7 +265,7 @@ StatusListItem {
}
const bridgeTxHash = "" // TODO fill tx hash for Bridge
if (!!bridgeTxHash) {
details += qsTr("%1 Tx hash").arg(root.bridgeNetworkName) + endl + bridgeTxHash + endl2
details += qsTr("%1 Tx hash").arg(toNetworkName) + endl + bridgeTxHash + endl2
}
const protocolFromContractAddress = "" // TODO fill protocol contract address for 'from' network for Bridge and Swap
if (!!protocolName && !!protocolFromContractAddress) {
@ -291,12 +273,12 @@ StatusListItem {
details += protocolFromContractAddress + endl2
}
if (!!modelData.contract) {
details += qsTr("%1 %2 contract address").arg(root.networkName).arg(root.symbol) + endl
details += qsTr("%1 %2 contract address").arg(root.networkName).arg(modelData.symbol) + endl
details += modelData.contract + endl2
}
const protocolToContractAddress = "" // TODO fill protocol contract address for 'to' network for Bridge
if (!!protocolToContractAddress && !!protocolName) {
details += qsTr("%1 %2 contract address").arg(root.bridgeNetworkName).arg(protocolName) + endl
details += qsTr("%1 %2 contract address").arg(toNetworkName).arg(protocolName) + endl
details += protocolToContractAddress + endl2
}
const swapContractAddress = "" // TODO fill swap contract address for Swap
@ -304,13 +286,13 @@ StatusListItem {
switch (type) {
case Constants.TransactionType.Swap:
if (!!swapContractAddress) {
details += qsTr("%1 %2 contract address").arg(root.networkName).arg(root.swapSymbol) + endl
details += qsTr("%1 %2 contract address").arg(root.networkName).arg(modelData.toSymbol) + endl
details += swapContractAddress + endl2
}
break
case Constants.TransactionType.Bridge:
if (!!bridgeContractAddress) {
details += qsTr("%1 %2 contract address").arg(root.bridgeNetworkName).arg(root.symbol) + endl
details += qsTr("%1 %2 contract address").arg(toNetworkName).arg(modelData.symbol) + endl
details += bridgeContractAddress + endl2
}
break
@ -327,7 +309,7 @@ StatusListItem {
if (type === Constants.TransactionType.Bridge) {
details += qsTr("Included in Block on %1").arg(networkName) + endl
details += rootStore.hex2Dec(modelData.blockNumber) + endl2
details += qsTr("Included in Block on %1").arg(bridgeNetworkName) + endl
details += qsTr("Included in Block on %1").arg(toNetworkName) + endl
const bridgeBlockNumber = 0 // TODO fill when bridge data is implemented
details += rootStore.hex2Dec(bridgeBlockNumber) + endl2
} else {
@ -335,27 +317,29 @@ StatusListItem {
}
// VALUES
const fiatTransactionValue = rootStore.formatCurrencyAmount(root.fiatValue, root.currentCurrency)
const fiatTransactionValue = rootStore.formatCurrencyAmount(isMultiTransaction ? root.outFiatValue : root.fiatValue, root.currentCurrency)
const feeFiatValue = rootStore.getFiatValue(feeEthValue, "ETH", root.currentCurrency)
let valuesString = ""
if (!root.isNFT) {
switch(type) {
case Constants.TransactionType.Send:
valuesString += qsTr("Amount sent %1 (%2)").arg(root.transactionValue).arg(fiatTransactionValue) + endl2
break
case Constants.TransactionType.Swap:
case Constants.TransactionType.Bridge:
valuesString += qsTr("Amount sent %1 (%2)").arg(root.transactionValue).arg(fiatTransactionValue) + endl2
valuesString += qsTr("Amount sent %1 (%2)").arg(root.outTransactionValue).arg(fiatTransactionValue) + endl2
break
default:
break
}
if (type === Constants.TransactionType.Swap) {
const crypto = rootStore.formatCurrencyAmount(root.swapCryptoValue, root.swapSymbol)
const fiat = rootStore.formatCurrencyAmount(root.swapCryptoValue, root.swapSymbol)
const crypto = rootStore.formatCurrencyAmount(root.inCryptoValue, modelData.inSymbol)
const fiat = rootStore.formatCurrencyAmount(root.inCryptoValue, modelData.inSymbol)
valuesString += qsTr("Amount received %1 (%2)").arg(crypto).arg(fiat) + endl2
} else if (type === Constants.TransactionType.Bridge) {
// Reduce crypto value by fee value
const valueInCrypto = rootStore.getCryptoValue(root.fiatValue - feeFiatValue, root.symbol, root.currentCurrency)
const crypto = rootStore.formatCurrencyAmount(valueInCrypto, root.symbol)
const valueInCrypto = rootStore.getCryptoValue(root.fiatValue - feeFiatValue, modelData.inSymbol, root.currentCurrency)
const crypto = rootStore.formatCurrencyAmount(valueInCrypto, modelData.inSymbol)
const fiat = rootStore.formatCurrencyAmount(root.fiatValue - feeFiatValue, root.currentCurrency)
valuesString += qsTr("Amount received %1 (%2)").arg(crypto).arg(fiat) + endl2
}
@ -381,7 +365,8 @@ StatusListItem {
valuesString += qsTr("Total %1 (%2)").arg(root.transactionValue).arg(fiatTransactionValue) + endl2
} else {
const feeEth = rootStore.formatCurrencyAmount(feeEthValue, "ETH")
valuesString += qsTr("Total %1 + %2 (%3)").arg(root.transactionValue).arg(feeEth).arg(fiatTransactionValue) + endl2
const txValue = isMultiTransaction ? root.inTransactionValue : root.transactionValue
valuesString += qsTr("Total %1 + %2 (%3)").arg(txValue).arg(feeEth).arg(fiatTransactionValue) + endl2
}
}
@ -399,6 +384,7 @@ StatusListItem {
rightPadding: 16
enabled: !loading
loading: !isModelDataValid
color: sensor.containsMouse ? Theme.palette.baseColor5 : Style.current.transparent
statusListItemIcon.active: (loading || root.asset.name)
@ -486,15 +472,15 @@ StatusListItem {
spacing: 8
Row {
visible: !root.loading
spacing: swapTokenImage.visible ? -tokenImage.width * 0.2 : 0
spacing: secondTokenImage.visible ? -tokenImage.width * 0.2 : 0
StatusRoundIcon {
id: tokenImage
anchors.verticalCenter: parent.verticalCenter
asset: root.tokenIconAsset
}
StatusRoundIcon {
id: swapTokenImage
visible: root.isModelDataValid && !root.isNFT && !!root.swapTokenImage &&modelData.txType === Constants.TransactionType.Swap
id: secondTokenImage
visible: root.isModelDataValid && !root.isNFT && !!root.inTokenImage &&modelData.txType === Constants.TransactionType.Swap
anchors.verticalCenter: parent.verticalCenter
asset: StatusAssetSettings {
width: root.tokenIconAsset.width
@ -505,7 +491,7 @@ StatusListItem {
bgColor: root.color
isImage:root.tokenIconAsset.isImage
color: root.tokenIconAsset.color
name: root.swapTokenImage
name: root.inTokenImage
isLetterIdenticon: root.tokenIconAsset.isLetterIdenticon
}
}
@ -537,11 +523,12 @@ StatusListItem {
case Constants.TransactionType.Sell:
return qsTr("%1 on %2 via %3").arg(transactionValue).arg(toAddress).arg(networkName)
case Constants.TransactionType.Destroy:
return qsTr("%1 at %2 via %3").arg(transactionValue).arg(toAddress).arg(networkName)
return qsTr("%1 at %2 via %3").arg(inTransactionValue).arg(toAddress).arg(networkName)
case Constants.TransactionType.Swap:
return qsTr("%1 to %2 via %3").arg(transactionValue).arg(swapTransactionValue).arg(networkName)
return qsTr("%1 to %2 via %3").arg(outTransactionValue).arg(inTransactionValue).arg(networkName)
case Constants.TransactionType.Bridge:
return qsTr("%1 from %2 to %3").arg(transactionValue).arg(bridgeNetworkName).arg(networkName)
let toNetworkName = "" // TODO fill when Bridge data is implemented
return qsTr("%1 from %2 to %3").arg(inTransactionValue).arg(networkName).arg(toNetworkName)
default:
return qsTr("%1 to %2 via %3").arg(transactionValue).arg(toAddress).arg(networkName)
}
@ -574,14 +561,18 @@ StatusListItem {
case Constants.TransactionType.Receive:
return "+" + root.transactionValue
case Constants.TransactionType.Swap:
let outValue = root.outTransactionValue
outValue = outValue.replace('<', '&lt;')
let inValue = root.inTransactionValue
inValue = inValue.replace('<', '&lt;')
return "<font color=\"%1\">-%2</font> <font color=\"%3\">/</font> <font color=\"%4\">+%5</font>"
.arg(Theme.palette.directColor1)
.arg(root.transactionValue)
.arg(outValue)
.arg(Theme.palette.baseColor1)
.arg(Theme.palette.successColor1)
.arg(root.swapTransactionValue)
.arg(inValue)
case Constants.TransactionType.Bridge:
return "" + root.rootStore.formatCurrencyAmount(feeCryptoValue, root.symbol)
return "" + root.rootStore.formatCurrencyAmount(feeCryptoValue, modelData.symbol)
default:
return ""
}
@ -623,8 +614,8 @@ StatusListItem {
case Constants.TransactionType.Receive:
return "+" + root.rootStore.formatCurrencyAmount(root.fiatValue, root.currentCurrency)
case Constants.TransactionType.Swap:
return "-%1 / +%2".arg(root.rootStore.formatCurrencyAmount(root.fiatValue, root.currentCurrency))
.arg(root.rootStore.formatCurrencyAmount(root.swapFiatValue, root.currentCurrency))
return "-%1 / +%2".arg(root.rootStore.formatCurrencyAmount(root.outFiatValue, root.currentCurrency))
.arg(root.rootStore.formatCurrencyAmount(root.inFiatValue, root.currentCurrency))
case Constants.TransactionType.Bridge:
return "" + root.rootStore.formatCurrencyAmount(root.feeFiatValue, root.currentCurrency)
default:

View File

@ -256,19 +256,11 @@ ColumnLayout {
Component {
id: transactionDelegate
TransactionDelegate {
required property var model
required property int index
width: ListView.view.width
modelData: model.activityEntry
currentCurrency: RootStore.currentCurrency
cryptoValue: isModelDataValid ? modelData.value : 0.0
fiatValue: isModelDataValid ? RootStore.getFiatValue(cryptoValue, symbol, currentCurrency): 0.0
networkIcon: isModelDataValid ? RootStore.getNetworkIcon(modelData.chainId) : ""
networkColor: isModelDataValid ? RootStore.getNetworkColor(modelData.chainId) : ""
networkName: isModelDataValid ? RootStore.getNetworkFullName(modelData.chainId) : ""
symbol: isModelDataValid && !!modelData.symbol ? modelData.symbol : ""
transactionStatus: isModelDataValid ? modelData.status : 0
timeStampText: isModelDataValid ? LocaleUtils.formatRelativeTimestamp(modelData.timestamp * 1000, true) : ""
addressNameTo: isModelDataValid ? WalletStores.RootStore.getNameForAddress(modelData.recipient) : ""
addressNameFrom: isModelDataValid ? WalletStores.RootStore.getNameForAddress(modelData.sender) : ""
rootStore: RootStore
walletRootStore: WalletStores.RootStore
onClicked: {
@ -309,6 +301,8 @@ ColumnLayout {
model: RootStore.historyTransactions.hasMore || d.isInitialLoading ? 10 : 0
TransactionDelegate {
Layout.fillWidth: true
rootStore: RootStore
walletRootStore: WalletStores.RootStore
loading: true
}
}