diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 33f4e26f24..e665ab0a03 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -404,7 +404,6 @@ QML_RES_QML = \
qml/controls/TextButton.qml \
qml/controls/Theme.qml \
qml/controls/ToggleButton.qml \
- qml/controls/utils.js \
qml/controls/ValueInput.qml \
qml/pages/initerrormessage.qml \
qml/pages/main.qml \
diff --git a/src/qml/bitcoin_qml.qrc b/src/qml/bitcoin_qml.qrc
index e64fab65e1..e9e65f1c25 100644
--- a/src/qml/bitcoin_qml.qrc
+++ b/src/qml/bitcoin_qml.qrc
@@ -44,7 +44,6 @@
controls/TextButton.qml
controls/Theme.qml
controls/ToggleButton.qml
- controls/utils.js
controls/ValueInput.qml
pages/initerrormessage.qml
pages/main.qml
diff --git a/src/qml/components/BlockClock.qml b/src/qml/components/BlockClock.qml
index 51b02afe51..f8f1f90a48 100644
--- a/src/qml/components/BlockClock.qml
+++ b/src/qml/components/BlockClock.qml
@@ -10,7 +10,6 @@ import Qt.labs.settings 1.0
import org.bitcoincore.qt 1.0
import "../controls"
-import "../controls/utils.js" as Utils
Item {
id: root
@@ -25,13 +24,12 @@ Item {
property alias headerSize: mainText.font.pixelSize
property alias subText: subText.text
property int headerSize: 32
- property bool connected: nodeModel.numOutboundPeers > 0
- property bool synced: nodeModel.verificationProgress > 0.999
- property string syncProgress: formatProgressPercentage(nodeModel.verificationProgress * 100)
+ property bool connected: nodeModel.connected
+ property bool synced: nodeModel.synced
+ property string syncProgress: nodeModel.formattedVerificationProgress
property bool paused: false
- property var syncState: Utils.formatRemainingSyncTime(nodeModel.remainingSyncTime)
- property string syncTime: syncState.text
- property bool estimating: syncState.estimating
+ property string syncTime: nodeModel.formattedRemainingSyncTime
+ property bool estimating: nodeModel.estimatingSyncTime
property bool faulted: nodeModel.faulted
activeFocusOnTab: true
@@ -53,7 +51,7 @@ Item {
verificationProgress: nodeModel.verificationProgress
paused: root.paused || root.faulted
connected: root.connected
- synced: nodeModel.verificationProgress > 0.999
+ synced: root.synced
backgroundColor: Theme.color.neutral2
timeTickColor: Theme.color.neutral5
confirmationColors: Theme.color.confirmationColors
@@ -242,17 +240,4 @@ Item {
}
}
]
-
-
- function formatProgressPercentage(progress) {
- if (progress >= 1) {
- return Math.round(progress) + "%"
- } else if (progress >= 0.1) {
- return progress.toFixed(1) + "%"
- } else if (progress >= 0.01) {
- return progress.toFixed(2) + "%"
- } else {
- return "0%"
- }
- }
}
diff --git a/src/qml/controls/utils.js b/src/qml/controls/utils.js
deleted file mode 100644
index bcbe9af35c..0000000000
--- a/src/qml/controls/utils.js
+++ /dev/null
@@ -1,66 +0,0 @@
-// 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.
-
-// utils.js
-
-function formatRemainingSyncTime(milliseconds) {
- var minutes = Math.floor(milliseconds / 60000);
- var seconds = Math.floor((milliseconds % 60000) / 1000);
- var weeks = Math.floor(minutes / 10080);
- minutes %= 10080;
- var days = Math.floor(minutes / 1440);
- minutes %= 1440;
- var hours = Math.floor(minutes / 60);
- minutes %= 60;
- var result = "";
- var estimatingStatus = false;
-
- if (weeks > 0) {
- return {
- text: "~" + weeks + (weeks === 1 ? " week" : " weeks") + " left",
- estimating: false
- };
- }
- if (days > 0) {
- return {
- text: "~" + days + (days === 1 ? " day" : " days") + " left",
- estimating: false
- };
- }
- if (hours >= 5) {
- return {
- text: "~" + hours + (hours === 1 ? " hour" : " hours") + " left",
- estimating: false
- };
- }
- if (hours > 0) {
- return {
- text: "~" + hours + "h " + minutes + "m" + " left",
- estimating: false
- };
- }
- if (minutes >= 5) {
- return {
- text: "~" + minutes + (minutes === 1 ? " minute" : " minutes") + " left",
- estimating: false
- };
- }
- if (minutes > 0) {
- return {
- text: "~" + minutes + "m " + seconds + "s" + " left",
- estimating: false
- };
- }
- if (seconds > 0) {
- return {
- text: "~" + seconds + (seconds === 1 ? " second" : " seconds") + " left",
- estimating: false
- };
- } else {
- return {
- text: "Estimating",
- estimating: true
- };
- }
-}
diff --git a/src/qml/models/nodemodel.cpp b/src/qml/models/nodemodel.cpp
index 521e5fa1c5..bc7d80f184 100644
--- a/src/qml/models/nodemodel.cpp
+++ b/src/qml/models/nodemodel.cpp
@@ -36,10 +36,32 @@ void NodeModel::setNumOutboundPeers(int new_num)
{
if (new_num != m_num_outbound_peers) {
m_num_outbound_peers = new_num;
+
+ bool new_connected = (m_num_outbound_peers > 0);
+ if (new_connected != m_connected) {
+ setConnected(new_connected);
+ }
+
Q_EMIT numOutboundPeersChanged();
}
}
+void NodeModel::setConnected(bool new_connected)
+{
+ if (new_connected != m_connected) {
+ m_connected = new_connected;
+ Q_EMIT connectedChanged();
+ }
+}
+
+void NodeModel::setEstimatingSyncTime(bool new_estimating)
+{
+ if (m_estimating_sync_time != new_estimating) {
+ m_estimating_sync_time = new_estimating;
+ Q_EMIT estimatingSyncTimeChanged();
+ }
+}
+
void NodeModel::setRemainingSyncTime(double new_progress)
{
int currentTime = QDateTime::currentDateTime().toMSecsSinceEpoch();
@@ -66,6 +88,7 @@ void NodeModel::setRemainingSyncTime(double new_progress)
}
if (remainingMSecs > 0 && m_block_process_time.count() % 1000 == 0) {
m_remaining_sync_time = remainingMSecs;
+ setFormattedRemainingSyncTime(m_remaining_sync_time);
Q_EMIT remainingSyncTimeChanged();
}
@@ -73,18 +96,110 @@ void NodeModel::setRemainingSyncTime(double new_progress)
if (m_block_process_time.count() > MAX_SAMPLES) {
m_block_process_time.remove(1, m_block_process_time.count() - 1);
}
+ } else {
+ m_remaining_sync_time = 0;
+ setFormattedRemainingSyncTime(m_remaining_sync_time);
+
+ Q_EMIT remainingSyncTimeChanged();
+ }
+}
+
+void NodeModel::setFormattedRemainingSyncTime(int new_time)
+{
+ int minutes = new_time / 60000;
+ int seconds = (new_time % 60000) / 1000;
+
+ int weeks = minutes / 10080;
+ minutes %= 10080;
+
+ int days = minutes / 1440;
+ minutes %= 1440;
+
+ int hours = minutes / 60;
+ minutes %= 60;
+
+ if (weeks > 0) {
+ m_formatted_remaining_sync_time = QObject::tr("~%1 %2 left")
+ .arg(weeks)
+ .arg(weeks == 1 ? QObject::tr("week") : QObject::tr("weeks"));
+ setEstimatingSyncTime(false);
+ } else if (days > 0) {
+ m_formatted_remaining_sync_time = QObject::tr("~%1 %2 left")
+ .arg(days)
+ .arg(days == 1 ? QObject::tr("day") : QObject::tr("days"));
+ setEstimatingSyncTime(false);
+ } else if (hours >= 5) {
+ m_formatted_remaining_sync_time = QObject::tr("~%1 %2 left")
+ .arg(hours)
+ .arg(hours == 1 ? QObject::tr("hour") : QObject::tr("hours"));
+ setEstimatingSyncTime(false);
+ } else if (hours > 0) {
+ m_formatted_remaining_sync_time = QObject::tr("~%1h %2m left")
+ .arg(hours)
+ .arg(minutes);
+ setEstimatingSyncTime(false);
+ } else if (minutes >= 5) {
+ m_formatted_remaining_sync_time = QObject::tr("~%1 %2 left")
+ .arg(minutes)
+ .arg(minutes == 1 ? QObject::tr("minute") : QObject::tr("minutes"));
+ setEstimatingSyncTime(false);
+ } else if (minutes > 0) {
+ m_formatted_remaining_sync_time = QObject::tr("~%1m %2s left")
+ .arg(minutes)
+ .arg(seconds);
+ setEstimatingSyncTime(false);
+ } else if (seconds > 0) {
+ m_formatted_remaining_sync_time = QObject::tr("~%1 %2 left")
+ .arg(seconds)
+ .arg(seconds == 1 ? QObject::tr("second") : QObject::tr("seconds"));
+ setEstimatingSyncTime(false);
+ } else {
+ if (m_synced) {
+ setEstimatingSyncTime(false);
+ } else {
+ m_formatted_remaining_sync_time = QObject::tr("Estimating");
+ setEstimatingSyncTime(true);
+ }
}
}
+
void NodeModel::setVerificationProgress(double new_progress)
{
if (new_progress != m_verification_progress) {
setRemainingSyncTime(new_progress);
+ setFormattedVerificationProgress(new_progress);
+
+ if (new_progress >= 0.999) {
+ setSynced(true);
+ }
m_verification_progress = new_progress;
Q_EMIT verificationProgressChanged();
}
}
+void NodeModel::setFormattedVerificationProgress(double new_progress)
+{
+ double percentage = new_progress * 100.00;
+
+ if (percentage >= 99.99) {
+ m_formatted_verification_progress = QStringLiteral("100%");
+ } else if (percentage == 0.0) {
+ m_formatted_verification_progress = QStringLiteral("0%");
+ } else {
+ int decimal_places = (percentage >= 10.0) ? 1 : 2;
+ m_formatted_verification_progress = QString::number(percentage, 'f', decimal_places) + '%';
+ }
+}
+
+void NodeModel::setSynced(bool new_synced)
+{
+ if (m_synced != new_synced) {
+ m_synced = new_synced;
+ Q_EMIT syncedChanged();
+ }
+}
+
void NodeModel::setPause(bool new_pause)
{
if(m_pause != new_pause) {
diff --git a/src/qml/models/nodemodel.h b/src/qml/models/nodemodel.h
index a17f9b0833..8a88a38fea 100644
--- a/src/qml/models/nodemodel.h
+++ b/src/qml/models/nodemodel.h
@@ -30,8 +30,13 @@ class NodeModel : public QObject
Q_PROPERTY(QString fullClientVersion READ fullClientVersion CONSTANT)
Q_PROPERTY(int numOutboundPeers READ numOutboundPeers NOTIFY numOutboundPeersChanged)
Q_PROPERTY(int maxNumOutboundPeers READ maxNumOutboundPeers CONSTANT)
+ Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged)
Q_PROPERTY(int remainingSyncTime READ remainingSyncTime NOTIFY remainingSyncTimeChanged)
+ Q_PROPERTY(QString formattedRemainingSyncTime READ formattedRemainingSyncTime NOTIFY remainingSyncTimeChanged)
+ Q_PROPERTY(bool estimatingSyncTime READ estimatingSyncTime NOTIFY estimatingSyncTimeChanged)
Q_PROPERTY(double verificationProgress READ verificationProgress NOTIFY verificationProgressChanged)
+ Q_PROPERTY(QString formattedVerificationProgress READ formattedVerificationProgress NOTIFY verificationProgressChanged)
+ Q_PROPERTY(bool synced READ synced NOTIFY syncedChanged)
Q_PROPERTY(bool pause READ pause WRITE setPause NOTIFY pauseChanged)
Q_PROPERTY(bool faulted READ errorState WRITE setErrorState NOTIFY errorStateChanged)
@@ -44,10 +49,15 @@ class NodeModel : public QObject
int numOutboundPeers() const { return m_num_outbound_peers; }
void setNumOutboundPeers(int new_num);
int maxNumOutboundPeers() const { return m_max_num_outbound_peers; }
+ bool connected() const { return m_connected; }
int remainingSyncTime() const { return m_remaining_sync_time; }
void setRemainingSyncTime(double new_progress);
+ QString formattedRemainingSyncTime() const { return m_formatted_remaining_sync_time; }
+ bool estimatingSyncTime() const { return m_estimating_sync_time; }
double verificationProgress() const { return m_verification_progress; }
void setVerificationProgress(double new_progress);
+ QString formattedVerificationProgress() const { return m_formatted_verification_progress; }
+ bool synced() const { return m_synced; }
bool pause() const { return m_pause; }
void setPause(bool new_pause);
bool errorState() const { return m_faulted; }
@@ -68,10 +78,13 @@ public Q_SLOTS:
Q_SIGNALS:
void blockTipHeightChanged();
void numOutboundPeersChanged();
+ void connectedChanged();
void remainingSyncTimeChanged();
+ void estimatingSyncTimeChanged();
void requestedInitialize();
void requestedShutdown();
void verificationProgressChanged();
+ void syncedChanged();
void pauseChanged(bool new_pause);
void errorStateChanged(bool new_error_state);
@@ -82,12 +95,23 @@ public Q_SLOTS:
void timerEvent(QTimerEvent* event) override;
private:
+ void setConnected(bool new_connected);
+ void setEstimatingSyncTime(bool new_estimating);
+ void setFormattedRemainingSyncTime(int new_time);
+ void setFormattedVerificationProgress(double new_progress);
+ void setSynced(bool new_synced);
+
// Properties that are exposed to QML.
int m_block_tip_height{0};
int m_num_outbound_peers{0};
+ bool m_connected{false};
static constexpr int m_max_num_outbound_peers{MAX_OUTBOUND_FULL_RELAY_CONNECTIONS + MAX_BLOCK_RELAY_ONLY_CONNECTIONS};
int m_remaining_sync_time{0};
+ QString m_formatted_remaining_sync_time;
+ bool m_estimating_sync_time{false};
double m_verification_progress{0.0};
+ QString m_formatted_verification_progress;
+ bool m_synced{false};
bool m_pause{false};
bool m_faulted{false};
diff --git a/src/qml/pages/wallet/DesktopWallets.qml b/src/qml/pages/wallet/DesktopWallets.qml
index 59a7ac15e4..a050d5e7bc 100644
--- a/src/qml/pages/wallet/DesktopWallets.qml
+++ b/src/qml/pages/wallet/DesktopWallets.qml
@@ -9,7 +9,6 @@ import QtQuick.Layouts 1.15
import org.bitcoincore.qt 1.0
import "../../controls"
-import "../../controls/utils.js" as Utils
import "../../components"
import "../node"
@@ -77,10 +76,10 @@ Page {
Tooltip {
id: blockClockTooltip
- property var syncState: Utils.formatRemainingSyncTime(nodeModel.remainingSyncTime)
- property bool synced: nodeModel.verificationProgress > 0.9999
+ property string remainingSyncTime: nodeModel.formattedRemainingSyncTime
+ property bool synced: nodeModel.synced
property bool paused: nodeModel.pause
- property bool connected: nodeModel.numOutboundPeers > 0
+ property bool connected: nodeModel.connected
anchors.top: blockClockTabButton.bottom
anchors.topMargin: -5
@@ -93,7 +92,7 @@ Page {
} else if (connected && synced) {
qsTr("Blocktime\n" + Number(nodeModel.blockTipHeight).toLocaleString(Qt.locale(), 'f', 0))
} else if (connected){
- qsTr("Downloading blocks\n" + syncState.text)
+ qsTr("Downloading blocks\n" + blockClockTooltip.remainingSyncTime)
} else {
qsTr("Connecting")
}