Files
pay/guis/desktop/Transaction.qml
T

223 lines
6.9 KiB
QML

/*
* This file is part of the Flowee project
* Copyright (C) 2020-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 as QQC2
import "../Flowee" as Flowee
import "../Utils.js" as Utils
Rectangle {
id: txRoot
height: {
var rc = mainLabel.height + 10 + date.height
if (detailsPane.item != null)
rc += detailsPane.item.height;
return rc;
}
width: mainLabel.width + bitcoinAmountLabel.width + 30
property int minedHeight: model.height
property bool isRejected: minedHeight === -2 // -2 is the magic block-height indicating 'rejected'
property bool isMoved: Utils.isMoved(model);
/*
we have
model.fundsIn the amount of satoshis consumed by inputs we own
model.fundsOut the amout of satoshis locked up in outputs we own
model.txid
model.height
model.date
model.isCoinbase
model.isFused
model.comment
*/
// overlay that indicates the transactions is new since last sync.
Rectangle {
id: isNewIndicator
anchors.verticalCenter: mainLabel.verticalCenter
width: model.isNew ? 5 : 0 // avoid taking space
height: width
radius: width
color: Pay.useDarkSkin ? mainWindow.floweeSalmon : mainWindow.floweeBlue
}
Flowee.Label {
id: mainLabel
anchors.left: isNewIndicator.right
anchors.leftMargin: model.isNew ? 3 : 0
text: {
if (model.isCoinbase)
return qsTr("Miner Reward")
if (model.isFused)
return qsTr("Fused")
if (model.fundsIn === 0)
return qsTr("Received")
if (isMoved)
return qsTr("Moved");
return qsTr("Sent")
}
}
Flowee.Label {
id: date
anchors.top: mainLabel.bottom
function updateText() {
if (txRoot.isRejected)
text = qsTr("rejected")
else
text = Pay.formatDateTime(model.date);
}
opacity: detailsPane.item === null ? 0.8 : 1;
font.pointSize: mainLabel.font.pointSize * 0.9
color: txRoot.isRejected ? (Pay.useDarkSkin ? "#ec2327" : "#b41214") : palette.windowText
Component.onCompleted: date.updateText()
Timer {
interval: 60000
running: !txRoot.isRejected
repeat: true
onTriggered: date.updateText()
}
}
Flowee.CFIcon {
id: fusedIcon
visible: model.isFused
anchors.right: userComment.left
anchors.rightMargin: 6
anchors.verticalCenter: userComment.verticalCenter
width: 16
height: 16
}
Flowee.Label {
id: userComment
y: (date.y + date.height - height) / 2
anchors {
// Pick the widest label to be aligned to
left: date.width > mainLabel.width ? date.right : mainLabel.right
right: bitcoinAmountLabel.left
leftMargin: fusedIcon.visible ? 25 : 6
rightMargin: 10
}
text: model.comment
elide: Text.ElideRight
Connections {
target: date
function onTextChanged() {
if (date.width > mainLabel.width) {
userComment.anchors.left = date.right
} else {
userComment.anchors.left = mainLabel.right
}
}
}
}
Flowee.BitcoinAmountLabel {
id: bitcoinAmountLabel
visible: Pay.activityShowsBch || !Pay.isMainChain
fiatTimestamp: model.date
fiatWidth: Pay.isMainChain ? 90 : 0
value: {
let inputs = model.fundsIn
let outputs = model.fundsOut
let diff = model.fundsOut - model.fundsIn
if (!model.isFused
&& diff < 0 && diff > -1000) // then the diff is likely just fees.
return inputs;
return diff;
}
anchors.top: mainLabel.top
anchors.right: parent.right
colorize: !isMoved
}
Flowee.Label {
anchors.top: mainLabel.top
anchors.right: parent.right
visible: bitcoinAmountLabel.visible === false
text: {
if (isRejected)
return "";
var fiatPrice = Fiat.historicalPrice(model.date);
return Fiat.formattedPrice(bitcoinAmountLabel.value, fiatPrice)
}
color: {
var num = bitcoinAmountLabel.value
if (num > 0) // positive value
return Pay.useDarkSkin ? "#86ffa8" : "green";
else if (num < 0) // negative
return Pay.useDarkSkin ? "#ffdede" : "#444446";
// zero is shown without normally
return palette.windowText
}
}
Item {
id: checks
width: row.width
height: 18
anchors.right: parent.right
anchors.top: mainLabel.bottom
property int txHeight: model.height
property int confirmations: Pay.chainHeight - txHeight + 1
visible: txHeight === -1 || confirmations < 5
Row {
id: row
height: parent.height
spacing: -3
Flowee.Label {
text: "?"
font.bold: true
font.pixelSize: 14
color: Pay.useDarkSkin ? "#5d94c7" : mainWindow.floweeBlue
visible: checks.txHeight === -1
}
Repeater {
model: {
if (checks.txHeight < 0)
return 0;
var c = checks.confirmations;
return c < 5 ? c : 0;
}
Flowee.Label {
text: "✓"
font.pixelSize: 18
color: Pay.useDarkSkin ? "#86ffa8" : "green";
}
}
}
}
MouseArea {
anchors.fill: parent
onClicked: detailsPane.source = (detailsPane.source == "") ? "./TransactionInfoSmall.qml" : ""
}
Loader {
id: detailsPane
anchors.bottom: parent.bottom
anchors.bottomMargin: 6
width: parent.width
onLoaded: item.infoObject = portfolio.current.txInfo(model.walletIndex, item)
}
Behavior on height { NumberAnimation { duration: 100 } }
}