/* * This file is part of the Flowee project * Copyright (C) 2020-2025 Tom Zander * * 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 . */ 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: 90 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 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 } } }