diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index a61043608d..7cad78ff37 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -337,11 +337,14 @@ QML_RES_ICONS = \
qml/res/icons/caret-left.png \
qml/res/icons/caret-right.png \
qml/res/icons/check.png \
+ qml/res/icons/circle-file.png \
+ qml/res/icons/circle-green-check.png \
qml/res/icons/cross.png \
qml/res/icons/error.png \
qml/res/icons/export.png \
qml/res/icons/gear.png \
qml/res/icons/gear-outline.png \
+ qml/res/icons/green-check.png \
qml/res/icons/hidden.png \
qml/res/icons/info.png \
qml/res/icons/network-dark.png \
@@ -372,6 +375,7 @@ QML_RES_QML = \
qml/components/NetworkIndicator.qml \
qml/components/ProxySettings.qml \
qml/components/Separator.qml \
+ qml/components/SnapshotSettings.qml \
qml/components/StorageLocations.qml \
qml/components/StorageOptions.qml \
qml/components/StorageSettings.qml \
@@ -425,6 +429,7 @@ QML_RES_QML = \
qml/pages/settings/SettingsDeveloper.qml \
qml/pages/settings/SettingsDisplay.qml \
qml/pages/settings/SettingsProxy.qml \
+ qml/pages/settings/SettingsSnapshot.qml \
qml/pages/settings/SettingsStorage.qml \
qml/pages/settings/SettingsTheme.qml \
qml/pages/wallet/AddWallet.qml \
diff --git a/src/qml/bitcoin_qml.qrc b/src/qml/bitcoin_qml.qrc
index ec48e0c74d..167bfd2f91 100644
--- a/src/qml/bitcoin_qml.qrc
+++ b/src/qml/bitcoin_qml.qrc
@@ -15,6 +15,7 @@
components/ProxySettings.qml
components/StorageLocations.qml
components/Separator.qml
+ components/SnapshotSettings.qml
components/StorageOptions.qml
components/StorageSettings.qml
components/ThemeSettings.qml
@@ -25,6 +26,7 @@
controls/CoreTextField.qml
controls/ExternalLink.qml
controls/FocusBorder.qml
+ controls/GreenCheckIcon.qml
controls/Header.qml
controls/Icon.qml
controls/InformationPage.qml
@@ -66,6 +68,7 @@
pages/settings/SettingsDeveloper.qml
pages/settings/SettingsDisplay.qml
pages/settings/SettingsProxy.qml
+ pages/settings/SettingsSnapshot.qml
pages/settings/SettingsStorage.qml
pages/settings/SettingsTheme.qml
pages/wallet/AddWallet.qml
@@ -91,11 +94,14 @@
res/icons/caret-left.png
res/icons/caret-right.png
res/icons/check.png
+ res/icons/circle-file.png
+ res/icons/circle-green-check.png
res/icons/cross.png
res/icons/error.png
res/icons/export.png
res/icons/gear.png
res/icons/gear-outline.png
+ res/icons/green-check.png
res/icons/hidden.png
res/icons/info.png
res/icons/minus.png
diff --git a/src/qml/components/ConnectionSettings.qml b/src/qml/components/ConnectionSettings.qml
index 90625a7def..fea589685d 100644
--- a/src/qml/components/ConnectionSettings.qml
+++ b/src/qml/components/ConnectionSettings.qml
@@ -8,7 +8,36 @@ import QtQuick.Layouts 1.15
import "../controls"
ColumnLayout {
+ property bool snapshotImported: false
+ function setSnapshotImported(imported) {
+ snapshotImported = imported
+ }
spacing: 4
+ Setting {
+ id: gotoSnapshot
+ Layout.fillWidth: true
+ header: qsTr("Load snapshot")
+ description: qsTr("Instant use with background sync")
+ actionItem: Item {
+ width: 26
+ height: 26
+ CaretRightIcon {
+ anchors.centerIn: parent
+ visible: !snapshotImported
+ color: gotoSnapshot.stateColor
+ }
+ GreenCheckIcon {
+ anchors.centerIn: parent
+ visible: snapshotImported
+ color: Theme.color.transparent
+ }
+ }
+ onClicked: {
+ connectionSwipe.incrementCurrentIndex()
+ connectionSwipe.incrementCurrentIndex()
+ }
+ }
+ Separator { Layout.fillWidth: true }
Setting {
Layout.fillWidth: true
header: qsTr("Enable listening")
diff --git a/src/qml/components/SnapshotSettings.qml b/src/qml/components/SnapshotSettings.qml
new file mode 100644
index 0000000000..ebac415b60
--- /dev/null
+++ b/src/qml/components/SnapshotSettings.qml
@@ -0,0 +1,204 @@
+// Copyright (c) 2023-present 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 "../controls"
+
+ColumnLayout {
+ signal snapshotImportCompleted()
+ property int snapshotVerificationCycles: 0
+ property real snapshotVerificationProgress: 0
+ property bool snapshotVerified: false
+
+ id: columnLayout
+ width: Math.min(parent.width, 450)
+ anchors.horizontalCenter: parent.horizontalCenter
+
+
+ Timer {
+ id: snapshotSimulationTimer
+ interval: 50 // Update every 50ms
+ running: false
+ repeat: true
+ onTriggered: {
+ if (snapshotVerificationProgress < 1) {
+ snapshotVerificationProgress += 0.01
+ } else {
+ snapshotVerificationCycles++
+ if (snapshotVerificationCycles < 1) {
+ snapshotVerificationProgress = 0
+ } else {
+ running = false
+ snapshotVerified = true
+ settingsStack.currentIndex = 2
+ }
+ }
+ }
+ }
+
+ StackLayout {
+ id: settingsStack
+ currentIndex: 0
+
+ ColumnLayout {
+ Layout.alignment: Qt.AlignHCenter
+ Layout.preferredWidth: Math.min(parent.width, 450)
+
+ Image {
+ Layout.alignment: Qt.AlignCenter
+ source: "image://images/circle-file"
+
+ sourceSize.width: 200
+ sourceSize.height: 200
+ }
+
+ Header {
+ Layout.fillWidth: true
+ Layout.topMargin: 20
+ headerBold: true
+ header: qsTr("Load snapshot")
+ descriptionBold: false
+ descriptionColor: Theme.color.neutral6
+ descriptionSize: 17
+ descriptionLineHeight: 1.1
+ description: qsTr("You can start using the application more quickly by loading a recent transaction snapshot." +
+ " It will be automatically verified in the background.")
+ }
+
+ ContinueButton {
+ Layout.preferredWidth: Math.min(300, columnLayout.width - 2 * Layout.leftMargin)
+ Layout.topMargin: 40
+ Layout.leftMargin: 20
+ Layout.rightMargin: Layout.leftMargin
+ Layout.bottomMargin: 20
+ Layout.alignment: Qt.AlignCenter
+ text: qsTr("Choose snapshot file")
+ onClicked: {
+ settingsStack.currentIndex = 1
+ snapshotSimulationTimer.start()
+ }
+ }
+ }
+
+ ColumnLayout {
+ Layout.alignment: Qt.AlignHCenter
+ Layout.preferredWidth: Math.min(parent.width, 450)
+
+ Image {
+ Layout.alignment: Qt.AlignCenter
+ source: "image://images/circle-file"
+
+ sourceSize.width: 200
+ sourceSize.height: 200
+ }
+
+ Header {
+ Layout.fillWidth: true
+ Layout.topMargin: 20
+ Layout.leftMargin: 20
+ Layout.rightMargin: 20
+ header: qsTr("Loading Snapshot")
+ }
+
+ ProgressIndicator {
+ id: progressIndicator
+ Layout.topMargin: 20
+ width: 200
+ height: 20
+ progress: snapshotVerificationProgress
+ Layout.alignment: Qt.AlignCenter
+ progressColor: Theme.color.blue
+ }
+ }
+
+ ColumnLayout {
+ id: loadedSnapshotColumn
+ Layout.alignment: Qt.AlignHCenter
+ Layout.preferredWidth: Math.min(parent.width, 450)
+
+ Image {
+ Layout.alignment: Qt.AlignCenter
+ source: "image://images/circle-green-check"
+
+ sourceSize.width: 60
+ sourceSize.height: 60
+ }
+
+ Header {
+ Layout.fillWidth: true
+ Layout.topMargin: 20
+ headerBold: true
+ header: qsTr("Snapshot Loaded")
+ descriptionBold: false
+ descriptionColor: Theme.color.neutral6
+ descriptionSize: 17
+ descriptionLineHeight: 1.1
+ description: qsTr("It contains transactions up to January 12, 2024. Newer transactions still need to be downloaded." +
+ " The data will be verified in the background.")
+ }
+
+ ContinueButton {
+ Layout.preferredWidth: Math.min(300, columnLayout.width - 2 * Layout.leftMargin)
+ Layout.topMargin: 40
+ Layout.alignment: Qt.AlignCenter
+ text: qsTr("Done")
+ onClicked: {
+ snapshotImportCompleted()
+ connectionSwipe.decrementCurrentIndex()
+ connectionSwipe.decrementCurrentIndex()
+ }
+ }
+
+ Setting {
+ id: viewDetails
+ Layout.alignment: Qt.AlignCenter
+ header: qsTr("View details")
+ actionItem: CaretRightIcon {
+ id: caretIcon
+ color: viewDetails.stateColor
+ rotation: viewDetails.expanded ? 90 : 0
+ Behavior on rotation { NumberAnimation { duration: 200 } }
+ }
+
+ property bool expanded: false
+
+ onClicked: {
+ expanded = !expanded
+ }
+ }
+
+ ColumnLayout {
+ id: detailsContent
+ visible: viewDetails.expanded
+ Layout.preferredWidth: Math.min(300, parent.width - 2 * Layout.leftMargin)
+ Layout.alignment: Qt.AlignCenter
+ Layout.leftMargin: 80
+ Layout.rightMargin: 80
+ Layout.topMargin: 10
+ spacing: 10
+ // TODO: make sure the block height number aligns right
+ RowLayout {
+ CoreText {
+ text: qsTr("Block Height:")
+ Layout.alignment: Qt.AlignLeft
+ font.pixelSize: 14
+ }
+ CoreText {
+ text: qsTr("200,000")
+ Layout.alignment: Qt.AlignRight
+ font.pixelSize: 14
+ }
+ }
+ Separator { Layout.fillWidth: true }
+ CoreText {
+ text: qsTr("Hash: 0x1234567890abcdef...")
+ font.pixelSize: 14
+ }
+ }
+ }
+ }
+}
diff --git a/src/qml/controls/GreenCheckIcon.qml b/src/qml/controls/GreenCheckIcon.qml
new file mode 100644
index 0000000000..cf6ccec67c
--- /dev/null
+++ b/src/qml/controls/GreenCheckIcon.qml
@@ -0,0 +1,11 @@
+// Copyright (c) 2023 - present 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
+
+Icon {
+ source: "image://images/green-check"
+ size: 26
+}
diff --git a/src/qml/controls/Header.qml b/src/qml/controls/Header.qml
index f3c4c0c3e3..ece49234d2 100644
--- a/src/qml/controls/Header.qml
+++ b/src/qml/controls/Header.qml
@@ -25,6 +25,7 @@ ColumnLayout {
property int subtextSize: 15
property color subtextColor: Theme.color.neutral9
property bool wrap: true
+ property real descriptionLineHeight: 1
spacing: 0
Loader {
@@ -60,6 +61,7 @@ ColumnLayout {
text: root.description
horizontalAlignment: root.center ? Text.AlignHCenter : Text.AlignLeft
wrapMode: wrap ? Text.WordWrap : Text.NoWrap
+ lineHeight: root.descriptionLineHeight
Behavior on color {
ColorAnimation { duration: 150 }
diff --git a/src/qml/controls/ProgressIndicator.qml b/src/qml/controls/ProgressIndicator.qml
index 117a4baebb..9d6d62d329 100644
--- a/src/qml/controls/ProgressIndicator.qml
+++ b/src/qml/controls/ProgressIndicator.qml
@@ -7,6 +7,7 @@ import QtQuick.Controls 2.15
Control {
property real progress: 0
+ property color progressColor: Theme.color.orange
Behavior on progress {
NumberAnimation {
easing.type: Easing.Bezier
@@ -26,7 +27,7 @@ Control {
width: contentItem.width
height: contentItem.height
radius: contentItem.radius
- color: Theme.color.orange
+ color: progressColor
}
}
}
diff --git a/src/qml/controls/Theme.qml b/src/qml/controls/Theme.qml
index f57e152cbd..3c7621c2b5 100644
--- a/src/qml/controls/Theme.qml
+++ b/src/qml/controls/Theme.qml
@@ -27,6 +27,7 @@ Control {
required property color blue
required property color amber
required property color purple
+ required property color transparent
required property color neutral0
required property color neutral1
required property color neutral2
@@ -59,6 +60,7 @@ Control {
blue: "#3CA3DE"
amber: "#C9B500"
purple: "#C075DC"
+ transparent: "#00000000"
neutral0: "#000000"
neutral1: "#1A1A1A"
neutral2: "#2D2D2D"
@@ -91,6 +93,7 @@ Control {
blue: "#2D9CDB"
amber: "#C9B500"
purple: "#BB6BD9"
+ transparent: "#00000000"
neutral0: "#FFFFFF"
neutral1: "#F8F8F8"
neutral2: "#F4F4F4"
diff --git a/src/qml/imageprovider.cpp b/src/qml/imageprovider.cpp
index daf2feeae2..abd387f3b4 100644
--- a/src/qml/imageprovider.cpp
+++ b/src/qml/imageprovider.cpp
@@ -77,6 +77,16 @@ QPixmap ImageProvider::requestPixmap(const QString& id, QSize* size, const QSize
return QIcon(":/icons/check").pixmap(requested_size);
}
+ if (id == "circle-file") {
+ *size = requested_size;
+ return QIcon(":/icons/circle-file").pixmap(requested_size);
+ }
+
+ if (id == "circle-green-check") {
+ *size = requested_size;
+ return QIcon(":/icons/circle-green-check").pixmap(requested_size);
+ }
+
if (id == "cross") {
*size = requested_size;
return QIcon(":/icons/cross").pixmap(requested_size);
@@ -102,6 +112,11 @@ QPixmap ImageProvider::requestPixmap(const QString& id, QSize* size, const QSize
return QIcon(":/icons/gear-outline").pixmap(requested_size);
}
+ if (id == "green-check") {
+ *size = requested_size;
+ return QIcon(":/icons/green-check").pixmap(requested_size);
+ }
+
if (id == "info") {
*size = requested_size;
return QIcon(":/icons/info").pixmap(requested_size);
diff --git a/src/qml/pages/settings/SettingsConnection.qml b/src/qml/pages/settings/SettingsConnection.qml
index 9ff9094f11..2e36dd4a99 100644
--- a/src/qml/pages/settings/SettingsConnection.qml
+++ b/src/qml/pages/settings/SettingsConnection.qml
@@ -13,6 +13,11 @@ Item {
property alias navMiddleDetail: connectionSwipe.navMiddleDetail
property alias navLeftDetail: connectionSwipe.navLeftDetail
property alias showHeader: connectionSwipe.showHeader
+
+ function setSnapshotImported(imported) {
+ connection_settings.loadedDetailItem.setSnapshotImported(imported)
+ }
+
SwipeView {
id: connectionSwipe
property alias navRightDetail: connection_settings.navRightDetail
@@ -38,5 +43,14 @@ Item {
connectionSwipe.decrementCurrentIndex()
}
}
+ SettingsSnapshot {
+ onSnapshotImportCompleted: {
+ setSnapshotImported(true)
+ }
+ onBackClicked: {
+ connectionSwipe.decrementCurrentIndex()
+ connectionSwipe.decrementCurrentIndex()
+ }
+ }
}
}
diff --git a/src/qml/pages/settings/SettingsSnapshot.qml b/src/qml/pages/settings/SettingsSnapshot.qml
new file mode 100644
index 0000000000..e6c557a022
--- /dev/null
+++ b/src/qml/pages/settings/SettingsSnapshot.qml
@@ -0,0 +1,37 @@
+// Copyright (c) 2024-present 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 "../../controls"
+import "../../components"
+
+Page {
+ signal backClicked
+ signal snapshotImportCompleted
+
+ id: root
+
+ background: null
+ implicitWidth: 450
+ leftPadding: 20
+ rightPadding: 20
+ topPadding: 30
+
+ header: NavigationBar2 {
+ leftItem: NavButton {
+ iconSource: "image://images/caret-left"
+ text: qsTr("Back")
+ onClicked: root.backClicked()
+ }
+ }
+ SnapshotSettings {
+ width: Math.min(parent.width, 450)
+ anchors.horizontalCenter: parent.horizontalCenter
+ onSnapshotImportCompleted: {
+ root.snapshotImportCompleted()
+ }
+ }
+}
diff --git a/src/qml/res/icons/circle-file.png b/src/qml/res/icons/circle-file.png
new file mode 100644
index 0000000000..14a776e6d5
Binary files /dev/null and b/src/qml/res/icons/circle-file.png differ
diff --git a/src/qml/res/icons/circle-green-check.png b/src/qml/res/icons/circle-green-check.png
new file mode 100644
index 0000000000..25bb20e00f
Binary files /dev/null and b/src/qml/res/icons/circle-green-check.png differ
diff --git a/src/qml/res/icons/green-check.png b/src/qml/res/icons/green-check.png
new file mode 100644
index 0000000000..65b1799020
Binary files /dev/null and b/src/qml/res/icons/green-check.png differ
diff --git a/src/qml/res/src/circle-file.svg b/src/qml/res/src/circle-file.svg
new file mode 100644
index 0000000000..d8af3949d8
--- /dev/null
+++ b/src/qml/res/src/circle-file.svg
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/src/qml/res/src/circle-green-check.svg b/src/qml/res/src/circle-green-check.svg
new file mode 100644
index 0000000000..d56c175fd4
--- /dev/null
+++ b/src/qml/res/src/circle-green-check.svg
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/src/qml/res/src/green-check.svg b/src/qml/res/src/green-check.svg
new file mode 100644
index 0000000000..fba9cac6b9
--- /dev/null
+++ b/src/qml/res/src/green-check.svg
@@ -0,0 +1,4 @@
+