Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions qml/bitcoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <qml/models/activitylistmodel.h>
#include <qml/models/banlistmodel.h>
#include <qml/models/bitcoinaddress.h>
#include <qml/models/bumptransactionmodel.h>
#include <qml/models/chainmodel.h>
#include <qml/models/debuglogmodel.h>
#include <qml/models/networktraffictower.h>
Expand Down Expand Up @@ -364,6 +365,8 @@ int QmlGuiMain(int argc, char* argv[])
qmlRegisterUncreatableType<SendRecipient>("org.bitcoincore.qt", 1, 0, "SendRecipient", "");

#ifdef ENABLE_WALLET
qmlRegisterUncreatableType<BumpTransactionModel>("org.bitcoincore.qt", 1, 0, "BumpTransactionModel",
"BumpTransactionModel cannot be instantiated from QML");
qmlRegisterUncreatableType<WalletQmlModel>("org.bitcoincore.qt", 1, 0, "WalletQmlModel",
"WalletQmlModel cannot be instantiated from QML");
qmlRegisterUncreatableType<WalletQmlModelTransaction>("org.bitcoincore.qt", 1, 0, "WalletQmlModelTransaction",
Expand Down
2 changes: 2 additions & 0 deletions qml/bitcoin_qml.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<file>components/ExternalPopup.qml</file>
<file>components/FeeSelection.qml</file>
<file>components/MonospaceOutputView.qml</file>
<file>components/InfoBanner.qml</file>
<file>components/NetworkTrafficGraph.qml</file>
<file>components/NetworkIndicator.qml</file>
<file>components/OptionPopup.qml</file>
Expand Down Expand Up @@ -103,6 +104,7 @@
<file>pages/wallet/Send.qml</file>
<file>pages/wallet/SendResult.qml</file>
<file>pages/wallet/SendReview.qml</file>
<file>pages/wallet/SpeedUpOverlay.qml</file>
<file>pages/wallet/WalletBadge.qml</file>
<file>pages/wallet/WalletSelect.qml</file>
</qresource>
Expand Down
155 changes: 155 additions & 0 deletions qml/components/InfoBanner.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// Copyright (c) 2026 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import org.bitcoincore.qt 1.0

import "../controls"

Rectangle {
id: root

enum Layout {
Horizontal,
Vertical
}

property url iconSource: ""
property string title: ""
property string message: ""
property string primaryButtonText: ""
property string dismissButtonText: ""
property int bannerLayout: InfoBanner.Layout.Horizontal

signal primaryClicked()
signal dismissClicked()

radius: 10
color: Qt.rgba(Theme.color.blue.r, Theme.color.blue.g, Theme.color.blue.b, 0.2)
implicitHeight: contentLoader.item ? contentLoader.item.height + 60 : 60

Loader {
id: contentLoader
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.margins: 30
sourceComponent: root.bannerLayout === InfoBanner.Layout.Vertical
? verticalContent : horizontalContent
}

Component {
id: horizontalContent
RowLayout {
spacing: 15

Icon {
visible: root.iconSource != ""
source: root.iconSource
color: Theme.color.neutral7
size: 24
}

ColumnLayout {
Layout.fillWidth: true
spacing: 4

CoreText {
visible: root.title !== ""
text: root.title
font.pixelSize: 15
bold: true
color: Theme.color.neutral9
horizontalAlignment: Text.AlignLeft
Layout.fillWidth: true
}

CoreText {
visible: root.message !== ""
text: root.message
font.pixelSize: 13
color: Theme.color.neutral7
horizontalAlignment: Text.AlignLeft
Layout.fillWidth: true
wrap: true
}
}

OutlineButton {
objectName: root.objectName !== "" ? root.objectName + "DismissButton" : ""
visible: root.dismissButtonText !== ""
text: root.dismissButtonText
onClicked: root.dismissClicked()
}

ContinueButton {
objectName: root.objectName !== "" ? root.objectName + "PrimaryButton" : ""
visible: root.primaryButtonText !== ""
text: root.primaryButtonText
onClicked: root.primaryClicked()
}
}
}

Component {
id: verticalContent
ColumnLayout {
spacing: 15

Icon {
visible: root.iconSource != ""
source: root.iconSource
color: Theme.color.neutral7
size: 24
Layout.alignment: Qt.AlignHCenter
}

CoreText {
visible: root.title !== ""
text: root.title
font.pixelSize: 15
bold: true
color: Theme.color.neutral9
horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true
}

CoreText {
visible: root.message !== ""
text: root.message
font.pixelSize: 13
color: Theme.color.neutral7
horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true
Layout.bottomMargin: 10
wrap: true
}

RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: 15

OutlineButton {
objectName: root.objectName !== "" ? root.objectName + "DismissButton" : ""
visible: root.dismissButtonText !== ""
text: root.dismissButtonText
Layout.maximumWidth: 200
onClicked: root.dismissClicked()
}

ContinueButton {
objectName: root.objectName !== "" ? root.objectName + "PrimaryButton" : ""
visible: root.primaryButtonText !== ""
text: root.primaryButtonText
Layout.maximumWidth: 200
leftPadding: 30
rightPadding: 30
onClicked: root.primaryClicked()
}
}
}
}
}
53 changes: 51 additions & 2 deletions qml/models/activitylistmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
return tx->status;
case TypeRole:
return tx->type;
case TxidRole:
return tx->txid;
case CanBumpRole:
return m_wallet_model ? m_wallet_model->canBumpTransaction(tx->hash) : false;
case ReplacesTxidRole:
return tx->replacesTxid;
case ReplacedByTxidRole:
return tx->replacedByTxid;
default:
return QVariant();
}
Expand All @@ -90,9 +98,44 @@ QHash<int, QByteArray> ActivityListModel::roleNames() const
roles[LabelRole] = "label";
roles[StatusRole] = "status";
roles[TypeRole] = "type";
roles[TxidRole] = "txid";
roles[CanBumpRole] = "canBump";
roles[ReplacesTxidRole] = "replacesTxid";
roles[ReplacedByTxidRole] = "replacedByTxid";
return roles;
}

void ActivityListModel::reload()
{
beginResetModel();
m_transactions.clear();
refreshWallet();
endResetModel();
}

QVariantMap ActivityListModel::transactionDetails(const QString& txid) const
{
for (const auto& tx : m_transactions) {
if (tx->txid == txid) {
updateTransactionStatus(tx);
updateTransactionLabel(tx);
return {
{"txid", tx->txid},
{"canBump", m_wallet_model ? m_wallet_model->canBumpTransaction(tx->hash) : false},
{"replacedByTxid", tx->replacedByTxid},
{"amount", tx->prettyAmount()},
{"date", tx->dateTimeString()},
{"depth", tx->depth},
{"type", tx->type},
{"status", tx->status},
{"address", tx->address},
{"label", tx->label}
};
}
}
return {};
}

void ActivityListModel::refreshWallet()
{
if (m_wallet_model == nullptr) {
Expand Down Expand Up @@ -123,11 +166,17 @@ void ActivityListModel::updateTransaction(const uint256& hash, const interfaces:
// new transaction
interfaces::WalletTx wtx = m_wallet_model->getWalletTx(hash);
auto transactions = Transaction::fromWalletTx(wtx);
if (transactions.isEmpty()) {
return;
}
for (const auto& tx : transactions) {
tx->updateStatus(tx_status, num_blocks, block_time);
m_transactions.push_front(tx);
}
Q_EMIT dataChanged(this->index(0), this->index(m_transactions.size() - 1));
beginInsertRows(QModelIndex(), 0, transactions.size() - 1);
for (auto it = transactions.crbegin(); it != transactions.crend(); ++it) {
m_transactions.push_front(*it);
}
endInsertRows();
}
}

Expand Down
8 changes: 7 additions & 1 deletion qml/models/activitylistmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,15 @@ class ActivityListModel : public QAbstractListModel
DepthRole,
LabelRole,
StatusRole,
TypeRole
TypeRole,
TxidRole,
CanBumpRole,
ReplacesTxidRole,
ReplacedByTxidRole
};

Q_INVOKABLE void reload();
Q_INVOKABLE QVariantMap transactionDetails(const QString& txid) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
Expand Down
Loading
Loading