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:
2026-07-15 20:56:53 -05:00
parent 02839dc415
commit 88091bd77c
14 changed files with 161 additions and 29 deletions

View File

@@ -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?",

View File

@@ -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?",

View File

@@ -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 ?",

View File

@@ -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": "実行中のノードを停止しますか?",

View File

@@ -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": "실행 중인 노드를 중지할까요?",

View File

@@ -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?",

View File

@@ -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": "Остановить запущенный узел?",

View File

@@ -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": "停止正在运行的节点?",