Files
pay/guis/mobile/TransactionDetails.qml
T
2026-03-14 21:14:42 +01:00

281 lines
11 KiB
QML

/*
* This file is part of the Flowee project
* Copyright (C) 2022-2025 Tom Zander <tom@flowee.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick
import QtQuick.Controls.Basic as QQC2
import QtQuick.Layouts
import "../Flowee" as Flowee
import Flowee.org.pay
Page {
id: root
required property QtObject wallet
required property int txIndex
readonly property QtObject infoObject: wallet.txInfo(txIndex, root)
headerText: qsTr("Transaction Details")
property QtObject openInExplorer: QQC2.Action {
text: qsTr("Open in Explorer")
onTriggered: Pay.openInExplorer(root.infoObject.txid)
}
menuItems: [ openInExplorer ]
Flickable {
anchors.fill: parent
contentHeight: content.height
ColumnLayout {
id: content
width: parent.width
spacing: 10
PageTitledBox {
title: qsTr("Transaction Hash")
Item {
implicitHeight: txidLabel.implicitHeight
width: parent.width
Flowee.Label {
id: txidLabel
text: root.infoObject.txid
anchors.left: parent.left
anchors.right: copyIcon.left
anchors.rightMargin: 10
wrapMode: Text.WrapAnywhere
font.pixelSize: root.font.pixelSize * 0.9
Rectangle {
id: txidHighlight
anchors.fill: parent
color: palette.mid
opacity: 0
visible: opacity > 0
Timer {
running: parent.visible
onTriggered: parent.opacity = 0
interval: 500
}
Behavior on opacity { NumberAnimation { duration: 250 } }
}
}
Image {
id: copyIcon
anchors.right: parent.right
width: 20
height: 20
source: "qrc:/edit-copy" + (Pay.useDarkSkin ? "-light" : "") + ".svg"
MouseArea {
anchors.fill: parent
anchors.margins: -15
onClicked: {
txidHighlight.opacity = 0.6
Pay.copyToClipboard(txidLabel.text)
}
}
}
}
}
PageTitledBox {
title: qsTr("First Seen")
Flowee.Label {
Layout.fillWidth: true
text: Qt.formatDateTime(root.infoObject.date)
}
}
PageTitledBox {
title: {
var h = root.infoObject.minedHeight
if (h === -2)
return qsTr("Rejected")
if (h === -1)
return qsTr("Waiting for block")
return qsTr("Mined at")
}
Flowee.Label {
visible: text !== ""
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
Layout.fillWidth: true
text: {
let txHeight = root.infoObject.minedHeight
if (txHeight < 1)
return ""
var answer = txHeight + "\n" + Pay.formatBlockTime(txHeight)
let blockAge = Pay.chainHeight - txHeight + 1
answer += "\n"
answer += qsTr("%1 blocks ago", "", blockAge).arg(blockAge)
return answer
}
}
}
PageTitledBox {
title: qsTr("Transaction comment")
EditableLabel {
id: editableLabel
width: parent.width
text: root.infoObject.userComment
editable: infoObject.commentEditable
onEdited: infoObject.userComment = text
}
}
PageTitledBox {
visible: fiatPrices.visible
Flowee.FiatTxInfo {
id: fiatPrices
txInfo: root.infoObject
width: parent.width
}
}
PageTitledBox {
Flowee.Label {
text: qsTr("Size: %1 bytes").arg(infoObject.size)
}
Flowee.Label {
text: qsTr("Coinbase")
visible: root.infoObject.isCoinbase
}
}
// We can't calculate the fees of just any transaction, only for the ones
// this account created.
PageTitledBox {
title: qsTr("Fees paid")
visible: infoObject.fees > 0
Flowee.Label {
text: qsTr("%1 Satoshi / 1000 bytes").arg((infoObject.fees * 1000 / infoObject.size).toFixed(0))
}
Flowee.BitcoinAmountLabel {
value: infoObject.fees
fiatTimestamp: root.infoObject.date
colorize: false
}
}
PageTitledBox {
spacing: 10
title: {
if (infoObject.isFused)
return qsTr("Fused from my addresses")
if (infoObject.createdByUs)
return qsTr("Sent from my addresses")
if (infoObject.isFused)
return qsTr("Sent from addresses")
return ""
}
Repeater {
/*
* We only have the one transaction, which means we only have the previous txid and we have
* no details about which address or anything else these funds come from if we didn't
* create the Tx. So just don't show anything.
*/
model: infoObject.knownInputs
delegate: Item {
Layout.alignment: Qt.AlignRight
width: content.width
height: {
var neededWidth = inAddress.implicitWidth + 10 + amount.implicitWidth
if (fusedIcon.visible)
neededWidth += 6 + fusedIcon.width
if (neededWidth < content.width)
return inAddress.height + 10
return inAddress.height + amount.height + 16
}
Flowee.CFIcon {
id: fusedIcon
visible: modelData.fromFused
}
Flowee.AddressLabel {
id: inAddress
txInfo: modelData
x: fusedIcon.visible ? fusedIcon.width + 6 : 0
width: Math.min(implicitWidth, parent.width - (fusedIcon.visible ? fusedIcon.width: 0))
}
Flowee.BitcoinAmountLabel {
id: amount
value: -1 * modelData.value
fiatTimestamp: root.infoObject.date
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: 10
font.pixelSize: root.font.pixelSize * 0.9
}
}
}
}
PageTitledBox {
spacing: 10
title: {
if (infoObject.isFused)
return qsTr("Fused into my addresses")
if (infoObject.createdByUs)
return qsTr("Received at addresses")
return qsTr("Received at my addresses")
}
Repeater {
model: infoObject.knownOutputs
delegate: Item {
Layout.alignment: Qt.AlignRight
width: content.width
height: {
var neededWidth = outAddress.implicitWidth + 10 + outAmount.implicitWidth
if (ctIcon.visible)
neededWidth += 6 + ctIcon.width
if (neededWidth < width)
return outAmount.height + 10
return outAddress.height + outAmount.height + 16
}
Image {
id: ctIcon
visible: modelData.hasCashToken
source: "qrc:/CashTokens.svg"
width: 24
height: 24
}
Flowee.AddressLabel {
id: outAddress
txInfo: modelData
x: ctIcon.visible ? ctIcon.width + 6 : 0
highlight: modelData.forMe
width: Math.min(implicitWidth, parent.width - (ctIcon.visible ? ctIcon.width: 0))
}
Flowee.BitcoinAmountLabel {
id: outAmount
value: modelData.value
fiatTimestamp: root.infoObject.date
colorize: modelData.forMe
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: 10
font.pixelSize: root.font.pixelSize * 0.9
}
}
}
}
}
}
}