Refactor app services and stabilize refresh/UI flows

- Add refresh scheduler and network refresh service boundaries for typed
  refresh results, ordered RPC collectors, applicators, and price parsing.
- Add daemon lifecycle and wallet security workflow helpers while preserving
  App-owned command RPC, decrypt, cancellation, and UI handoff behavior.
- Split balance, console, mining, amount formatting, and async task logic into
  focused modules with expanded Phase 4 test coverage.
- Fix market price loading by triggering price refresh immediately, avoiding
  queue-pressure drops, tracking loading/error state, and adding translations.
- Polish send, explorer, peers, settings, theme/schema, and related tab UI.
- Replace checked-in generated language headers with build-generated resources.
- Document the cleanup audit, UI static-state guidance, and architecture updates.
This commit is contained in:
2026-04-29 12:47:57 -05:00
parent ee8a08e569
commit 9edab31728
95 changed files with 8776 additions and 37563 deletions

View File

@@ -21,6 +21,7 @@
#include <algorithm>
#include <unordered_map>
#include <cmath>
#include <string>
namespace dragonx {
namespace ui {
@@ -916,7 +917,7 @@ inline bool DrawDialogTitleBar(const char* title, bool* p_open, ImU32 accent_col
// Similar to the shutdown screen pattern but for interactive dialogs.
inline bool BeginOverlayDialog(const char* title, bool* p_open, float cardWidth = 460.0f, float scrimOpacity = 0.92f,
float cardBottomViewportRatio = 0.85f)
float cardBottomViewportRatio = 0.85f, const char* idSuffix = nullptr)
{
MarkOverlayDialogActive();
@@ -933,7 +934,16 @@ inline bool BeginOverlayDialog(const char* title, bool* p_open, float cardWidth
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
bool opened = ImGui::Begin("##OverlayScrim", nullptr,
std::string scrimId = "##OverlayScrim";
std::string childId = "##OverlayDialogContent";
if (idSuffix && idSuffix[0] != '\0') {
scrimId += "_";
scrimId += idSuffix;
childId += "_";
childId += idSuffix;
}
bool opened = ImGui::Begin(scrimId.c_str(), nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoNav |
@@ -983,7 +993,7 @@ inline bool BeginOverlayDialog(const char* title, bool* p_open, float cardWidth
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(28, 24));
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0, 0, 0, 0)); // Transparent - glass already drawn
bool childVisible = ImGui::BeginChild("##OverlayDialogContent",
bool childVisible = ImGui::BeginChild(childId.c_str(),
ImVec2(cardWidth, 0), // 0 height = auto-size
ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysUseWindowPadding,
ImGuiWindowFlags_NoScrollbar);
@@ -1007,6 +1017,23 @@ inline void EndOverlayDialog()
ImGui::PopStyleColor(); // WindowBg scrim
}
inline void PlaceOverlayDialogActions(float totalWidth)
{
float rowStartX = ImGui::GetCursorPosX();
float contentW = ImGui::GetContentRegionAvail().x;
ImGui::SetCursorPosX(rowStartX + std::max(0.0f, (contentW - totalWidth) * 0.5f));
}
inline void BeginOverlayDialogFooter(float totalActionWidth, bool drawSeparator = true)
{
ImGui::Spacing();
if (drawSeparator) {
ImGui::Separator();
ImGui::Spacing();
}
PlaceOverlayDialogActions(totalActionWidth);
}
} // namespace material
} // namespace ui
} // namespace dragonx

View File

@@ -0,0 +1,115 @@
// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
#pragma once
#include "typography.h"
#include "../../embedded/IconsMaterialDesign.h"
#include "imgui.h"
#include <string>
namespace dragonx {
namespace ui {
namespace material {
namespace project_icons {
struct ProjectIcon {
const char* name;
const char* glyph;
};
inline constexpr const char* kPickaxeName = "pickaxe";
inline constexpr const char* kPickaxeGlyph = "\xEE\x80\x81";
inline constexpr ProjectIcon kWalletIcons[] = {
{"savings", ICON_MD_SAVINGS}, {"account_balance", ICON_MD_ACCOUNT_BALANCE}, {"account_balance_wallet", ICON_MD_ACCOUNT_BALANCE_WALLET}, {"wallet", ICON_MD_WALLET},
{"payments", ICON_MD_PAYMENTS}, {"credit_card", ICON_MD_CREDIT_CARD}, {"local_atm", ICON_MD_LOCAL_ATM}, {"diamond", ICON_MD_DIAMOND},
{"attach_money", ICON_MD_ATTACH_MONEY}, {"currency_bitcoin", ICON_MD_CURRENCY_BITCOIN}, {"currency_exchange", ICON_MD_CURRENCY_EXCHANGE}, {"balance", ICON_MD_BALANCE},
{"calculate", ICON_MD_CALCULATE}, {"trending_up", ICON_MD_TRENDING_UP}, {"euro", ICON_MD_EURO}, {"leaderboard", ICON_MD_LEADERBOARD},
{"paid", ICON_MD_PAID}, {"sell", ICON_MD_SELL}, {"receipt", ICON_MD_RECEIPT}, {"percent", ICON_MD_PERCENT},
{"price_change", ICON_MD_PRICE_CHANGE}, {"price_check", ICON_MD_PRICE_CHECK}, {"toll", ICON_MD_TOLL}, {"money", ICON_MD_MONEY},
{"show_chart", ICON_MD_SHOW_CHART}, {"candlestick_chart", ICON_MD_CANDLESTICK_CHART}, {"bar_chart", ICON_MD_BAR_CHART}, {"pie_chart", ICON_MD_PIE_CHART},
{"area_chart", ICON_MD_AREA_CHART}, {"stacked_bar_chart", ICON_MD_STACKED_BAR_CHART}, {"waterfall_chart", ICON_MD_WATERFALL_CHART}, {"scatter_plot", ICON_MD_SCATTER_PLOT},
{"query_stats", ICON_MD_QUERY_STATS}, {"speed", ICON_MD_SPEED}, {"donut_large", ICON_MD_DONUT_LARGE},
{kPickaxeName, nullptr},
{"hardware", ICON_MD_HARDWARE}, {"construction", ICON_MD_CONSTRUCTION}, {"handyman", ICON_MD_HANDYMAN}, {"build", ICON_MD_BUILD},
{"carpenter", ICON_MD_CARPENTER}, {"plumbing", ICON_MD_PLUMBING}, {"home_repair_service", ICON_MD_HOME_REPAIR_SERVICE}, {"precision_manufacturing", ICON_MD_PRECISION_MANUFACTURING},
{"factory", ICON_MD_FACTORY}, {"warehouse", ICON_MD_WAREHOUSE}, {"inventory", ICON_MD_INVENTORY}, {"recycling", ICON_MD_RECYCLING},
{"oil_barrel", ICON_MD_OIL_BARREL}, {"offline_bolt", ICON_MD_OFFLINE_BOLT}, {"thunderstorm", ICON_MD_THUNDERSTORM}, {"terminal", ICON_MD_TERMINAL},
{"storage", ICON_MD_STORAGE}, {"memory", ICON_MD_MEMORY}, {"developer_board", ICON_MD_DEVELOPER_BOARD},
{"shield", ICON_MD_SHIELD}, {"security", ICON_MD_SECURITY}, {"lock", ICON_MD_LOCK}, {"swap_horiz", ICON_MD_SWAP_HORIZ},
{"verified", ICON_MD_VERIFIED}, {"verified_user", ICON_MD_VERIFIED_USER}, {"key", ICON_MD_KEY}, {"badge", ICON_MD_BADGE},
{"store", ICON_MD_STORE}, {"storefront", ICON_MD_STOREFRONT}, {"shopping_bag", ICON_MD_SHOPPING_BAG}, {"business", ICON_MD_BUSINESS},
{"work", ICON_MD_WORK}, {"real_estate_agent", ICON_MD_REAL_ESTATE_AGENT}, {"gavel", ICON_MD_GAVEL}, {"local_shipping", ICON_MD_LOCAL_SHIPPING},
{"home", ICON_MD_HOME}, {"apartment", ICON_MD_APARTMENT}, {"cottage", ICON_MD_COTTAGE}, {"landscape", ICON_MD_LANDSCAPE},
{"account_circle", ICON_MD_ACCOUNT_CIRCLE}, {"face", ICON_MD_FACE}, {"manage_accounts", ICON_MD_MANAGE_ACCOUNTS}, {"groups", ICON_MD_GROUPS}, {"mood", ICON_MD_MOOD},
{"rocket_launch", ICON_MD_ROCKET_LAUNCH}, {"flight", ICON_MD_FLIGHT}, {"directions_car", ICON_MD_DIRECTIONS_CAR}, {"travel_explore", ICON_MD_TRAVEL_EXPLORE},
{"explore", ICON_MD_EXPLORE}, {"location_on", ICON_MD_LOCATION_ON}, {"map", ICON_MD_MAP}, {"luggage", ICON_MD_LUGGAGE}, {"anchor", ICON_MD_ANCHOR},
{"public", ICON_MD_PUBLIC}, {"language", ICON_MD_LANGUAGE}, {"forest", ICON_MD_FOREST}, {"park", ICON_MD_PARK},
{"water_drop", ICON_MD_WATER_DROP}, {"beach_access", ICON_MD_BEACH_ACCESS}, {"energy_savings_leaf", ICON_MD_ENERGY_SAVINGS_LEAF}, {"solar_power", ICON_MD_SOLAR_POWER},
{"favorite", ICON_MD_FAVORITE}, {"star", ICON_MD_STAR}, {"celebration", ICON_MD_CELEBRATION}, {"casino", ICON_MD_CASINO},
{"auto_awesome", ICON_MD_AUTO_AWESOME}, {"emoji_events", ICON_MD_EMOJI_EVENTS}, {"military_tech", ICON_MD_MILITARY_TECH}, {"flag", ICON_MD_FLAG},
{"bolt", ICON_MD_BOLT}, {"tungsten", ICON_MD_TUNGSTEN}, {"lightbulb", ICON_MD_LIGHTBULB}, {"insights", ICON_MD_INSIGHTS},
{"hub", ICON_MD_HUB}, {"token", ICON_MD_TOKEN}, {"electric_bolt", ICON_MD_ELECTRIC_BOLT}, {"science", ICON_MD_SCIENCE}, {"biotech", ICON_MD_BIOTECH},
{"category", ICON_MD_CATEGORY}, {"label", ICON_MD_LABEL}, {"school", ICON_MD_SCHOOL}, {"local_hospital", ICON_MD_LOCAL_HOSPITAL}, {"local_florist", ICON_MD_LOCAL_FLORIST},
{"coffee", ICON_MD_LOCAL_CAFE}, {"restaurant", ICON_MD_RESTAURANT}, {"wine_bar", ICON_MD_WINE_BAR}, {"liquor", ICON_MD_LIQUOR},
{"outdoor_grill", ICON_MD_OUTDOOR_GRILL}, {"nightlife", ICON_MD_NIGHTLIFE}, {"sports_bar", ICON_MD_SPORTS_BAR},
{"pets", ICON_MD_PETS}, {"fitness_center", ICON_MD_FITNESS_CENTER}, {"spa", ICON_MD_SPA}, {"self_improvement", ICON_MD_SELF_IMPROVEMENT},
{"psychology", ICON_MD_PSYCHOLOGY}, {"sports_soccer", ICON_MD_SPORTS_SOCCER}, {"sports_esports", ICON_MD_SPORTS_ESPORTS},
{"hiking", ICON_MD_HIKING}, {"palette", ICON_MD_PALETTE}, {"museum", ICON_MD_MUSEUM}, {"church", ICON_MD_CHURCH}, {"surfing", ICON_MD_SURFING},
{"redeem", ICON_MD_REDEEM}, {"handshake", ICON_MD_HANDSHAKE}, {"healing", ICON_MD_HEALING}, {"volunteer", ICON_MD_VOLUNTEER_ACTIVISM},
{"stadium", ICON_MD_STADIUM}, {"temple_buddhist", ICON_MD_TEMPLE_BUDDHIST}, {"theater_comedy", ICON_MD_THEATER_COMEDY}, {"watch", ICON_MD_WATCH},
};
inline int walletIconCount()
{
return static_cast<int>(sizeof(kWalletIcons) / sizeof(kWalletIcons[0]));
}
inline const char* walletIconName(int index)
{
return (index >= 0 && index < walletIconCount()) ? kWalletIcons[index].name : "";
}
inline const char* glyphForName(const std::string& name)
{
for (const auto& icon : kWalletIcons) {
if (name == icon.name) return icon.glyph;
}
return nullptr;
}
inline bool drawByName(ImDrawList* drawList,
const std::string& name,
ImVec2 center,
ImU32 color,
ImFont* iconFont,
float iconFontSize)
{
if (!drawList) return false;
if (name == kPickaxeName) {
ImFont* pickaxeFont = Typography::instance().pickaxeFontForSize(iconFontSize);
if (!pickaxeFont) return false;
ImVec2 iconSize = pickaxeFont->CalcTextSizeA(iconFontSize, 1000.0f, 0.0f, kPickaxeGlyph);
drawList->AddText(pickaxeFont, iconFontSize,
ImVec2(center.x - iconSize.x * 0.5f, center.y - iconSize.y * 0.5f), color, kPickaxeGlyph);
return true;
}
const char* glyph = glyphForName(name);
if (!glyph || !iconFont) return false;
ImVec2 iconSize = iconFont->CalcTextSizeA(iconFontSize, 1000.0f, 0.0f, glyph);
drawList->AddText(iconFont, iconFontSize,
ImVec2(center.x - iconSize.x * 0.5f, center.y - iconSize.y * 0.5f), color, glyph);
return true;
}
} // namespace project_icons
} // namespace material
} // namespace ui
} // namespace dragonx