diff --git a/res/fonts/NotoSansCJK-Subset.ttf b/res/fonts/NotoSansCJK-Subset.ttf index 5a4acd6..a4c876b 100644 Binary files a/res/fonts/NotoSansCJK-Subset.ttf and b/res/fonts/NotoSansCJK-Subset.ttf differ diff --git a/res/lang/de.json b/res/lang/de.json index ac20877..4bb9d79 100644 --- a/res/lang/de.json +++ b/res/lang/de.json @@ -1294,6 +1294,10 @@ "sweep_to": "Gefegt an:", "sweep_toggle": "In meine Wallet fegen (Schlüssel nicht behalten)", "sweep_tx": "Transaktion:", + "switch_stopnode_body": "Beim Wallet-Wechsel wird der Knoten mit der ausgewählten Wallet neu gestartet. Der laufende Knoten wird gestoppt und mit der neuen Wallet neu gestartet – wenn Sie ihn absichtlich laufen ließen, startet er automatisch wieder.", + "switch_stopnode_confirm": "Knoten stoppen & wechseln", + "switch_stopnode_title": "Laufenden Knoten stoppen?", + "switch_stopnode_warn": "Es läuft bereits ein Knoten, den diese Wallet nicht gestartet hat.", "syncing": "Synchronisiere...", "t_address": "T-Adresse", "t_addresses": "T-Adressen", diff --git a/res/lang/es.json b/res/lang/es.json index a877ba0..416d087 100644 --- a/res/lang/es.json +++ b/res/lang/es.json @@ -1294,6 +1294,10 @@ "sweep_to": "Barrido a:", "sweep_toggle": "Barrer a mi monedero (no conservar la clave)", "sweep_tx": "Transacción:", + "switch_stopnode_body": "Cambiar de cartera reinicia el nodo con la cartera seleccionada. El nodo en ejecución se detendrá y se reiniciará con la nueva cartera; si lo dejaste en marcha a propósito, volverá a iniciarse automáticamente.", + "switch_stopnode_confirm": "Detener nodo y cambiar", + "switch_stopnode_title": "¿Detener el nodo en ejecución?", + "switch_stopnode_warn": "Ya hay un nodo en ejecución que esta cartera no inició.", "syncing": "Sincronizando...", "t_address": "Dirección T", "t_addresses": "Direcciones T", diff --git a/res/lang/fr.json b/res/lang/fr.json index d537add..f9f6f91 100644 --- a/res/lang/fr.json +++ b/res/lang/fr.json @@ -1294,6 +1294,10 @@ "sweep_to": "Balayé vers :", "sweep_toggle": "Balayer vers mon portefeuille (ne pas conserver la clé)", "sweep_tx": "Transaction :", + "switch_stopnode_body": "Changer de portefeuille redémarre le nœud sur le portefeuille sélectionné. Le nœud en cours sera arrêté puis relancé sur le nouveau portefeuille — si vous l'avez laissé tourner exprès, il redémarre automatiquement.", + "switch_stopnode_confirm": "Arrêter le nœud et changer", + "switch_stopnode_title": "Arrêter le nœud en cours d'exécution ?", + "switch_stopnode_warn": "Un nœud que ce portefeuille n'a pas démarré est déjà en cours d'exécution.", "syncing": "Synchronisation...", "t_address": "Adresse T", "t_addresses": "Adresses T", diff --git a/res/lang/ja.json b/res/lang/ja.json index 0dfb124..88a83a3 100644 --- a/res/lang/ja.json +++ b/res/lang/ja.json @@ -1294,6 +1294,10 @@ "sweep_to": "集約先:", "sweep_toggle": "ウォレットに集約(鍵は保持しない)", "sweep_tx": "取引:", + "switch_stopnode_body": "ウォレットを切り替えると、選択したウォレットでノードが再起動します。実行中のノードは停止され、新しいウォレットで再起動されます。意図的に起動したままにしていた場合は、自動的に復帰します。", + "switch_stopnode_confirm": "ノードを停止して切り替え", + "switch_stopnode_title": "実行中のノードを停止しますか?", + "switch_stopnode_warn": "このウォレットが起動していないノードがすでに実行中です。", "syncing": "同期中...", "t_address": "Tアドレス", "t_addresses": "Tアドレス", diff --git a/res/lang/ko.json b/res/lang/ko.json index fb83487..bf08637 100644 --- a/res/lang/ko.json +++ b/res/lang/ko.json @@ -1294,6 +1294,10 @@ "sweep_to": "쓸어담은 주소:", "sweep_toggle": "내 지갑으로 쓸어담기 (키 보관 안 함)", "sweep_tx": "거래:", + "switch_stopnode_body": "지갑을 전환하면 선택한 지갑으로 노드가 다시 시작됩니다. 실행 중인 노드가 중지되고 새 지갑으로 다시 시작됩니다. 일부러 계속 실행해 두었다면 자동으로 다시 켜집니다.", + "switch_stopnode_confirm": "노드 중지 후 전환", + "switch_stopnode_title": "실행 중인 노드를 중지할까요?", + "switch_stopnode_warn": "이 지갑이 시작하지 않은 노드가 이미 실행 중입니다.", "syncing": "동기화 중...", "t_address": "T 주소", "t_addresses": "T 주소", diff --git a/res/lang/pt.json b/res/lang/pt.json index 8eb68c0..e9810c0 100644 --- a/res/lang/pt.json +++ b/res/lang/pt.json @@ -1294,6 +1294,10 @@ "sweep_to": "Varrido para:", "sweep_toggle": "Varrer para minha carteira (não manter a chave)", "sweep_tx": "Transação:", + "switch_stopnode_body": "Trocar de carteira reinicia o nó com a carteira selecionada. O nó em execução será parado e reiniciado com a nova carteira — se você o deixou em execução de propósito, ele volta automaticamente.", + "switch_stopnode_confirm": "Parar nó e trocar", + "switch_stopnode_title": "Parar o nó em execução?", + "switch_stopnode_warn": "Já há um nó em execução que esta carteira não iniciou.", "syncing": "Sincronizando...", "t_address": "Endereço T", "t_addresses": "Endereços T", diff --git a/res/lang/ru.json b/res/lang/ru.json index 23c69ba..8753c90 100644 --- a/res/lang/ru.json +++ b/res/lang/ru.json @@ -1294,6 +1294,10 @@ "sweep_to": "Переведено на:", "sweep_toggle": "Перевести в мой кошелёк (не сохранять ключ)", "sweep_tx": "Транзакция:", + "switch_stopnode_body": "Смена кошелька перезапускает узел с выбранным кошельком. Запущенный узел будет остановлен и перезапущен с новым кошельком — если вы намеренно оставили его работать, он запустится снова автоматически.", + "switch_stopnode_confirm": "Остановить узел и переключить", + "switch_stopnode_title": "Остановить запущенный узел?", + "switch_stopnode_warn": "Уже запущен узел, который не был запущен этим кошельком.", "syncing": "Синхронизация...", "t_address": "T-адрес", "t_addresses": "T-адреса", diff --git a/res/lang/zh.json b/res/lang/zh.json index c856b2f..abcb69f 100644 --- a/res/lang/zh.json +++ b/res/lang/zh.json @@ -1294,6 +1294,10 @@ "sweep_to": "归集到:", "sweep_toggle": "归集到我的钱包(不保留密钥)", "sweep_tx": "交易:", + "switch_stopnode_body": "切换钱包会以所选钱包重启节点。正在运行的节点将被停止并以新钱包重新启动——如果你是特意让它保持运行的,它会自动重新启动。", + "switch_stopnode_confirm": "停止节点并切换", + "switch_stopnode_title": "停止正在运行的节点?", + "switch_stopnode_warn": "已有一个并非由此钱包启动的节点正在运行。", "syncing": "同步中...", "t_address": "T 地址", "t_addresses": "T 地址", diff --git a/src/app.cpp b/src/app.cpp index 063cb76..7c661f1 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -2034,6 +2034,7 @@ void App::render() renderEncryptWalletDialog(); renderDecryptWalletDialog(); renderPinDialogs(); + renderSwitchStopDaemonDialog(); // Render notifications (toast messages) ui::Notifications::instance().render(); @@ -3860,6 +3861,42 @@ void App::renderAntivirusHelpDialog() #endif } +void App::renderSwitchStopDaemonDialog() +{ + if (!show_switch_stop_daemon_confirm_) return; + + ui::material::OverlayDialogSpec ov; + ov.title = TR("switch_stopnode_title"); + ov.p_open = &show_switch_stop_daemon_confirm_; // the X / backdrop click acts as Cancel + ov.style = ui::material::OverlayStyle::BlurFloat; + ov.cardWidth = 520.0f; ov.idSuffix = "switchstopnode"; + if (!ui::material::BeginOverlayDialog(ov)) { + // Closed via X/backdrop — treat as cancel: drop the pending target. + if (!show_switch_stop_daemon_confirm_) pending_switch_wallet_file_.clear(); + return; + } + const float dp = ui::Layout::dpiScale(); + + ui::material::DialogWarningHeader(TR("switch_stopnode_warn")); + ImGui::Dummy(ImVec2(0, ui::Layout::spacingSm())); + ImGui::TextWrapped("%s", TR("switch_stopnode_body")); + ImGui::Dummy(ImVec2(0, ui::Layout::spacingMd())); + + if (ui::material::TactileButton(TR("switch_stopnode_confirm"), ImVec2(200.0f * dp, 0))) { + const std::string w = pending_switch_wallet_file_; + pending_switch_wallet_file_.clear(); + show_switch_stop_daemon_confirm_ = false; + switchToWallet(w, /*stopDaemonConfirmed=*/true); // now proceeds to stop + wait + restart + } + ImGui::SameLine(); + if (ui::material::TactileButton(TR("cancel"), ImVec2(110.0f * dp, 0))) { + pending_switch_wallet_file_.clear(); + show_switch_stop_daemon_confirm_ = false; + } + + ui::material::EndOverlayDialog(); +} + void App::refreshNow() { // Trigger immediate refresh on all categories diff --git a/src/app.h b/src/app.h index 7b5b426..155542c 100644 --- a/src/app.h +++ b/src/app.h @@ -488,7 +488,9 @@ public: // Switch the active wallet: persist the new -wallet=, stop the node, restart on it // (rescan only if it was never synced in this datadir). Per-wallet data follows automatically // via the identity-scoped caches (P1). No-op if that wallet is already active. - void switchToWallet(const std::string& walletFile); + // stopDaemonConfirmed: skip the "stop the running node?" confirmation (set true when re-entered from + // that dialog). The confirmation only appears when the node to restart was ADOPTED (externally running). + void switchToWallet(const std::string& walletFile, bool stopDaemonConfirmed = false); // Main-thread continuation: if a wallet switch's daemon failed to start, revert active_wallet_file. void processWalletSwitchRevert(); @@ -762,6 +764,10 @@ private: // loop flags a revert so a broken wallet still can't stick. std::atomic wallet_switch_pending_confirm_{false}; std::string wallet_switch_prev_file_; + // Confirm-before-stop for a switch that would stop an ADOPTED (externally-running) node. switchToWallet + // sets these and defers to renderSwitchStopDaemonDialog; confirming re-calls switchToWallet(w, true). + bool show_switch_stop_daemon_confirm_ = false; + std::string pending_switch_wallet_file_; // Set by the deleteBlockchainData worker (item count); the main loop surfaces a completion toast // and resets it to -1. Atomic because the worker thread writes it and the UI thread reads/clears it. @@ -1163,6 +1169,7 @@ private: void renderDecryptWalletDialog(); void renderPinDialogs(); void renderAntivirusHelpDialog(); + void renderSwitchStopDaemonDialog(); // confirm before stopping an adopted node to switch wallets void processDeferredEncryption(); // Private methods - connection diff --git a/src/app_network.cpp b/src/app_network.cpp index 216178b..d1c821a 100644 --- a/src/app_network.cpp +++ b/src/app_network.cpp @@ -1023,7 +1023,7 @@ void App::updateWalletIndexForActiveWallet(bool markOpened) if (wallet_index_.upsert(e)) wallet_index_.save(); } -void App::switchToWallet(const std::string& walletFile) +void App::switchToWallet(const std::string& walletFile, bool stopDaemonConfirmed) { if (!supportsFullNodeLifecycleActions()) { ui::Notifications::instance().warning("Full-node lifecycle actions are unavailable in lite build"); @@ -1056,6 +1056,15 @@ void App::switchToWallet(const std::string& walletFile) ui::Notifications::instance().warning("Finish or cancel the pending send before switching wallets."); return; } + // The node we'd restart may be one this session ADOPTED — a dragonxd already running when the wallet + // launched (left running by "keep node running", or started by the user). Switching must stop+restart + // it on the new wallet, so confirm first rather than killing a node the user may have kept up on + // purpose. (An owned node we started this session restarts silently, as before.) + if (!stopDaemonConfirmed && daemon_controller_ && daemon_controller_->externalDaemonDetected()) { + pending_switch_wallet_file_ = walletFile; + show_switch_stop_daemon_confirm_ = true; + return; + } // Rescan only if this wallet was never synced in this datadir (freshly imported/new). One we've // loaded here before just catches up from its recorded block on start — fast. diff --git a/src/app_sweep.cpp b/src/app_sweep.cpp index 7a82532..9d977c7 100644 --- a/src/app_sweep.cpp +++ b/src/app_sweep.cpp @@ -376,6 +376,9 @@ void App::buildSweepCatalog() add("modal-antivirus", ui::NavPage::Mining, [](App& a) { a.pending_antivirus_dialog_ = true; }, [](App& a) { a.pending_antivirus_dialog_ = false; }); + add("modal-switch-stopnode", ui::NavPage::Settings, + [](App& a) { a.pending_switch_wallet_file_ = "wallet-savings.dat"; a.show_switch_stop_daemon_confirm_ = true; }, + [](App& a) { a.show_switch_stop_daemon_confirm_ = false; a.pending_switch_wallet_file_.clear(); }); // Wave-2 fund/secret dialogs (setup never fires the async RPC — no button is clicked). add("modal-shield", ui::NavPage::Send, [](App&) { ui::ShieldDialog::showShieldCoinbase(); }, diff --git a/src/util/i18n.cpp b/src/util/i18n.cpp index 9a640e3..b353004 100644 --- a/src/util/i18n.cpp +++ b/src/util/i18n.cpp @@ -1039,7 +1039,13 @@ void I18n::loadBuiltinEnglish() strings_["confirm_transaction"] = "Confirm Transaction"; strings_["confirm_and_send"] = "Confirm & Send"; strings_["cancel"] = "Cancel"; - + + // Wallet-switch "stop the running node?" confirmation + strings_["switch_stopnode_title"] = "Stop the running node?"; + strings_["switch_stopnode_warn"] = "A node is already running that this wallet didn't start."; + strings_["switch_stopnode_body"] = "Switching wallets restarts the node on the wallet you selected. The running node will be stopped and relaunched on the new wallet — if you kept it running on purpose, it comes back up automatically."; + strings_["switch_stopnode_confirm"] = "Stop node & switch"; + // Receive Tab strings_["receiving_addresses"] = "Your Receiving Addresses"; strings_["new_z_shielded"] = "New z-Address (Shielded)";