feat(network): a clear auth-failed node banner with a fix-it action
When the node rejects the wallet's RPC login (bad rpcuser/rpcpassword), the
status bar showed developer jargon ("Auth failed — check rpcuser/rpcpassword")
and the only action was Reconnect, which just repeats the failure. Now a
dedicated AuthFailed banner reason surfaces "Wallet can't sign in to the node"
with a plain-language detail and a real fix: Restart node for the embedded
daemon (regenerates its cookie creds) or Open config folder for an external one
(to fix DRAGONX.conf). Driven by a new rpc_auth_failed_ flag (set on the auth
path, cleared on each connect attempt), with evaluator unit tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+13
-3
@@ -2662,6 +2662,7 @@ void App::renderNodeStatusBanner()
|
||||
const NodeBusyText busy = nodeBusyText();
|
||||
in.node_busy = busy.busy;
|
||||
in.node_busy_detail = busy.compact; // one line; the overlay and the quit prompt explain more
|
||||
in.auth_failed = rpc_auth_failed_;
|
||||
|
||||
const ui::NodeBannerState banner = ui::evaluateNodeStatusBanner(in);
|
||||
if (!banner.show) return;
|
||||
@@ -2698,6 +2699,8 @@ void App::renderNodeStatusBanner()
|
||||
title = TR("node_banner_no_peers_title"); icon = ICON_MD_CLOUD_OFF; break;
|
||||
case ui::NodeBannerReason::NodeBusy:
|
||||
title = busy.title.c_str(); icon = ICON_MD_HOURGLASS_EMPTY; break;
|
||||
case ui::NodeBannerReason::AuthFailed:
|
||||
title = TR("node_banner_auth_title"); icon = ICON_MD_LOCK; break;
|
||||
case ui::NodeBannerReason::FullNodeOffline:
|
||||
default:
|
||||
title = TR("node_banner_offline_title"); icon = ICON_MD_CLOUD_OFF; break;
|
||||
@@ -2706,13 +2709,15 @@ void App::renderNodeStatusBanner()
|
||||
const char* actionLabel = nullptr;
|
||||
if (banner.action == ui::NodeBannerAction::Reconnect) actionLabel = TR("node_banner_reconnect");
|
||||
else if (banner.action == ui::NodeBannerAction::RestartNode) actionLabel = TR("node_banner_restart");
|
||||
else if (banner.action == ui::NodeBannerAction::OpenConfigFolder) actionLabel = TR("node_banner_open_config");
|
||||
|
||||
// The NoPeers case deliberately carries no passthrough detail (connection_status is just
|
||||
// "Connected" there, which reads as a contradiction under the "no peers" headline). Show a
|
||||
// translated, actionable hint instead; every other reason keeps its live status/error text.
|
||||
const std::string detailText = (banner.reason == ui::NodeBannerReason::NoPeers)
|
||||
? std::string(TR("node_banner_no_peers_detail"))
|
||||
: banner.detail;
|
||||
std::string detailText;
|
||||
if (banner.reason == ui::NodeBannerReason::NoPeers) detailText = TR("node_banner_no_peers_detail");
|
||||
else if (banner.reason == ui::NodeBannerReason::AuthFailed) detailText = TR("node_banner_auth_detail");
|
||||
else detailText = banner.detail;
|
||||
|
||||
const float padX = ui::Layout::spacingLg();
|
||||
|
||||
@@ -2801,6 +2806,11 @@ void App::renderNodeStatusBanner()
|
||||
if (m::TactileButton(actionLabel, ImVec2(btnW, btnH))) {
|
||||
if (banner.action == ui::NodeBannerAction::RestartNode) restartDaemon();
|
||||
else if (banner.action == ui::NodeBannerAction::Reconnect) tryConnect();
|
||||
else if (banner.action == ui::NodeBannerAction::OpenConfigFolder) {
|
||||
const std::string conf = rpc::Connection::getDefaultConfPath();
|
||||
util::Platform::openFolder(std::filesystem::path(conf).parent_path().string(),
|
||||
/*createIfMissing=*/false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user