Skip to content

Commit

Permalink
qml: Add PeerDetails page
Browse files Browse the repository at this point in the history
  • Loading branch information
johnny9 committed Jan 29, 2024
1 parent 67150ef commit 845a878
Show file tree
Hide file tree
Showing 11 changed files with 349 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/Makefile.qt.include
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ QT_MOC_CPP = \
qml/models/moc_networktraffictower.cpp \
qml/models/moc_nodemodel.cpp \
qml/models/moc_options_model.cpp \
qml/models/moc_peerdetailsmodel.cpp \
qml/models/moc_peerlistsortproxy.cpp \
qml/moc_appmode.cpp \
qt/moc_addressbookpage.cpp \
Expand Down Expand Up @@ -120,6 +121,7 @@ BITCOIN_QT_H = \
qml/models/networktraffictower.h \
qml/models/nodemodel.h \
qml/models/options_model.h \
qml/models/peerdetailsmodel.h \
qml/models/peerlistsortproxy.h \
qml/appmode.h \
qml/bitcoin.h \
Expand Down Expand Up @@ -307,6 +309,7 @@ BITCOIN_QML_BASE_CPP = \
qml/models/networktraffictower.cpp \
qml/models/nodemodel.cpp \
qml/models/options_model.cpp \
qml/models/peerdetailsmodel.cpp \
qml/models/peerlistsortproxy.cpp \
qml/imageprovider.cpp \
qml/util.cpp
Expand Down Expand Up @@ -371,9 +374,11 @@ QML_RES_QML = \
qml/controls/OptionButton.qml \
qml/controls/OptionSwitch.qml \
qml/controls/OutlineButton.qml \
qml/controls/PeerRadioButton.qml \
qml/controls/ProgressIndicator.qml \
qml/controls/qmldir \
qml/controls/Setting.qml \
qml/controls/SettingsButton.qml \
qml/controls/TextButton.qml \
qml/controls/Theme.qml \
qml/controls/ToggleButton.qml \
Expand All @@ -384,6 +389,7 @@ QML_RES_QML = \
qml/pages/node/NodeRunner.qml \
qml/pages/node/NodeSettings.qml \
qml/pages/node/Peers.qml \
qml/pages/node/PeerDetails.qml \
qml/pages/node/Shutdown.qml \
qml/pages/onboarding/OnboardingBlockclock.qml \
qml/pages/onboarding/OnboardingConnection.qml \
Expand Down
4 changes: 4 additions & 0 deletions src/qml/bitcoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "qml/models/peerdetailsmodel.h"
#include <qml/bitcoin.h>

#include <chainparams.h>
Expand All @@ -24,6 +25,7 @@
#include <qml/models/networktraffictower.h>
#include <qml/models/nodemodel.h>
#include <qml/models/options_model.h>
#include <qml/models/peerdetailsmodel.h>
#include <qml/models/peerlistsortproxy.h>
#include <qml/imageprovider.h>
#include <qml/util.h>
Expand Down Expand Up @@ -292,6 +294,8 @@ int QmlGuiMain(int argc, char* argv[])
qmlRegisterSingletonInstance<AppMode>("org.bitcoincore.qt", 1, 0, "AppMode", &app_mode);
qmlRegisterType<BlockClockDial>("org.bitcoincore.qt", 1, 0, "BlockClockDial");
qmlRegisterType<LineGraph>("org.bitcoincore.qt", 1, 0, "LineGraph");
qmlRegisterUncreatableType<PeerDetailsModel>("org.bitcoincore.qt", 1, 0, "PeerDetailsModel", "");


engine.load(QUrl(QStringLiteral("qrc:///qml/pages/main.qml")));
if (engine.rootObjects().isEmpty()) {
Expand Down
3 changes: 3 additions & 0 deletions src/qml/bitcoin_qml.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@
<file>controls/OptionButton.qml</file>
<file>controls/OptionSwitch.qml</file>
<file>controls/OutlineButton.qml</file>
<file>controls/PeerRadioButton.qml</file>
<file>controls/ProgressIndicator.qml</file>
<file>controls/qmldir</file>
<file>controls/Setting.qml</file>
<file>controls/SettingsButton.qml</file>
<file>controls/TextButton.qml</file>
<file>controls/Theme.qml</file>
<file>controls/ToggleButton.qml</file>
Expand All @@ -45,6 +47,7 @@
<file>pages/node/NodeRunner.qml</file>
<file>pages/node/NodeSettings.qml</file>
<file>pages/node/Peers.qml</file>
<file>pages/node/PeerDetails.qml</file>
<file>pages/node/Shutdown.qml</file>
<file>pages/onboarding/OnboardingBlockclock.qml</file>
<file>pages/onboarding/OnboardingConnection.qml</file>
Expand Down
30 changes: 30 additions & 0 deletions src/qml/controls/PeerRadioButton.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) 2022 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 org.bitcoincore.qt 1.0

RadioButton {
id: control

indicator: Rectangle {
implicitWidth: 14
implicitHeight: 14
x: control.leftPadding
y: parent.height / 2 - height / 2
radius: 7
border.color: Theme.color.white
color: control.checked ? Theme.color.white : Theme.color.background
}

contentItem: Text {
text: control.text
font: control.font
opacity: enabled ? 1.0 : 0.3
color: Theme.color.white
verticalAlignment: Text.AlignVCenter
leftPadding: control.indicator.width + control.spacing
}
}
45 changes: 45 additions & 0 deletions src/qml/controls/SettingsButton.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) 2022 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 org.bitcoincore.qt 1.0

Button {
id: root
hoverEnabled: AppMode.isDesktop
contentItem: CoreText {
text: parent.text
bold: true
font.pixelSize: 15
}
background: Rectangle {
id: bg
implicitHeight: 46
implicitWidth: 300
color: Theme.color.background
border.color: Theme.color.neutral4
border.width: 1
radius: 5

states: [
State {
name: "PRESSED"; when: root.pressed
PropertyChanges { target: bg; color: Theme.color.neutral5 }
},
State {
name: "HOVER"; when: root.hovered
PropertyChanges { target: bg; color: Theme.color.neutral2 }
}
]

Behavior on color {
ColorAnimation { duration: 150 }
}

FocusBorder {
visible: root.visualFocus
}
}
}
43 changes: 43 additions & 0 deletions src/qml/models/peerdetailsmodel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) 2024 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <qml/models/peerdetailsmodel.h>

PeerDetailsModel::PeerDetailsModel(CNodeCombinedStats* nodeStats, const QModelIndex& index, PeerTableModel* parent)
: m_combinedStats{nodeStats}
, m_model{parent}
{
m_row = index.row();
connect(parent, &PeerTableModel::rowsRemoved, this, &PeerDetailsModel::onModelRowsRemoved);
connect(parent, &PeerTableModel::dataChanged, this, &PeerDetailsModel::onModelDataChanged);
}

void PeerDetailsModel::onModelRowsRemoved(const QModelIndex& parent, int first, int last)
{
for (int row = first; row <= last; ++row) {
QModelIndex index = m_model->index(row, 0, parent);
int nodeIdInRow = m_model->data(index, PeerTableModel::NetNodeId).toInt();
if (nodeIdInRow == this->nodeId()) {
Q_EMIT disconnected();
break;
}
}
}

void PeerDetailsModel::onModelDataChanged(const QModelIndex& /* top_left */, const QModelIndex& /* bottom_right */)
{
if (m_model->data(m_model->index(m_row, 0), PeerTableModel::NetNodeId).isNull() ||
m_model->data(m_model->index(m_row, 0), PeerTableModel::NetNodeId).toInt() != nodeId()) {
// This peer has been removed from the model
Q_EMIT disconnected();
return;
}

m_combinedStats = m_model->data(m_model->index(m_row, 0), PeerTableModel::StatsRole).value<CNodeCombinedStats*>();

// Only update when all information is available
if (m_combinedStats && m_combinedStats->fNodeStateStatsAvailable) {
Q_EMIT dataChanged();
}
}
94 changes: 94 additions & 0 deletions src/qml/models/peerdetailsmodel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright (c) 2024 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_QML_MODELS_PEER_DETAILS_MODEL_H
#define BITCOIN_QML_MODELS_PEER_DETAILS_MODEL_H

#include "qobjectdefs.h"
#include <QObject>

#include <qml/models/peerlistsortproxy.h>
#include <qt/guiutil.h>
#include <qt/peertablemodel.h>
#include <qt/rpcconsole.h>
#include <util/time.h>

class PeerDetailsModel : public QObject
{
Q_OBJECT
Q_PROPERTY(int nodeId READ nodeId NOTIFY dataChanged)
Q_PROPERTY(QString address READ address NOTIFY dataChanged)
Q_PROPERTY(QString addressLocal READ addressLocal NOTIFY dataChanged)
Q_PROPERTY(QString type READ type NOTIFY dataChanged)
Q_PROPERTY(QString version READ version NOTIFY dataChanged)
Q_PROPERTY(QString userAgent READ userAgent NOTIFY dataChanged)
Q_PROPERTY(QString services READ services NOTIFY dataChanged)
Q_PROPERTY(bool transactionRelay READ transactionRelay NOTIFY dataChanged)
Q_PROPERTY(bool addressRelay READ addressRelay NOTIFY dataChanged)
Q_PROPERTY(QString startingHeight READ startingHeight NOTIFY dataChanged)
Q_PROPERTY(QString syncedHeaders READ syncedHeaders NOTIFY dataChanged)
Q_PROPERTY(QString syncedBlocks READ syncedBlocks NOTIFY dataChanged)
Q_PROPERTY(QString direction READ direction NOTIFY dataChanged)
Q_PROPERTY(QString lastSend READ lastSend NOTIFY dataChanged)
Q_PROPERTY(QString lastReceived READ lastReceived NOTIFY dataChanged)
Q_PROPERTY(QString bytesSent READ bytesSent NOTIFY dataChanged)
Q_PROPERTY(QString bytesReceived READ bytesReceived NOTIFY dataChanged)
Q_PROPERTY(QString pingTime READ pingTime NOTIFY dataChanged)
Q_PROPERTY(QString pingWait READ pingWait NOTIFY dataChanged)
Q_PROPERTY(QString pingMin READ pingMin NOTIFY dataChanged)
Q_PROPERTY(QString timeOffset READ timeOffset NOTIFY dataChanged)
Q_PROPERTY(QString mappedAS READ mappedAS NOTIFY dataChanged)
Q_PROPERTY(QString permission READ permission NOTIFY dataChanged)

public:
explicit PeerDetailsModel(CNodeCombinedStats* nodeStats, const QModelIndex& index, PeerTableModel* model);

int nodeId() const { return m_combinedStats->nodeStats.nodeid; }
QString address() const { return QString::fromStdString(m_combinedStats->nodeStats.m_addr_name); }
QString addressLocal() const { return QString::fromStdString(m_combinedStats->nodeStats.addrLocal); }
QString type() const { return GUIUtil::ConnectionTypeToQString(m_combinedStats->nodeStats.m_conn_type, /*prepend_direction=*/true); }
QString version() const { return QString::number(m_combinedStats->nodeStats.nVersion); }
QString userAgent() const { return QString::fromStdString(m_combinedStats->nodeStats.cleanSubVer); }
QString services() const { return GUIUtil::formatServicesStr(m_combinedStats->nodeStateStats.their_services); }
bool transactionRelay() const { return m_combinedStats->nodeStateStats.m_relay_txs; }
bool addressRelay() const { return m_combinedStats->nodeStateStats.m_addr_relay_enabled; }
QString startingHeight() const { return QString::number(m_combinedStats->nodeStats.m_starting_height); }
QString syncedHeaders() const { return QString::number(m_combinedStats->nodeStateStats.nSyncHeight); }
QString syncedBlocks() const { return QString::number(m_combinedStats->nodeStateStats.nCommonHeight); }
QString direction() const { return QString::fromStdString(m_combinedStats->nodeStats.fInbound ? "Inbound" : "Outbound"); }
QString lastSend() const { return GUIUtil::formatDurationStr(GetTime<std::chrono::seconds>() - m_combinedStats->nodeStats.m_last_send); }
QString lastReceived() const { return GUIUtil::formatDurationStr(GetTime<std::chrono::seconds>() - m_combinedStats->nodeStats.m_last_recv); }
QString bytesSent() const { return GUIUtil::formatBytes(m_combinedStats->nodeStats.nSendBytes); }
QString bytesReceived() const { return GUIUtil::formatBytes(m_combinedStats->nodeStats.nRecvBytes); }
QString pingTime() const { return GUIUtil::formatPingTime(m_combinedStats->nodeStats.m_last_ping_time); }
QString pingMin() const { return GUIUtil::formatPingTime(m_combinedStats->nodeStats.m_min_ping_time); }
QString pingWait() const { return GUIUtil::formatPingTime(m_combinedStats->nodeStateStats.m_ping_wait); }
QString timeOffset() const { return GUIUtil::formatTimeOffset(m_combinedStats->nodeStats.nTimeOffset); }
QString mappedAS() const { return m_combinedStats->nodeStats.m_mapped_as != 0 ? QString::number(m_combinedStats->nodeStats.m_mapped_as) : tr("N/A"); }
QString permission() const {
if (m_combinedStats->nodeStats.m_permission_flags == NetPermissionFlags::None) {
return tr("N/A");
}
QStringList permissions;
for (const auto& permission : NetPermissions::ToStrings(m_combinedStats->nodeStats.m_permission_flags)) {
permissions.append(QString::fromStdString(permission));
}
return permissions.join(" & ");
}

Q_SIGNALS:
void dataChanged();
void disconnected();

private Q_SLOTS:
void onModelRowsRemoved(const QModelIndex& parent, int first, int last);
void onModelDataChanged(const QModelIndex& top_left, const QModelIndex& bottom_right);

private:
int m_row;
CNodeCombinedStats* m_combinedStats;
PeerTableModel* m_model;
};

#endif // BITCOIN_QML_MODELS_PEER_DETAILS_MODEL_H
6 changes: 6 additions & 0 deletions src/qml/models/peerlistsortproxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <qml/models/peerlistsortproxy.h>
#include <qml/models/peerdetailsmodel.h>
#include <qt/peertablemodel.h>

PeerListSortProxy::PeerListSortProxy(QObject* parent)
Expand All @@ -23,6 +24,7 @@ QHash<int, QByteArray> PeerListSortProxy::roleNames() const
roles[PeerTableModel::Sent] = "sent";
roles[PeerTableModel::Received] = "received";
roles[PeerTableModel::Subversion] = "subversion";
roles[PeerTableModel::StatsRole] = "stats";
return roles;
}

Expand All @@ -40,6 +42,10 @@ int PeerListSortProxy::RoleNameToIndex(const QString & name) const
QVariant PeerListSortProxy::data(const QModelIndex& index, int role) const
{
if (role == PeerTableModel::StatsRole) {
auto stats = PeerTableSortProxy::data(index, role);
auto details = new PeerDetailsModel(stats.value<CNodeCombinedStats*>(), index, qobject_cast<PeerTableModel*>(sourceModel()));
return QVariant::fromValue(details);
} else if (role == PeerTableModel::NetNodeId) {
return PeerTableSortProxy::data(index, role);
}

Expand Down
11 changes: 11 additions & 0 deletions src/qml/pages/node/NodeSettings.qml
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,17 @@ Item {
nodeSettingsView.pop()
peerTableModel.stopAutoRefresh();
}
onPeerSelected: (peerDetails) => {
nodeSettingsView.push(peer_details, {"details": peerDetails})
}
}
}
Component {
id: peer_details
PeerDetails {
onBackClicked: {
nodeSettingsView.pop()
}
}
}
Component {
Expand Down

0 comments on commit 845a878

Please sign in to comment.