From 7af55d5e190bc29a07c54701f85f45cb3a6e7c95 Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Tue, 3 Jun 2025 23:56:20 -0400 Subject: [PATCH 1/3] qml: Hide tabs and show Select Wallet if no wallet is loaded --- src/qml/models/walletqmlmodel.h | 6 ++++ src/qml/pages/wallet/DesktopWallets.qml | 4 ++- src/qml/pages/wallet/WalletBadge.qml | 40 ++++++++++++++++++++++++- src/qml/walletqmlcontroller.cpp | 11 +++++++ src/qml/walletqmlcontroller.h | 5 ++++ 5 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/qml/models/walletqmlmodel.h b/src/qml/models/walletqmlmodel.h index 340b351ada..d17b038cbb 100644 --- a/src/qml/models/walletqmlmodel.h +++ b/src/qml/models/walletqmlmodel.h @@ -29,6 +29,7 @@ class WalletQmlModel : public QObject Q_PROPERTY(SendRecipient* sendRecipient READ sendRecipient CONSTANT) Q_PROPERTY(WalletQmlModelTransaction* currentTransaction READ currentTransaction NOTIFY currentTransactionChanged) Q_PROPERTY(unsigned int targetBlocks READ feeTargetBlocks WRITE setFeeTargetBlocks NOTIFY feeTargetBlocksChanged) + Q_PROPERTY(bool isWalletLoaded READ isWalletLoaded NOTIFY walletIsLoadedChanged) public: WalletQmlModel(std::unique_ptr wallet, QObject* parent = nullptr); @@ -67,11 +68,15 @@ class WalletQmlModel : public QObject unsigned int feeTargetBlocks() const; void setFeeTargetBlocks(unsigned int target_blocks); + bool isWalletLoaded() const { return m_is_wallet_loaded; } + void setWalletLoaded(bool loaded); + Q_SIGNALS: void nameChanged(); void balanceChanged(); void currentTransactionChanged(); void feeTargetBlocksChanged(); + void walletIsLoadedChanged(); private: std::unique_ptr m_wallet; @@ -80,6 +85,7 @@ class WalletQmlModel : public QObject SendRecipient* m_current_recipient{nullptr}; WalletQmlModelTransaction* m_current_transaction{nullptr}; wallet::CCoinControl m_coin_control; + bool m_is_wallet_loaded{false}; }; #endif // BITCOIN_QML_MODELS_WALLETQMLMODEL_H diff --git a/src/qml/pages/wallet/DesktopWallets.qml b/src/qml/pages/wallet/DesktopWallets.qml index d2bf0469b3..3d639bd579 100644 --- a/src/qml/pages/wallet/DesktopWallets.qml +++ b/src/qml/pages/wallet/DesktopWallets.qml @@ -30,6 +30,7 @@ Page { text: walletController.selectedWallet.name balance: walletController.selectedWallet.balance loading: !walletController.initialized + noWalletAvailable: !walletController.isWalletLoaded MouseArea { anchors.fill: parent @@ -52,9 +53,9 @@ Page { } } centerItem: RowLayout { + visible: walletController.isWalletLoaded NavigationTab { id: activityTabButton - checked: true text: qsTr("Activity") property int index: 0 ButtonGroup.group: navigationTabs @@ -79,6 +80,7 @@ Page { } NavigationTab { id: blockClockTabButton + checked: true Layout.preferredWidth: 30 Layout.rightMargin: 10 property int index: 3 diff --git a/src/qml/pages/wallet/WalletBadge.qml b/src/qml/pages/wallet/WalletBadge.qml index 1fca0b7105..fb97fff5b8 100644 --- a/src/qml/pages/wallet/WalletBadge.qml +++ b/src/qml/pages/wallet/WalletBadge.qml @@ -23,6 +23,7 @@ Button { property bool showIcon: true property string balance: "0.0 000 000" property bool loading: false + property bool noWalletAvailable: false checkable: true hoverEnabled: AppMode.isDesktop @@ -32,6 +33,10 @@ Button { topPadding: 0 clip: true + HoverHandler{ + cursorShape: Qt.PointingHandCursor + } + contentItem: Item { RowLayout { visible: root.loading @@ -65,7 +70,40 @@ Button { } RowLayout { - visible: !root.loading + visible: !root.loading && root.noWalletAvailable + + opacity: visible ? 1 : 0 + + Behavior on opacity { + NumberAnimation { duration: 400 } + } + + anchors.leftMargin: 5 + anchors.rightMargin: 5 + anchors.centerIn: parent + clip: true + spacing: 5 + Icon { + visible: root.showIcon + source: "image://images/caret-down-medium-filled" + color: Theme.color.neutral8 + size: 30 + Layout.minimumWidth: 30 + Layout.preferredWidth: 30 + Layout.maximumWidth: 30 + } + CoreText { + horizontalAlignment: Text.AlignLeft + Layout.fillWidth: true + wrap: false + font.pixelSize: 15 + text: qsTr("Select Wallet") + color: root.textColor + } + } + + RowLayout { + visible: !root.loading && !root.noWalletAvailable opacity: visible ? 1 : 0 diff --git a/src/qml/walletqmlcontroller.cpp b/src/qml/walletqmlcontroller.cpp index a9eed57d0d..9d0c520e0f 100644 --- a/src/qml/walletqmlcontroller.cpp +++ b/src/qml/walletqmlcontroller.cpp @@ -94,6 +94,7 @@ void WalletQmlController::handleLoadWallet(std::unique_ptr w if (wallet_model->name() == name) { m_selected_wallet = wallet_model; Q_EMIT selectedWalletChanged(); + setWalletLoaded(true); return; } } @@ -104,6 +105,7 @@ void WalletQmlController::handleLoadWallet(std::unique_ptr w m_selected_wallet = wallet_model; m_wallets.push_back(m_selected_wallet); Q_EMIT selectedWalletChanged(); + setWalletLoaded(true); } void WalletQmlController::initialize() @@ -118,9 +120,18 @@ void WalletQmlController::initialize() } if (!m_wallets.empty()) { m_selected_wallet = m_wallets.front(); + setWalletLoaded(true); Q_EMIT selectedWalletChanged(); } m_initialized = true; Q_EMIT initializedChanged(); } + +void WalletQmlController::setWalletLoaded(bool loaded) +{ + if (m_is_wallet_loaded != loaded) { + m_is_wallet_loaded = loaded; + Q_EMIT isWalletLoadedChanged(); + } +} diff --git a/src/qml/walletqmlcontroller.h b/src/qml/walletqmlcontroller.h index 4dd5acb211..aba14db386 100644 --- a/src/qml/walletqmlcontroller.h +++ b/src/qml/walletqmlcontroller.h @@ -22,6 +22,7 @@ class WalletQmlController : public QObject Q_OBJECT Q_PROPERTY(WalletQmlModel* selectedWallet READ selectedWallet NOTIFY selectedWalletChanged) Q_PROPERTY(bool initialized READ initialized NOTIFY initializedChanged) + Q_PROPERTY(bool isWalletLoaded READ isWalletLoaded NOTIFY isWalletLoadedChanged) public: explicit WalletQmlController(interfaces::Node& node, QObject *parent = nullptr); @@ -33,10 +34,13 @@ class WalletQmlController : public QObject WalletQmlModel* selectedWallet() const; void unloadWallets(); bool initialized() const { return m_initialized; } + bool isWalletLoaded() const { return m_is_wallet_loaded; } + void setWalletLoaded(bool loaded); Q_SIGNALS: void selectedWalletChanged(); void initializedChanged(); + void isWalletLoadedChanged(); public Q_SLOTS: void initialize(); @@ -52,6 +56,7 @@ public Q_SLOTS: QMutex m_wallets_mutex; std::vector m_wallets; std::unique_ptr m_handler_load_wallet; + bool m_is_wallet_loaded{false}; bilingual_str m_error_message; std::vector m_warning_messages; From 5b9b484738ee046b5010c0f90657932a9eb1325d Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Wed, 4 Jun 2025 23:46:44 -0400 Subject: [PATCH 2/3] qml: Show Add Wallet in WalletBadge if none are found --- src/qml/pages/wallet/DesktopWallets.qml | 13 ++++++++++--- src/qml/pages/wallet/WalletBadge.qml | 17 +++++++++-------- src/qml/walletqmlcontroller.cpp | 15 +++++++++++++++ src/qml/walletqmlcontroller.h | 5 +++++ 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/qml/pages/wallet/DesktopWallets.qml b/src/qml/pages/wallet/DesktopWallets.qml index 3d639bd579..4763a901f9 100644 --- a/src/qml/pages/wallet/DesktopWallets.qml +++ b/src/qml/pages/wallet/DesktopWallets.qml @@ -30,13 +30,20 @@ Page { text: walletController.selectedWallet.name balance: walletController.selectedWallet.balance loading: !walletController.initialized - noWalletAvailable: !walletController.isWalletLoaded + noWalletLoaded: !walletController.isWalletLoaded + noWalletsFound: walletController.noWalletsFound MouseArea { anchors.fill: parent onClicked: { - walletListModel.listWalletDir() - walletSelect.opened ? walletSelect.close() : walletSelect.open() + if (walletController.initialized) { + walletListModel.listWalletDir() + if (walletController.noWalletsFound) { + root.addWallet() + } else { + walletSelect.opened ? walletSelect.close() : walletSelect.open() + } + } } } diff --git a/src/qml/pages/wallet/WalletBadge.qml b/src/qml/pages/wallet/WalletBadge.qml index fb97fff5b8..39d5726043 100644 --- a/src/qml/pages/wallet/WalletBadge.qml +++ b/src/qml/pages/wallet/WalletBadge.qml @@ -23,7 +23,8 @@ Button { property bool showIcon: true property string balance: "0.0 000 000" property bool loading: false - property bool noWalletAvailable: false + property bool noWalletLoaded: false + property bool noWalletsFound: false checkable: true hoverEnabled: AppMode.isDesktop @@ -70,7 +71,7 @@ Button { } RowLayout { - visible: !root.loading && root.noWalletAvailable + visible: !root.loading && root.noWalletLoaded opacity: visible ? 1 : 0 @@ -85,25 +86,25 @@ Button { spacing: 5 Icon { visible: root.showIcon - source: "image://images/caret-down-medium-filled" + source: root.noWalletsFound ? "image://images/plus" : "image://images/caret-down-medium-filled" color: Theme.color.neutral8 size: 30 - Layout.minimumWidth: 30 - Layout.preferredWidth: 30 - Layout.maximumWidth: 30 + Layout.minimumWidth: 25 + Layout.preferredWidth: 25 + Layout.maximumWidth: 25 } CoreText { horizontalAlignment: Text.AlignLeft Layout.fillWidth: true wrap: false font.pixelSize: 15 - text: qsTr("Select Wallet") + text: root.noWalletsFound ? qsTr("Add Wallet") : qsTr("Select Wallet") color: root.textColor } } RowLayout { - visible: !root.loading && !root.noWalletAvailable + visible: !root.loading && !root.noWalletLoaded opacity: visible ? 1 : 0 diff --git a/src/qml/walletqmlcontroller.cpp b/src/qml/walletqmlcontroller.cpp index 9d0c520e0f..81065a5d86 100644 --- a/src/qml/walletqmlcontroller.cpp +++ b/src/qml/walletqmlcontroller.cpp @@ -79,6 +79,7 @@ void WalletQmlController::createSingleSigWallet(const QString &name, const QStri if (wallet) { m_selected_wallet = new WalletQmlModel(std::move(*wallet)); m_wallets.push_back(m_selected_wallet); + setNoWalletsFound(false); Q_EMIT selectedWalletChanged(); } else { m_error_message = util::ErrorString(wallet); @@ -124,6 +125,12 @@ void WalletQmlController::initialize() Q_EMIT selectedWalletChanged(); } + if (m_node.walletLoader().listWalletDir().size() == 0) { + setNoWalletsFound(true); + } else { + setNoWalletsFound(false); + } + m_initialized = true; Q_EMIT initializedChanged(); } @@ -135,3 +142,11 @@ void WalletQmlController::setWalletLoaded(bool loaded) Q_EMIT isWalletLoadedChanged(); } } + +void WalletQmlController::setNoWalletsFound(bool no_wallets_found) +{ + if (m_no_wallets_found != no_wallets_found) { + m_no_wallets_found = no_wallets_found; + Q_EMIT noWalletsFoundChanged(); + } +} diff --git a/src/qml/walletqmlcontroller.h b/src/qml/walletqmlcontroller.h index aba14db386..56f082e7c4 100644 --- a/src/qml/walletqmlcontroller.h +++ b/src/qml/walletqmlcontroller.h @@ -23,6 +23,7 @@ class WalletQmlController : public QObject Q_PROPERTY(WalletQmlModel* selectedWallet READ selectedWallet NOTIFY selectedWalletChanged) Q_PROPERTY(bool initialized READ initialized NOTIFY initializedChanged) Q_PROPERTY(bool isWalletLoaded READ isWalletLoaded NOTIFY isWalletLoadedChanged) + Q_PROPERTY(bool noWalletsFound READ noWalletsFound NOTIFY noWalletsFoundChanged) public: explicit WalletQmlController(interfaces::Node& node, QObject *parent = nullptr); @@ -36,11 +37,14 @@ class WalletQmlController : public QObject bool initialized() const { return m_initialized; } bool isWalletLoaded() const { return m_is_wallet_loaded; } void setWalletLoaded(bool loaded); + bool noWalletsFound() const { return m_no_wallets_found; } + void setNoWalletsFound(bool no_wallets_found); Q_SIGNALS: void selectedWalletChanged(); void initializedChanged(); void isWalletLoadedChanged(); + void noWalletsFoundChanged(); public Q_SLOTS: void initialize(); @@ -57,6 +61,7 @@ public Q_SLOTS: std::vector m_wallets; std::unique_ptr m_handler_load_wallet; bool m_is_wallet_loaded{false}; + bool m_no_wallets_found{false}; bilingual_str m_error_message; std::vector m_warning_messages; From 03b568ddb352b9a95251f513ad3cfd7402e60463 Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Thu, 5 Jun 2025 23:19:33 -0400 Subject: [PATCH 3/3] qml: Add Context to CreateWalletWizard The Context is used to change the nav bar text --- src/qml/pages/main.qml | 8 +++++--- src/qml/pages/wallet/CreateWalletWizard.qml | 13 ++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/qml/pages/main.qml b/src/qml/pages/main.qml index 60aa6c2705..43a94fad5e 100644 --- a/src/qml/pages/main.qml +++ b/src/qml/pages/main.qml @@ -69,8 +69,10 @@ ApplicationWindow { onFinished: { optionsModel.onboard() if (AppMode.walletEnabled && AppMode.isDesktop) { - main.push(desktopWallets) - main.push(createWalletWizard) + main.push([ + desktopWallets, {}, + createWalletWizard, { "launchContext": CreateWalletWizard.Context.Onboarding } + ]) } else { main.push(node) } @@ -82,7 +84,7 @@ ApplicationWindow { id: desktopWallets DesktopWallets { onAddWallet: { - main.push(createWalletWizard) + main.push(createWalletWizard, { "launchContext": CreateWalletWizard.Context.Main }) } onSendTransaction: { main.push(sendReviewPage) diff --git a/src/qml/pages/wallet/CreateWalletWizard.qml b/src/qml/pages/wallet/CreateWalletWizard.qml index f275c9d37a..b6223d79b1 100644 --- a/src/qml/pages/wallet/CreateWalletWizard.qml +++ b/src/qml/pages/wallet/CreateWalletWizard.qml @@ -13,8 +13,11 @@ import "../wallet" PageStack { id: root + enum Context { Onboarding, Main } + signal finished() property string walletName: "" + property int launchContext: CreateWalletWizard.Context.Onboarding initialItem: Page { background: null @@ -22,7 +25,15 @@ PageStack { header: NavigationBar2 { id: navbar rightItem: NavButton { - text: qsTr("Skip") + text: { + switch (root.launchContext) { + case CreateWalletWizard.Context.Main: + return qsTr("Cancel"); + case CreateWalletWizard.Context.Onboarding: + default: + return qsTr("Skip"); + } + } onClicked: { root.finished() }