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

@@ -12,9 +12,8 @@
#include "../../app.h"
#include "../../util/i18n.h"
#include "../material/draw_helpers.h"
#include "../material/typography.h"
#include "../material/project_icons.h"
#include "../theme.h"
#include "../../embedded/IconsMaterialDesign.h"
#include "imgui.h"
namespace dragonx {
@@ -22,8 +21,6 @@ namespace ui {
class AddressLabelDialog {
public:
static constexpr const char* kPickaxeGlyph = "\xEE\x80\x81";
static bool drawIconByName(ImDrawList* dl,
const std::string& name,
ImVec2 center,
@@ -31,23 +28,7 @@ public:
ImU32 color,
ImFont* iconFont,
float iconFontSize) {
if (name == "pickaxe") {
ImFont* pickaxeFont = material::Typography::instance().pickaxeFontForSize(iconFontSize);
if (!pickaxeFont) return false;
ImVec2 iSz = pickaxeFont->CalcTextSizeA(iconFontSize, 1000.0f, 0.0f, kPickaxeGlyph);
dl->AddText(pickaxeFont, iconFontSize,
ImVec2(center.x - iSz.x * 0.5f, center.y - iSz.y * 0.5f), color, kPickaxeGlyph);
return true;
}
const char* glyph = iconGlyphForName(name);
if (!glyph || !iconFont) return false;
ImVec2 iSz = iconFont->CalcTextSizeA(iconFontSize, 1000.0f, 0.0f, glyph);
dl->AddText(iconFont, iconFontSize,
ImVec2(center.x - iSz.x * 0.5f, center.y - iSz.y * 0.5f), color, glyph);
return true;
return material::project_icons::drawByName(dl, name, center, color, iconFont, iconFontSize);
}
static void show(App* app, const std::string& address, bool isZ) {
@@ -64,8 +45,8 @@ public:
s_label[sizeof(s_label) - 1] = '\0';
std::string existingIcon = app->getAddressIcon(address);
for (int i = 0; i < kIconCount; ++i) {
if (kIconNames[i] == existingIcon) {
for (int i = 0; i < material::project_icons::walletIconCount(); ++i) {
if (material::project_icons::walletIconName(i) == existingIcon) {
s_selectedIcon = i;
break;
}
@@ -134,13 +115,14 @@ public:
// Build filtered index list
std::vector<int> visible;
visible.reserve(kIconCount);
visible.reserve(material::project_icons::walletIconCount());
{
// Simple case-insensitive substring match on icon name
std::string needle(s_iconSearch);
for (char& c : needle) c = (char)std::tolower((unsigned char)c);
for (int i = 0; i < kIconCount; ++i) {
if (needle.empty() || std::strstr(kIconNames[i], needle.c_str()) != nullptr)
for (int i = 0; i < material::project_icons::walletIconCount(); ++i) {
const char* iconName = material::project_icons::walletIconName(i);
if (needle.empty() || std::strstr(iconName, needle.c_str()) != nullptr)
visible.push_back(i);
}
}
@@ -162,6 +144,7 @@ public:
int col = 0;
for (int vi = 0; vi < (int)visible.size(); ++vi) {
int i = visible[vi];
const char* iconName = material::project_icons::walletIconName(i);
if (col != 0) ImGui::SameLine(0, 4.0f * dp);
ImVec2 pos = ImGui::GetCursorScreenPos();
ImVec2 mn = pos;
@@ -179,7 +162,7 @@ public:
// Icon centered in cell
drawIconByName(dl,
kIconNames[i],
iconName,
ImVec2(mn.x + cellSz * 0.5f, mn.y + cellSz * 0.5f),
iconFsz,
sel ? Primary() : (hov ? OnSurface() : OnSurfaceMedium()),
@@ -189,7 +172,7 @@ public:
ImGui::PushID(i);
ImGui::InvisibleButton("##icon", ImVec2(cellSz, cellSz));
if (ImGui::IsItemClicked()) s_selectedIcon = i;
if (hov) ImGui::SetTooltip("%s", kIconNames[i]);
if (hov) ImGui::SetTooltip("%s", iconName);
ImGui::PopID();
col = (col + 1) % cols;
@@ -239,7 +222,7 @@ public:
// Apply changes
s_app->setAddressLabel(s_address, s_label);
if (s_selectedIcon >= 0)
s_app->setAddressIcon(s_address, kIconNames[s_selectedIcon]);
s_app->setAddressIcon(s_address, material::project_icons::walletIconName(s_selectedIcon));
else
s_app->setAddressIcon(s_address, "");
s_open = false;
@@ -260,123 +243,10 @@ private:
static inline int s_selectedIcon = -1;
static inline char s_iconSearch[64] = {};
// Icon palette — wallet-relevant Material Design icons
static inline const char* kIconNames[] = {
// Finance / Crypto
"savings", "account_balance", "account_balance_wallet", "wallet",
"payments", "credit_card", "local_atm", "diamond",
"attach_money", "currency_bitcoin", "currency_exchange", "balance",
"calculate", "trending_up", "euro", "leaderboard",
"paid", "sell", "receipt", "percent",
"price_change", "price_check", "toll", "money",
// Charts / Analytics
"show_chart", "candlestick_chart", "bar_chart", "pie_chart",
"area_chart", "stacked_bar_chart", "waterfall_chart", "scatter_plot",
"query_stats", "speed", "donut_large",
// Mining / Tools
"pickaxe",
"hardware", "construction", "handyman", "build",
"carpenter", "plumbing", "home_repair_service", "precision_manufacturing",
"factory", "warehouse", "inventory", "recycling",
"oil_barrel", "offline_bolt", "thunderstorm", "terminal",
"storage", "memory", "developer_board",
// Security / Auth
"shield", "security", "lock", "swap_horiz",
"verified", "verified_user", "key", "badge",
// Commerce / Business
"store", "storefront", "shopping_bag", "business",
"work", "real_estate_agent", "gavel", "local_shipping",
// Home / Property
"home", "apartment", "cottage", "landscape",
// People / Identity
"account_circle", "face", "manage_accounts", "groups", "mood",
// Travel / Transport
"rocket_launch", "flight", "directions_car", "travel_explore",
"explore", "location_on", "map", "luggage", "anchor",
// Nature / Outdoors
"public", "language", "forest", "park",
"water_drop", "beach_access", "energy_savings_leaf", "solar_power",
// Social / Lifestyle
"favorite", "star", "celebration", "casino",
"auto_awesome", "emoji_events", "military_tech", "flag",
// Tech / Science
"bolt", "tungsten", "lightbulb", "insights",
"hub", "token", "electric_bolt", "science", "biotech",
// Organisation
"category", "label", "school", "local_hospital", "local_florist",
// Food / Drink
"coffee", "restaurant", "wine_bar", "liquor",
"outdoor_grill", "nightlife", "sports_bar",
// Recreation / Health
"pets", "fitness_center", "spa", "self_improvement",
"psychology", "sports_soccer", "sports_esports",
"hiking", "palette", "museum", "church", "surfing",
// Community
"redeem", "handshake", "healing", "volunteer",
"stadium", "temple_buddhist", "theater_comedy", "watch",
};
static inline const char* kIconGlyphs[] = {
// Finance / Crypto
ICON_MD_SAVINGS, ICON_MD_ACCOUNT_BALANCE, ICON_MD_ACCOUNT_BALANCE_WALLET, ICON_MD_WALLET,
ICON_MD_PAYMENTS, ICON_MD_CREDIT_CARD, ICON_MD_LOCAL_ATM, ICON_MD_DIAMOND,
ICON_MD_ATTACH_MONEY, ICON_MD_CURRENCY_BITCOIN, ICON_MD_CURRENCY_EXCHANGE, ICON_MD_BALANCE,
ICON_MD_CALCULATE, ICON_MD_TRENDING_UP, ICON_MD_EURO, ICON_MD_LEADERBOARD,
ICON_MD_PAID, ICON_MD_SELL, ICON_MD_RECEIPT, ICON_MD_PERCENT,
ICON_MD_PRICE_CHANGE, ICON_MD_PRICE_CHECK, ICON_MD_TOLL, ICON_MD_MONEY,
// Charts / Analytics
ICON_MD_SHOW_CHART, ICON_MD_CANDLESTICK_CHART, ICON_MD_BAR_CHART, ICON_MD_PIE_CHART,
ICON_MD_AREA_CHART, ICON_MD_STACKED_BAR_CHART, ICON_MD_WATERFALL_CHART, ICON_MD_SCATTER_PLOT,
ICON_MD_QUERY_STATS, ICON_MD_SPEED, ICON_MD_DONUT_LARGE,
// Mining / Tools
nullptr,
ICON_MD_HARDWARE, ICON_MD_CONSTRUCTION, ICON_MD_HANDYMAN, ICON_MD_BUILD,
ICON_MD_CARPENTER, ICON_MD_PLUMBING, ICON_MD_HOME_REPAIR_SERVICE, ICON_MD_PRECISION_MANUFACTURING,
ICON_MD_FACTORY, ICON_MD_WAREHOUSE, ICON_MD_INVENTORY, ICON_MD_RECYCLING,
ICON_MD_OIL_BARREL, ICON_MD_OFFLINE_BOLT, ICON_MD_THUNDERSTORM, ICON_MD_TERMINAL,
ICON_MD_STORAGE, ICON_MD_MEMORY, ICON_MD_DEVELOPER_BOARD,
// Security / Auth
ICON_MD_SHIELD, ICON_MD_SECURITY, ICON_MD_LOCK, ICON_MD_SWAP_HORIZ,
ICON_MD_VERIFIED, ICON_MD_VERIFIED_USER, ICON_MD_KEY, ICON_MD_BADGE,
// Commerce / Business
ICON_MD_STORE, ICON_MD_STOREFRONT, ICON_MD_SHOPPING_BAG, ICON_MD_BUSINESS,
ICON_MD_WORK, ICON_MD_REAL_ESTATE_AGENT, ICON_MD_GAVEL, ICON_MD_LOCAL_SHIPPING,
// Home / Property
ICON_MD_HOME, ICON_MD_APARTMENT, ICON_MD_COTTAGE, ICON_MD_LANDSCAPE,
// People / Identity
ICON_MD_ACCOUNT_CIRCLE, ICON_MD_FACE, ICON_MD_MANAGE_ACCOUNTS, ICON_MD_GROUPS, ICON_MD_MOOD,
// Travel / Transport
ICON_MD_ROCKET_LAUNCH, ICON_MD_FLIGHT, ICON_MD_DIRECTIONS_CAR, ICON_MD_TRAVEL_EXPLORE,
ICON_MD_EXPLORE, ICON_MD_LOCATION_ON, ICON_MD_MAP, ICON_MD_LUGGAGE, ICON_MD_ANCHOR,
// Nature / Outdoors
ICON_MD_PUBLIC, ICON_MD_LANGUAGE, ICON_MD_FOREST, ICON_MD_PARK,
ICON_MD_WATER_DROP, ICON_MD_BEACH_ACCESS, ICON_MD_ENERGY_SAVINGS_LEAF, ICON_MD_SOLAR_POWER,
// Social / Lifestyle
ICON_MD_FAVORITE, ICON_MD_STAR, ICON_MD_CELEBRATION, ICON_MD_CASINO,
ICON_MD_AUTO_AWESOME, ICON_MD_EMOJI_EVENTS, ICON_MD_MILITARY_TECH, ICON_MD_FLAG,
// Tech / Science
ICON_MD_BOLT, ICON_MD_TUNGSTEN, ICON_MD_LIGHTBULB, ICON_MD_INSIGHTS,
ICON_MD_HUB, ICON_MD_TOKEN, ICON_MD_ELECTRIC_BOLT, ICON_MD_SCIENCE, ICON_MD_BIOTECH,
// Organisation
ICON_MD_CATEGORY, ICON_MD_LABEL, ICON_MD_SCHOOL, ICON_MD_LOCAL_HOSPITAL, ICON_MD_LOCAL_FLORIST,
// Food / Drink
ICON_MD_LOCAL_CAFE, ICON_MD_RESTAURANT, ICON_MD_WINE_BAR, ICON_MD_LIQUOR,
ICON_MD_OUTDOOR_GRILL, ICON_MD_NIGHTLIFE, ICON_MD_SPORTS_BAR,
// Recreation / Health
ICON_MD_PETS, ICON_MD_FITNESS_CENTER, ICON_MD_SPA, ICON_MD_SELF_IMPROVEMENT,
ICON_MD_PSYCHOLOGY, ICON_MD_SPORTS_SOCCER, ICON_MD_SPORTS_ESPORTS,
ICON_MD_HIKING, ICON_MD_PALETTE, ICON_MD_MUSEUM, ICON_MD_CHURCH, ICON_MD_SURFING,
// Community
ICON_MD_REDEEM, ICON_MD_HANDSHAKE, ICON_MD_HEALING, ICON_MD_VOLUNTEER_ACTIVISM,
ICON_MD_STADIUM, ICON_MD_TEMPLE_BUDDHIST, ICON_MD_THEATER_COMEDY, ICON_MD_WATCH,
};
static constexpr int kIconCount = static_cast<int>(std::size(kIconNames));
public:
// Expose for the address list to look up icon glyphs by name
static const char* iconGlyphForName(const std::string& name) {
for (int i = 0; i < kIconCount; ++i)
if (kIconNames[i] == name) return kIconGlyphs[i];
return nullptr;
return material::project_icons::glyphForName(name);
}
};