Compare commits
2
Commits
2fdf33f99b
...
ee6e99965c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee6e99965c | ||
|
|
b3e09a3314 |
@@ -1225,6 +1225,7 @@ pill-margin-y = { size = 3.0 }
|
||||
pill-rounding = { size = 12.0 }
|
||||
padding-x = { size = 12.0 }
|
||||
glass-fill-alpha = { size = 18.0 }
|
||||
accent-fill-alpha = { size = 0.22 } # severity-tint fill opacity (fraction of the accent alpha)
|
||||
glass-border-alpha = { size = 35.0 }
|
||||
progress-bar-height = { size = 2.0 }
|
||||
duration-info = { size = 1.0 }
|
||||
|
||||
@@ -1927,6 +1927,7 @@ private:
|
||||
// First-run wizard state
|
||||
WizardPhase wizard_phase_ = WizardPhase::None;
|
||||
std::unique_ptr<util::Bootstrap> bootstrap_;
|
||||
std::string wizard_bootstrap_url_; // source the wizard last started ("" = default); Retry keeps it
|
||||
bool bootstrap_downloading_ = false; // true while settings bootstrap dialog is active
|
||||
std::string wizard_pending_passphrase_; // held until daemon connects
|
||||
std::string wizard_saved_passphrase_; // held until PinSetup completes/skipped
|
||||
|
||||
+63
-28
@@ -800,21 +800,22 @@ void App::renderFirstRunWizard() {
|
||||
// Daemon status indicator
|
||||
{
|
||||
bool daemonUp = isEmbeddedDaemonRunning();
|
||||
const std::string& dStatus = getDaemonStatus();
|
||||
ImU32 dotCol = daemonUp ? IM_COL32(76, 175, 80, 200) // green
|
||||
: IM_COL32(120, 120, 120, 160); // gray
|
||||
if (dStatus.find("Stopping") != std::string::npos)
|
||||
dotCol = IM_COL32(255, 167, 38, 200); // orange
|
||||
// Key the dot + label off the daemon state ENUM, not an English substring of the status
|
||||
// text — the substring match silently failed under every non-English language.
|
||||
const bool daemonStopping = daemon_controller_ &&
|
||||
daemon_controller_->state() == daemon::EmbeddedDaemon::State::Stopping;
|
||||
ImU32 dotCol = daemonStopping ? IM_COL32(255, 167, 38, 230) // orange
|
||||
: daemonUp ? IM_COL32(76, 175, 80, 230) // green
|
||||
: IM_COL32(120, 120, 120, 180); // gray
|
||||
float dotR = 3.5f * dp;
|
||||
dl->AddCircleFilled(ImVec2(cx + dotR, cy + captionFont->LegacySize * 0.5f),
|
||||
dotR, dotCol);
|
||||
const char* label = daemonUp ? (dStatus.find("Stopping") != std::string::npos
|
||||
? TR("bootstrap_daemon_stopping")
|
||||
: TR("bootstrap_daemon_running"))
|
||||
: TR("bootstrap_daemon_stopped");
|
||||
const char* label = daemonStopping ? TR("bootstrap_daemon_stopping")
|
||||
: daemonUp ? TR("bootstrap_daemon_running")
|
||||
: TR("bootstrap_daemon_stopped");
|
||||
dl->AddText(captionFont, captionFont->LegacySize,
|
||||
ImVec2(cx + dotR * 2.0f + 6.0f * dp, cy),
|
||||
(dimCol & 0x00FFFFFF) | IM_COL32(0,0,0,140), label);
|
||||
(dimCol & 0x00FFFFFF) | IM_COL32(0,0,0,220), label);
|
||||
cy += captionFont->LegacySize + 6.0f * dp;
|
||||
}
|
||||
|
||||
@@ -862,14 +863,19 @@ void App::renderFirstRunWizard() {
|
||||
ui::material::Error(), TR("wiz_download_failed"));
|
||||
cy += bodyFont->LegacySize + 8.0f * dp;
|
||||
|
||||
// Wrap the (often long) error within the card instead of letting it clip off the edge.
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), textCol,
|
||||
errMsg.c_str());
|
||||
cy += captionFont->LegacySize + 16.0f * dp;
|
||||
errMsg.c_str(), nullptr, contentW);
|
||||
ImVec2 es = captionFont->CalcTextSizeA(captionFont->LegacySize, FLT_MAX, contentW, errMsg.c_str());
|
||||
cy += es.y + 16.0f * dp;
|
||||
|
||||
// Retry / Skip
|
||||
// Retry / (Try mirror) / Skip. Offer the mirror unless the last attempt already used it.
|
||||
const std::string mirrorUrl = std::string(util::Bootstrap::kMirrorUrl) + "/" + util::Bootstrap::kZipName;
|
||||
const bool showMirror = (wizard_bootstrap_url_ != mirrorUrl);
|
||||
float btnW2 = 120.0f * dp;
|
||||
float btnH2 = 40.0f * dp;
|
||||
float totalBW = btnW2 * 2 + 12.0f * dp;
|
||||
const int nBtn = showMirror ? 3 : 2;
|
||||
float totalBW = btnW2 * nBtn + 12.0f * dp * (nBtn - 1);
|
||||
float bx = rightX + (colW - totalBW) * 0.5f;
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(bx, cy));
|
||||
@@ -883,14 +889,33 @@ void App::renderFirstRunWizard() {
|
||||
stopDaemonForBootstrap();
|
||||
bootstrap_ = std::make_unique<util::Bootstrap>();
|
||||
std::string dataDir = util::Platform::getDragonXDataDir();
|
||||
bootstrap_->start(dataDir);
|
||||
if (wizard_bootstrap_url_.empty()) bootstrap_->start(dataDir);
|
||||
else bootstrap_->start(dataDir, wizard_bootstrap_url_); // keep the user's chosen source
|
||||
wizard_phase_ = WizardPhase::BootstrapInProgress;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(bx + btnW2 + 12.0f * dp, cy));
|
||||
float nextBx = bx + btnW2 + 12.0f * dp;
|
||||
if (showMirror) {
|
||||
ImGui::SetCursorScreenPos(ImVec2(nextBx, cy));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp);
|
||||
ImGui::BeginDisabled(!supportsFullNodeLifecycleActions());
|
||||
if (ui::material::TactileButton(TR("bootstrap_try_mirror"), ImVec2(btnW2, btnH2)) && !refuseIfNodeBusy()) {
|
||||
stopDaemonForBootstrap();
|
||||
bootstrap_ = std::make_unique<util::Bootstrap>();
|
||||
bootstrap_->start(util::Platform::getDragonXDataDir(), mirrorUrl);
|
||||
wizard_bootstrap_url_ = mirrorUrl;
|
||||
wizard_phase_ = WizardPhase::BootstrapInProgress;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered()) ui::material::Tooltip(TR("bootstrap_use_mirror_tooltip"));
|
||||
ImGui::PopStyleVar();
|
||||
nextBx += btnW2 + 12.0f * dp;
|
||||
}
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(nextBx, cy));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp);
|
||||
if (ui::material::TactileButton(TR("wiz_skip"), ImVec2(btnW2, btnH2))) {
|
||||
wizard_phase_ = WizardPhase::EncryptOffer;
|
||||
@@ -1014,6 +1039,15 @@ void App::renderFirstRunWizard() {
|
||||
cy += bsSize.y + 8.0f * dp;
|
||||
}
|
||||
|
||||
// Honest size/time hint (no number is known until the download HEADs the remote file).
|
||||
{
|
||||
const char* hintText = TR("wiz_bootstrap_size_hint");
|
||||
ImVec2 hs = captionFont->CalcTextSizeA(captionFont->LegacySize, FLT_MAX, contentW, hintText);
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy),
|
||||
(dimCol & 0x00FFFFFF) | IM_COL32(0, 0, 0, 200), hintText, nullptr, contentW);
|
||||
cy += hs.y + 8.0f * dp;
|
||||
}
|
||||
|
||||
// Trust warning
|
||||
{
|
||||
float warnOpacity = S.drawElement("screens.first-run", "trust-warning").opacity;
|
||||
@@ -1031,21 +1065,20 @@ void App::renderFirstRunWizard() {
|
||||
// Daemon status indicator (subtle, before buttons)
|
||||
{
|
||||
bool daemonUp = isEmbeddedDaemonRunning();
|
||||
const std::string& dStatus = getDaemonStatus();
|
||||
ImU32 dotCol = daemonUp ? IM_COL32(76, 175, 80, 200)
|
||||
: IM_COL32(120, 120, 120, 160);
|
||||
if (dStatus.find("Stopping") != std::string::npos)
|
||||
dotCol = IM_COL32(255, 167, 38, 200);
|
||||
const bool daemonStopping = daemon_controller_ &&
|
||||
daemon_controller_->state() == daemon::EmbeddedDaemon::State::Stopping;
|
||||
ImU32 dotCol = daemonStopping ? IM_COL32(255, 167, 38, 230)
|
||||
: daemonUp ? IM_COL32(76, 175, 80, 230)
|
||||
: IM_COL32(120, 120, 120, 180);
|
||||
float dotR = 3.5f * dp;
|
||||
dl->AddCircleFilled(ImVec2(cx + dotR, cy + captionFont->LegacySize * 0.5f),
|
||||
dotR, dotCol);
|
||||
const char* label = daemonUp ? (dStatus.find("Stopping") != std::string::npos
|
||||
? TR("bootstrap_daemon_stopping")
|
||||
: TR("bootstrap_daemon_running"))
|
||||
: TR("bootstrap_daemon_stopped");
|
||||
const char* label = daemonStopping ? TR("bootstrap_daemon_stopping")
|
||||
: daemonUp ? TR("bootstrap_daemon_running")
|
||||
: TR("bootstrap_daemon_stopped");
|
||||
dl->AddText(captionFont, captionFont->LegacySize,
|
||||
ImVec2(cx + dotR * 2.0f + 6.0f * dp, cy),
|
||||
(dimCol & 0x00FFFFFF) | IM_COL32(0,0,0,140), label);
|
||||
(dimCol & 0x00FFFFFF) | IM_COL32(0,0,0,220), label);
|
||||
cy += captionFont->LegacySize + 10.0f * dp;
|
||||
}
|
||||
|
||||
@@ -1071,6 +1104,7 @@ void App::renderFirstRunWizard() {
|
||||
bootstrap_ = std::make_unique<util::Bootstrap>();
|
||||
std::string dataDir = util::Platform::getDragonXDataDir();
|
||||
bootstrap_->start(dataDir);
|
||||
wizard_bootstrap_url_.clear(); // default source; Retry won't force the mirror
|
||||
wizard_phase_ = WizardPhase::BootstrapInProgress;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
@@ -1084,17 +1118,18 @@ void App::renderFirstRunWizard() {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(ui::material::OnSurface()));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp);
|
||||
ImGui::BeginDisabled(!supportsFullNodeLifecycleActions());
|
||||
if (ui::material::TactileButton(TR("bootstrap_mirror"), ImVec2(mirrorW, btnH2)) && !refuseIfNodeBusy()) {
|
||||
if (ui::material::TactileButton(TR("bootstrap_use_mirror"), ImVec2(mirrorW, btnH2)) && !refuseIfNodeBusy()) {
|
||||
stopDaemonForBootstrap();
|
||||
bootstrap_ = std::make_unique<util::Bootstrap>();
|
||||
std::string dataDir = util::Platform::getDragonXDataDir();
|
||||
std::string mirrorUrl = std::string(util::Bootstrap::kMirrorUrl) + "/" + util::Bootstrap::kZipName;
|
||||
bootstrap_->start(dataDir, mirrorUrl);
|
||||
wizard_bootstrap_url_ = mirrorUrl; // Retry on the failed card keeps this source
|
||||
wizard_phase_ = WizardPhase::BootstrapInProgress;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ui::material::Tooltip(TR("bootstrap_mirror_tooltip"));
|
||||
ui::material::Tooltip(TR("bootstrap_use_mirror_tooltip"));
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
@@ -123,9 +123,16 @@ void Notifications::render()
|
||||
glassSpec.borderWidth = 1.0f;
|
||||
material::DrawGlassPanel(dl, pMin, pMax, glassSpec);
|
||||
|
||||
// Severity-tinted fill so the toast differs by more than hue (helps when color alone is hard to read).
|
||||
{
|
||||
ImVec4 tint = accent_color; // already premultiplied by fade `alpha`
|
||||
tint.w *= nde("accent-fill-alpha", 0.22f);
|
||||
dl->AddRectFilled(pMin, pMax, ImGui::ColorConvertFloat4ToU32(tint), pillRounding);
|
||||
}
|
||||
|
||||
// Colored accent border on top of the glass border for type indication
|
||||
ImU32 accentCol = ImGui::ColorConvertFloat4ToU32(accent_color);
|
||||
dl->AddRect(pMin, pMax, accentCol, pillRounding, 0, 1.0f);
|
||||
dl->AddRect(pMin, pMax, accentCol, pillRounding, 0, 1.5f * dp);
|
||||
|
||||
// Progress bar at bottom of pill (accent-colored), clipped to pill rounded
|
||||
// corners. Draw a full-pill-size rounded rect and clip it to just the
|
||||
|
||||
@@ -526,6 +526,7 @@ static void RenderBalanceClassic(App* app)
|
||||
}
|
||||
|
||||
// Pending badge (top-right corner) — labeled so an unconfirmed "+X" doesn't read as a mystery.
|
||||
bool pendingChipHovered = false;
|
||||
if (state.unconfirmed_balance > 0.0) {
|
||||
snprintf(buf, sizeof(buf), TR("baltab_pending_chip"), state.unconfirmed_balance);
|
||||
ImVec2 ts = capFont->CalcTextSizeA(
|
||||
@@ -540,7 +541,7 @@ static void RenderBalanceClassic(App* app)
|
||||
dl->AddText(capFont, capFont->LegacySize,
|
||||
ImVec2(bMin.x + bp, bMin.y + bp * 0.5f),
|
||||
amberCol, buf);
|
||||
if (material::IsRectHovered(bMin, bMax)) material::Tooltip("%s", TR("baltab_pending_tip"));
|
||||
if (material::IsRectHovered(bMin, bMax)) { pendingChipHovered = true; material::Tooltip("%s", TR("baltab_pending_tip")); }
|
||||
}
|
||||
|
||||
// Hover glow + click to Receive
|
||||
@@ -548,6 +549,7 @@ static void RenderBalanceClassic(App* app)
|
||||
dl->AddRect(cMin, cMax, IM_COL32(255, 255, 255, (int)S.drawElement("tabs.balance", "hover-glow-alpha").sizeOr(40.0f)),
|
||||
cardSpec.rounding, 0, S.drawElement("tabs.balance", "hover-glow-thickness").sizeOr(1.5f) * dp);
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
if (!pendingChipHovered) material::Tooltip("%s", TR("shielded_card_tooltip"));
|
||||
if (ImGui::IsMouseClicked(0))
|
||||
app->setCurrentPage(NavPage::Receive);
|
||||
}
|
||||
|
||||
@@ -283,13 +283,17 @@ private:
|
||||
|
||||
Type().textColored(TypeStyle::H6, Success(), TR("bootstrap_success"));
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped("%s", TR("bootstrap_success_desc"));
|
||||
// For the embedded daemon, tell the user the node will start (and offer the button) instead of
|
||||
// instructing them to start it by hand. External-daemon users keep the manual-start wording.
|
||||
const bool embedded = s_app->isUsingEmbeddedDaemon();
|
||||
ImGui::TextWrapped("%s", TR(embedded ? "bootstrap_success_desc_start" : "bootstrap_success_desc"));
|
||||
ImGui::Spacing();
|
||||
ImGui::Spacing();
|
||||
|
||||
float btnW = 140.0f * dp;
|
||||
if (s_wasDaemonRunning) {
|
||||
if (TactileButton(TR("bootstrap_restart_daemon"), ImVec2(btnW, 0))) {
|
||||
float btnW = 150.0f * dp;
|
||||
if (s_wasDaemonRunning || embedded) {
|
||||
if (TactileButton(s_wasDaemonRunning ? TR("bootstrap_restart_daemon") : TR("bootstrap_start_daemon"),
|
||||
ImVec2(btnW, 0))) {
|
||||
s_app->setBootstrapDownloading(false);
|
||||
s_app->startEmbeddedDaemon();
|
||||
s_open = false;
|
||||
|
||||
@@ -899,6 +899,9 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["bootstrap_trust_warning"] = "Only use bootstrap.dragonx.is or bootstrap2.dragonx.is. Using files from untrusted sources could compromise your node.";
|
||||
strings_["bootstrap_mirror"] = "Mirror";
|
||||
strings_["bootstrap_mirror_tooltip"] = "Download from mirror (bootstrap2.dragonx.is).\nUse this if the main download is slow or failing.";
|
||||
strings_["bootstrap_use_mirror"] = "Use mirror";
|
||||
strings_["bootstrap_use_mirror_tooltip"] = "Download the same bootstrap from the backup server (bootstrap2.dragonx.is) instead of the main one (bootstrap.dragonx.is).\nChoose this if the main download is slow or failing.";
|
||||
strings_["wiz_bootstrap_size_hint"] = "This is a large download. The exact size and a time estimate appear once it starts, and it can be resumed if interrupted.";
|
||||
strings_["bootstrap_downloading"] = "Downloading bootstrap...";
|
||||
strings_["bootstrap_verifying"] = "Verifying checksums...";
|
||||
strings_["bootstrap_extracting"] = "Extracting blockchain data...";
|
||||
@@ -909,6 +912,8 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["bootstrap_success"] = "Bootstrap Complete";
|
||||
strings_["bootstrap_success_desc"] = "Blockchain data has been extracted successfully. Start the daemon to begin syncing from the bootstrap point.";
|
||||
strings_["bootstrap_restart_daemon"] = "Restart Daemon";
|
||||
strings_["bootstrap_start_daemon"] = "Start Daemon";
|
||||
strings_["bootstrap_success_desc_start"] = "Blockchain data was extracted successfully. Click Start Daemon to begin syncing from the bootstrap point.";
|
||||
strings_["bootstrap_failed"] = "Bootstrap Failed";
|
||||
strings_["tt_open_dir"] = "Click to open in file explorer";
|
||||
strings_["settings_open_data_dir"] = "Open data folder";
|
||||
@@ -2323,6 +2328,7 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["balance_mining_fmt"] = "Mining rewards: %.8f";
|
||||
strings_["mining_card_tooltip"] = "Solo-mining rewards arrive at your mining (transparent, R…) addresses. Those addresses can't receive payments. Rewards are shielded automatically once they mature.";
|
||||
strings_["mining_card_click_to_shield"] = "Click to shield matured rewards now.";
|
||||
strings_["shielded_card_tooltip"] = "Shielded (private) balance: funds held in z-addresses. Amounts, senders and recipients are hidden on the blockchain. Click to open Receive.";
|
||||
strings_["contact_chip_mining"] = "Mining only";
|
||||
strings_["failed_create_mining_address"] = "Failed to create mining address";
|
||||
strings_["new_mining_address_created"] = "New mining address created";
|
||||
|
||||
Reference in New Issue
Block a user