feat(wallet): live progress modal for wallet switching
The "Stop the running node?" confirm modal now stays open through the entire switch — stop → wait-for-exit → start → reconnect — showing a live phase, and auto-closes the moment the new node connects. This turns the up-to-a-minute graceful-shutdown wait from an apparent freeze into visible progress. - WalletSwitchPhase (Stopping/Starting/Reconnecting/Failed) + atomic phase and dialog-open flags; the worker advances the phase, onConnected closes the modal, and a failed switch shows the accurate reason with a Close button. - Owned switches (no confirm) also show the progress modal directly. - "Continue in background" escape hatch so a long rescan / a hung startup never traps the user (the switch keeps running; a toast reports the result). - isWalletSwitchInProgress() keeps the frame loop redrawing so the phase text and spinner animate while otherwise idle. - i18n (EN + 8 languages, additive) + a modal-switch-progress sweep surface. State machine adversarially verified 6/6 (thread-safety, no stuck modal, confirm→progress transition, owned/unowned, no phase leak, redraw scoping). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1294,6 +1294,13 @@
|
||||
"sweep_to": "Gefegt an:",
|
||||
"sweep_toggle": "In meine Wallet fegen (Schlüssel nicht behalten)",
|
||||
"sweep_tx": "Transaktion:",
|
||||
"switch_progress_background": "Im Hintergrund fortsetzen",
|
||||
"switch_progress_failed_title": "Wallet-Wechsel fehlgeschlagen",
|
||||
"switch_progress_hint": "Ein sauberes Herunterfahren kann bis zu einer Minute dauern.",
|
||||
"switch_progress_reconnecting": "Neu verbinden",
|
||||
"switch_progress_starting": "Knoten wird mit der neuen Wallet gestartet",
|
||||
"switch_progress_stopping": "Aktueller Knoten wird gestoppt",
|
||||
"switch_progress_title": "Wallet wird gewechselt",
|
||||
"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?",
|
||||
|
||||
@@ -1294,6 +1294,13 @@
|
||||
"sweep_to": "Barrido a:",
|
||||
"sweep_toggle": "Barrer a mi monedero (no conservar la clave)",
|
||||
"sweep_tx": "Transacción:",
|
||||
"switch_progress_background": "Continuar en segundo plano",
|
||||
"switch_progress_failed_title": "Error al cambiar de cartera",
|
||||
"switch_progress_hint": "Un apagado ordenado puede tardar hasta un minuto.",
|
||||
"switch_progress_reconnecting": "Reconectando",
|
||||
"switch_progress_starting": "Iniciando el nodo con la nueva cartera",
|
||||
"switch_progress_stopping": "Deteniendo el nodo actual",
|
||||
"switch_progress_title": "Cambiando de cartera",
|
||||
"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?",
|
||||
|
||||
@@ -1294,6 +1294,13 @@
|
||||
"sweep_to": "Balayé vers :",
|
||||
"sweep_toggle": "Balayer vers mon portefeuille (ne pas conserver la clé)",
|
||||
"sweep_tx": "Transaction :",
|
||||
"switch_progress_background": "Continuer en arrière-plan",
|
||||
"switch_progress_failed_title": "Échec du changement de portefeuille",
|
||||
"switch_progress_hint": "Un arrêt propre peut prendre jusqu'à une minute.",
|
||||
"switch_progress_reconnecting": "Reconnexion",
|
||||
"switch_progress_starting": "Démarrage du nœud sur le nouveau portefeuille",
|
||||
"switch_progress_stopping": "Arrêt du nœud actuel",
|
||||
"switch_progress_title": "Changement de portefeuille",
|
||||
"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 ?",
|
||||
|
||||
@@ -1294,6 +1294,13 @@
|
||||
"sweep_to": "集約先:",
|
||||
"sweep_toggle": "ウォレットに集約(鍵は保持しない)",
|
||||
"sweep_tx": "取引:",
|
||||
"switch_progress_background": "バックグラウンドで続行",
|
||||
"switch_progress_failed_title": "ウォレットの切り替えに失敗しました",
|
||||
"switch_progress_hint": "正常なシャットダウンには最大1分かかることがあります。",
|
||||
"switch_progress_reconnecting": "再接続しています",
|
||||
"switch_progress_starting": "新しいウォレットでノードを起動しています",
|
||||
"switch_progress_stopping": "現在のノードを停止しています",
|
||||
"switch_progress_title": "ウォレットを切り替え中",
|
||||
"switch_stopnode_body": "ウォレットを切り替えると、選択したウォレットでノードが再起動します。実行中のノードは停止され、新しいウォレットで再起動されます。意図的に起動したままにしていた場合は、自動的に復帰します。",
|
||||
"switch_stopnode_confirm": "ノードを停止して切り替え",
|
||||
"switch_stopnode_title": "実行中のノードを停止しますか?",
|
||||
|
||||
@@ -1294,6 +1294,13 @@
|
||||
"sweep_to": "쓸어담은 주소:",
|
||||
"sweep_toggle": "내 지갑으로 쓸어담기 (키 보관 안 함)",
|
||||
"sweep_tx": "거래:",
|
||||
"switch_progress_background": "백그라운드에서 계속",
|
||||
"switch_progress_failed_title": "지갑 전환 실패",
|
||||
"switch_progress_hint": "정상 종료에는 최대 1분이 걸릴 수 있습니다.",
|
||||
"switch_progress_reconnecting": "다시 연결하는 중",
|
||||
"switch_progress_starting": "새 지갑으로 노드를 시작하는 중",
|
||||
"switch_progress_stopping": "현재 노드를 중지하는 중",
|
||||
"switch_progress_title": "지갑 전환 중",
|
||||
"switch_stopnode_body": "지갑을 전환하면 선택한 지갑으로 노드가 다시 시작됩니다. 실행 중인 노드가 중지되고 새 지갑으로 다시 시작됩니다. 일부러 계속 실행해 두었다면 자동으로 다시 켜집니다.",
|
||||
"switch_stopnode_confirm": "노드 중지 후 전환",
|
||||
"switch_stopnode_title": "실행 중인 노드를 중지할까요?",
|
||||
|
||||
@@ -1294,6 +1294,13 @@
|
||||
"sweep_to": "Varrido para:",
|
||||
"sweep_toggle": "Varrer para minha carteira (não manter a chave)",
|
||||
"sweep_tx": "Transação:",
|
||||
"switch_progress_background": "Continuar em segundo plano",
|
||||
"switch_progress_failed_title": "Falha ao trocar de carteira",
|
||||
"switch_progress_hint": "Um desligamento normal pode levar até um minuto.",
|
||||
"switch_progress_reconnecting": "Reconectando",
|
||||
"switch_progress_starting": "Iniciando o nó com a nova carteira",
|
||||
"switch_progress_stopping": "Parando o nó atual",
|
||||
"switch_progress_title": "Trocando de carteira",
|
||||
"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?",
|
||||
|
||||
@@ -1294,6 +1294,13 @@
|
||||
"sweep_to": "Переведено на:",
|
||||
"sweep_toggle": "Перевести в мой кошелёк (не сохранять ключ)",
|
||||
"sweep_tx": "Транзакция:",
|
||||
"switch_progress_background": "Продолжить в фоне",
|
||||
"switch_progress_failed_title": "Не удалось переключить кошелёк",
|
||||
"switch_progress_hint": "Корректное завершение работы может занять до минуты.",
|
||||
"switch_progress_reconnecting": "Переподключение",
|
||||
"switch_progress_starting": "Запуск узла с новым кошельком",
|
||||
"switch_progress_stopping": "Остановка текущего узла",
|
||||
"switch_progress_title": "Переключение кошелька",
|
||||
"switch_stopnode_body": "Смена кошелька перезапускает узел с выбранным кошельком. Запущенный узел будет остановлен и перезапущен с новым кошельком — если вы намеренно оставили его работать, он запустится снова автоматически.",
|
||||
"switch_stopnode_confirm": "Остановить узел и переключить",
|
||||
"switch_stopnode_title": "Остановить запущенный узел?",
|
||||
|
||||
@@ -1294,6 +1294,13 @@
|
||||
"sweep_to": "归集到:",
|
||||
"sweep_toggle": "归集到我的钱包(不保留密钥)",
|
||||
"sweep_tx": "交易:",
|
||||
"switch_progress_background": "在后台继续",
|
||||
"switch_progress_failed_title": "切换钱包失败",
|
||||
"switch_progress_hint": "正常关闭最多可能需要一分钟。",
|
||||
"switch_progress_reconnecting": "正在重新连接",
|
||||
"switch_progress_starting": "正在以新钱包启动节点",
|
||||
"switch_progress_stopping": "正在停止当前节点",
|
||||
"switch_progress_title": "正在切换钱包",
|
||||
"switch_stopnode_body": "切换钱包会以所选钱包重启节点。正在运行的节点将被停止并以新钱包重新启动——如果你是特意让它保持运行的,它会自动重新启动。",
|
||||
"switch_stopnode_confirm": "停止节点并切换",
|
||||
"switch_stopnode_title": "停止正在运行的节点?",
|
||||
|
||||
75
src/app.cpp
75
src/app.cpp
@@ -3863,35 +3863,70 @@ void App::renderAntivirusHelpDialog()
|
||||
|
||||
void App::renderSwitchStopDaemonDialog()
|
||||
{
|
||||
if (!show_switch_stop_daemon_confirm_) return;
|
||||
const bool confirm = show_switch_stop_daemon_confirm_;
|
||||
const bool progress = wallet_switch_dialog_open_.load();
|
||||
if (!confirm && !progress) return;
|
||||
|
||||
const auto phase = static_cast<WalletSwitchPhase>(wallet_switch_phase_.load());
|
||||
const bool failed = progress && phase == WalletSwitchPhase::Failed;
|
||||
|
||||
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.title = confirm ? TR("switch_stopnode_title")
|
||||
: failed ? TR("switch_progress_failed_title")
|
||||
: TR("switch_progress_title");
|
||||
// A confirm can be dismissed via X/backdrop (= cancel); a running switch cannot (stay until it's up).
|
||||
ov.p_open = confirm ? &show_switch_stop_daemon_confirm_ : nullptr;
|
||||
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();
|
||||
if (confirm && !show_switch_stop_daemon_confirm_) pending_switch_wallet_file_.clear(); // X = cancel
|
||||
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;
|
||||
if (confirm) {
|
||||
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); // opens the live-progress phase below
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ui::material::TactileButton(TR("cancel"), ImVec2(110.0f * dp, 0))) {
|
||||
pending_switch_wallet_file_.clear();
|
||||
show_switch_stop_daemon_confirm_ = false;
|
||||
}
|
||||
} else if (failed) {
|
||||
ui::material::DialogWarningHeader(TR("switch_progress_failed_title"));
|
||||
ImGui::Dummy(ImVec2(0, ui::Layout::spacingSm()));
|
||||
ImGui::TextWrapped("%s", wallet_switch_error_.c_str());
|
||||
ImGui::Dummy(ImVec2(0, ui::Layout::spacingMd()));
|
||||
if (ui::material::TactileButton(TR("close"), ImVec2(110.0f * dp, 0))) {
|
||||
wallet_switch_dialog_open_.store(false);
|
||||
wallet_switch_phase_.store(static_cast<int>(WalletSwitchPhase::None));
|
||||
wallet_switch_error_.clear();
|
||||
}
|
||||
} else {
|
||||
// Live progress: an animated phase line, kept redrawing by isWalletSwitchInProgress().
|
||||
const char* status =
|
||||
phase == WalletSwitchPhase::Starting ? TR("switch_progress_starting")
|
||||
: phase == WalletSwitchPhase::Reconnecting ? TR("switch_progress_reconnecting")
|
||||
: TR("switch_progress_stopping");
|
||||
ImGui::Dummy(ImVec2(0, ui::Layout::spacingSm()));
|
||||
ui::material::Type().text(ui::material::TypeStyle::Subtitle1,
|
||||
(std::string(status) + ui::material::LoadingDots()).c_str());
|
||||
if (phase == WalletSwitchPhase::Stopping) {
|
||||
ImGui::Dummy(ImVec2(0, ui::Layout::spacingSm()));
|
||||
ImGui::TextWrapped("%s", TR("switch_progress_hint"));
|
||||
}
|
||||
ImGui::Dummy(ImVec2(0, ui::Layout::spacingMd()));
|
||||
// Escape hatch — the switch keeps running in the background (a toast reports the result).
|
||||
if (ui::material::TactileButton(TR("switch_progress_background"), ImVec2(220.0f * dp, 0)))
|
||||
wallet_switch_dialog_open_.store(false);
|
||||
}
|
||||
|
||||
ui::material::EndOverlayDialog();
|
||||
|
||||
11
src/app.h
11
src/app.h
@@ -139,6 +139,9 @@ public:
|
||||
* @brief Whether we are in the shutdown phase
|
||||
*/
|
||||
bool isShuttingDown() const { return shutting_down_; }
|
||||
// True while the wallet-switch progress modal is open — keeps the frame loop redrawing so its live
|
||||
// phase/spinner update in real time even when the app is otherwise idle.
|
||||
bool isWalletSwitchInProgress() const { return wallet_switch_dialog_open_.load(); }
|
||||
wallet::WalletCapabilities walletCapabilities() const { return wallet::currentWalletCapabilities(); }
|
||||
bool isLiteBuild() const { return wallet::isLiteBuild(walletCapabilities()); }
|
||||
bool supportsEmbeddedDaemon() const { return wallet::supportsEmbeddedDaemon(walletCapabilities()); }
|
||||
@@ -768,6 +771,14 @@ private:
|
||||
// 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_;
|
||||
// Live progress for the switch modal: it stays open from confirm through stop → wait-for-exit → start →
|
||||
// reconnect and auto-closes when the new node connects (onConnected). Phase is worker-updated;
|
||||
// dialog_open_ gates rendering — both atomic since onConnected/the worker may run off the main thread.
|
||||
// error_ is written only on the main thread (processWalletSwitchRevert) and read by the (main-thread) UI.
|
||||
enum class WalletSwitchPhase : int { None = 0, Stopping, Starting, Reconnecting, Failed };
|
||||
std::atomic<int> wallet_switch_phase_{0};
|
||||
std::atomic<bool> wallet_switch_dialog_open_{false};
|
||||
std::string wallet_switch_error_;
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -504,7 +504,13 @@ void App::tryConnect()
|
||||
void App::onConnected()
|
||||
{
|
||||
state_.connected = true;
|
||||
wallet_switch_pending_confirm_.store(false); // a successful connect confirms the switch
|
||||
const bool completedSwitch = wallet_switch_pending_confirm_.exchange(false); // this connect confirms a switch
|
||||
// A successful connect completes a switch — close/clear the progress modal (kept open through the
|
||||
// stop → start → reconnect sequence, or already hidden via "continue in background").
|
||||
if (completedSwitch || wallet_switch_dialog_open_.load()) {
|
||||
wallet_switch_dialog_open_.store(false);
|
||||
wallet_switch_phase_.store(static_cast<int>(WalletSwitchPhase::None));
|
||||
}
|
||||
state_.daemon_initializing = false; // RPC is answering now; clear the "initializing" overlay
|
||||
daemon_wait_attempts_ = 0; // re-arm the port-busy / start-failure notifications
|
||||
daemon_start_error_shown_ = false;
|
||||
@@ -1104,9 +1110,11 @@ void App::switchToWallet(const std::string& walletFile, bool stopDaemonConfirmed
|
||||
// can't surface (or sign outgoing chat) under the new wallet. It re-provisions from the new
|
||||
// wallet's seed once it connects. onDisconnected alone doesn't cover chat.
|
||||
resetChatSession();
|
||||
ui::Notifications::instance().info(
|
||||
"Switching wallet — stopping the node and restarting on the new wallet. "
|
||||
"A graceful shutdown can take up to a minute.", 60.0f);
|
||||
// Open the switch progress modal — it stays up through stop → wait-for-exit → start → reconnect and
|
||||
// auto-closes when the new node connects (onConnected). The worker advances the phase from here.
|
||||
wallet_switch_error_.clear();
|
||||
wallet_switch_phase_.store(static_cast<int>(WalletSwitchPhase::Stopping));
|
||||
wallet_switch_dialog_open_.store(true);
|
||||
|
||||
async_tasks_.submit("Switch wallet", [this, needRescan](const util::AsyncTaskManager::Token&) {
|
||||
bool ok = false;
|
||||
@@ -1126,6 +1134,7 @@ void App::switchToWallet(const std::string& walletFile, bool stopDaemonConfirmed
|
||||
} else {
|
||||
// The relaunch is ours — clear the adopted-external latch so the fresh process is treated
|
||||
// as owned (stop/isRunning/exit behave normally for the next switch and app exit).
|
||||
wallet_switch_phase_.store(static_cast<int>(WalletSwitchPhase::Starting));
|
||||
if (daemon_controller_) daemon_controller_->clearExternalDaemonDetected();
|
||||
if (needRescan && daemon_controller_) daemon_controller_->setRescanOnNextStart(true);
|
||||
ok = shutting_down_ ? true : startEmbeddedDaemon();
|
||||
@@ -1135,6 +1144,8 @@ void App::switchToWallet(const std::string& walletFile, bool stopDaemonConfirmed
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1500));
|
||||
if (!isEmbeddedDaemonRunning()) ok = false;
|
||||
}
|
||||
// Process is up and staying up — now waiting for RPC to answer (onConnected closes the modal).
|
||||
if (ok) wallet_switch_phase_.store(static_cast<int>(WalletSwitchPhase::Reconnecting));
|
||||
}
|
||||
} catch (...) {
|
||||
ok = false; // a throw during stop/start is a failed switch — revert rather than wedge
|
||||
@@ -1167,12 +1178,17 @@ void App::processWalletSwitchRevert()
|
||||
if (daemon_controller_) daemon_controller_->resetCrashCount();
|
||||
// Accurate reason: "port wouldn't free" (the running node kept the connection) vs. a genuinely bad
|
||||
// target wallet. The former is the adopted-external-daemon case; the latter a daemon that exited in init.
|
||||
if (switch_stop_failed_.exchange(false)) {
|
||||
ui::Notifications::instance().error(
|
||||
"Couldn't switch wallets — the running node didn't release its connection in time. "
|
||||
"It's still on the previous wallet.");
|
||||
const std::string reason = switch_stop_failed_.exchange(false)
|
||||
? std::string("Couldn't switch wallets — the running node didn't release its connection in time. "
|
||||
"It's still on the previous wallet.")
|
||||
: std::string("Couldn't open that wallet — reverted to the previous one.");
|
||||
// Surface it in the progress modal if it's still up (keep it open on Failed until the user closes);
|
||||
// otherwise (the user chose "continue in background") fall back to a toast.
|
||||
if (wallet_switch_dialog_open_.load()) {
|
||||
wallet_switch_error_ = reason;
|
||||
wallet_switch_phase_.store(static_cast<int>(WalletSwitchPhase::Failed));
|
||||
} else {
|
||||
ui::Notifications::instance().error("Couldn't open that wallet — reverted to the previous one.");
|
||||
ui::Notifications::instance().error(reason);
|
||||
}
|
||||
daemon_restarting_ = false; // the connect loop now restarts the previous wallet's daemon
|
||||
}
|
||||
|
||||
@@ -379,6 +379,11 @@ void App::buildSweepCatalog()
|
||||
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(); });
|
||||
add("modal-switch-progress", ui::NavPage::Settings,
|
||||
[](App& a) { a.wallet_switch_phase_.store(static_cast<int>(App::WalletSwitchPhase::Stopping));
|
||||
a.wallet_switch_dialog_open_.store(true); },
|
||||
[](App& a) { a.wallet_switch_dialog_open_.store(false);
|
||||
a.wallet_switch_phase_.store(static_cast<int>(App::WalletSwitchPhase::None)); });
|
||||
// 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(); },
|
||||
|
||||
@@ -1966,6 +1966,7 @@ int main(int argc, char* argv[])
|
||||
bool backdropNeedsFrames = (backdrop_active || app.getGradientTexture() != 0)
|
||||
&& !opaqueBackground;
|
||||
bool animating = app.isShuttingDown()
|
||||
|| app.isWalletSwitchInProgress()
|
||||
|| backdropNeedsFrames
|
||||
|| app.hasTransactionSendProgress()
|
||||
|| app.isTransactionRefreshInProgress()
|
||||
|
||||
@@ -1045,6 +1045,14 @@ void I18n::loadBuiltinEnglish()
|
||||
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";
|
||||
// Wallet-switch live progress modal
|
||||
strings_["switch_progress_title"] = "Switching wallet";
|
||||
strings_["switch_progress_failed_title"] = "Wallet switch failed";
|
||||
strings_["switch_progress_stopping"] = "Stopping the current node";
|
||||
strings_["switch_progress_starting"] = "Starting the node on the new wallet";
|
||||
strings_["switch_progress_reconnecting"] = "Reconnecting";
|
||||
strings_["switch_progress_hint"] = "A graceful shutdown can take up to a minute.";
|
||||
strings_["switch_progress_background"] = "Continue in background";
|
||||
|
||||
// Receive Tab
|
||||
strings_["receiving_addresses"] = "Your Receiving Addresses";
|
||||
|
||||
Reference in New Issue
Block a user