Compare commits
2 Commits
28b9e0dffb
...
dc4426810f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc4426810f | ||
|
|
915c1b4d23 |
2
build.sh
2
build.sh
@@ -20,7 +20,7 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
VERSION="1.1.1"
|
VERSION="1.2.0"
|
||||||
|
|
||||||
# ── Colours ──────────────────────────────────────────────────────────────────
|
# ── Colours ──────────────────────────────────────────────────────────────────
|
||||||
RED='\033[0;31m'
|
RED='\033[0;31m'
|
||||||
|
|||||||
@@ -654,6 +654,7 @@
|
|||||||
"show_qr_code": "QR-Code anzeigen",
|
"show_qr_code": "QR-Code anzeigen",
|
||||||
"showing_transactions": "Zeige %d–%d von %d Transaktionen (gesamt: %zu)",
|
"showing_transactions": "Zeige %d–%d von %d Transaktionen (gesamt: %zu)",
|
||||||
"simple_background": "Einfacher Hintergrund",
|
"simple_background": "Einfacher Hintergrund",
|
||||||
|
"slider_off": "Aus",
|
||||||
"start_mining": "Mining starten",
|
"start_mining": "Mining starten",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"stop_external": "Externen Daemon stoppen",
|
"stop_external": "Externen Daemon stoppen",
|
||||||
@@ -670,6 +671,12 @@
|
|||||||
"time_hours_ago": "vor %d Stunden",
|
"time_hours_ago": "vor %d Stunden",
|
||||||
"time_minutes_ago": "vor %d Minuten",
|
"time_minutes_ago": "vor %d Minuten",
|
||||||
"time_seconds_ago": "vor %d Sekunden",
|
"time_seconds_ago": "vor %d Sekunden",
|
||||||
|
"timeout_15min": "15 Min",
|
||||||
|
"timeout_1hour": "1 Stunde",
|
||||||
|
"timeout_1min": "1 Min",
|
||||||
|
"timeout_30min": "30 Min",
|
||||||
|
"timeout_5min": "5 Min",
|
||||||
|
"timeout_off": "Aus",
|
||||||
"to": "An",
|
"to": "An",
|
||||||
"to_upper": "AN",
|
"to_upper": "AN",
|
||||||
"tools": "WERKZEUGE",
|
"tools": "WERKZEUGE",
|
||||||
|
|||||||
@@ -654,6 +654,7 @@
|
|||||||
"show_qr_code": "Mostrar Código QR",
|
"show_qr_code": "Mostrar Código QR",
|
||||||
"showing_transactions": "Mostrando %d–%d de %d transacciones (total: %zu)",
|
"showing_transactions": "Mostrando %d–%d de %d transacciones (total: %zu)",
|
||||||
"simple_background": "Fondo simple",
|
"simple_background": "Fondo simple",
|
||||||
|
"slider_off": "Apagado",
|
||||||
"start_mining": "Iniciar Minería",
|
"start_mining": "Iniciar Minería",
|
||||||
"status": "Estado",
|
"status": "Estado",
|
||||||
"stop_external": "Detener daemon externo",
|
"stop_external": "Detener daemon externo",
|
||||||
@@ -670,6 +671,12 @@
|
|||||||
"time_hours_ago": "hace %d horas",
|
"time_hours_ago": "hace %d horas",
|
||||||
"time_minutes_ago": "hace %d minutos",
|
"time_minutes_ago": "hace %d minutos",
|
||||||
"time_seconds_ago": "hace %d segundos",
|
"time_seconds_ago": "hace %d segundos",
|
||||||
|
"timeout_15min": "15 min",
|
||||||
|
"timeout_1hour": "1 hora",
|
||||||
|
"timeout_1min": "1 min",
|
||||||
|
"timeout_30min": "30 min",
|
||||||
|
"timeout_5min": "5 min",
|
||||||
|
"timeout_off": "Apagado",
|
||||||
"to": "Para",
|
"to": "Para",
|
||||||
"to_upper": "PARA",
|
"to_upper": "PARA",
|
||||||
"tools": "HERRAMIENTAS",
|
"tools": "HERRAMIENTAS",
|
||||||
|
|||||||
@@ -654,6 +654,7 @@
|
|||||||
"show_qr_code": "Afficher le code QR",
|
"show_qr_code": "Afficher le code QR",
|
||||||
"showing_transactions": "Affichage %d–%d sur %d transactions (total : %zu)",
|
"showing_transactions": "Affichage %d–%d sur %d transactions (total : %zu)",
|
||||||
"simple_background": "Arrière-plan simple",
|
"simple_background": "Arrière-plan simple",
|
||||||
|
"slider_off": "Désactivé",
|
||||||
"start_mining": "Démarrer le minage",
|
"start_mining": "Démarrer le minage",
|
||||||
"status": "Statut",
|
"status": "Statut",
|
||||||
"stop_external": "Arrêter le daemon externe",
|
"stop_external": "Arrêter le daemon externe",
|
||||||
@@ -670,6 +671,12 @@
|
|||||||
"time_hours_ago": "il y a %d heures",
|
"time_hours_ago": "il y a %d heures",
|
||||||
"time_minutes_ago": "il y a %d minutes",
|
"time_minutes_ago": "il y a %d minutes",
|
||||||
"time_seconds_ago": "il y a %d secondes",
|
"time_seconds_ago": "il y a %d secondes",
|
||||||
|
"timeout_15min": "15 min",
|
||||||
|
"timeout_1hour": "1 heure",
|
||||||
|
"timeout_1min": "1 min",
|
||||||
|
"timeout_30min": "30 min",
|
||||||
|
"timeout_5min": "5 min",
|
||||||
|
"timeout_off": "Désactivé",
|
||||||
"to": "À",
|
"to": "À",
|
||||||
"to_upper": "À",
|
"to_upper": "À",
|
||||||
"tools": "OUTILS",
|
"tools": "OUTILS",
|
||||||
|
|||||||
@@ -654,6 +654,7 @@
|
|||||||
"show_qr_code": "QRコードを表示",
|
"show_qr_code": "QRコードを表示",
|
||||||
"showing_transactions": "%d–%d / %d 件の取引を表示中(合計:%zu)",
|
"showing_transactions": "%d–%d / %d 件の取引を表示中(合計:%zu)",
|
||||||
"simple_background": "シンプル背景",
|
"simple_background": "シンプル背景",
|
||||||
|
"slider_off": "オフ",
|
||||||
"start_mining": "マイニング開始",
|
"start_mining": "マイニング開始",
|
||||||
"status": "ステータス",
|
"status": "ステータス",
|
||||||
"stop_external": "外部デーモンを停止",
|
"stop_external": "外部デーモンを停止",
|
||||||
@@ -670,6 +671,12 @@
|
|||||||
"time_hours_ago": "%d時間前",
|
"time_hours_ago": "%d時間前",
|
||||||
"time_minutes_ago": "%d分前",
|
"time_minutes_ago": "%d分前",
|
||||||
"time_seconds_ago": "%d秒前",
|
"time_seconds_ago": "%d秒前",
|
||||||
|
"timeout_15min": "15分",
|
||||||
|
"timeout_1hour": "1時間",
|
||||||
|
"timeout_1min": "1分",
|
||||||
|
"timeout_30min": "30分",
|
||||||
|
"timeout_5min": "5分",
|
||||||
|
"timeout_off": "オフ",
|
||||||
"to": "宛先",
|
"to": "宛先",
|
||||||
"to_upper": "宛先",
|
"to_upper": "宛先",
|
||||||
"tools": "ツール",
|
"tools": "ツール",
|
||||||
|
|||||||
@@ -654,6 +654,7 @@
|
|||||||
"show_qr_code": "QR 코드 표시",
|
"show_qr_code": "QR 코드 표시",
|
||||||
"showing_transactions": "%d–%d / %d건의 거래 표시 중 (총: %zu)",
|
"showing_transactions": "%d–%d / %d건의 거래 표시 중 (총: %zu)",
|
||||||
"simple_background": "단순 배경",
|
"simple_background": "단순 배경",
|
||||||
|
"slider_off": "끔",
|
||||||
"start_mining": "채굴 시작",
|
"start_mining": "채굴 시작",
|
||||||
"status": "상태",
|
"status": "상태",
|
||||||
"stop_external": "외부 데몬 중지",
|
"stop_external": "외부 데몬 중지",
|
||||||
@@ -670,6 +671,12 @@
|
|||||||
"time_hours_ago": "%d시간 전",
|
"time_hours_ago": "%d시간 전",
|
||||||
"time_minutes_ago": "%d분 전",
|
"time_minutes_ago": "%d분 전",
|
||||||
"time_seconds_ago": "%d초 전",
|
"time_seconds_ago": "%d초 전",
|
||||||
|
"timeout_15min": "15분",
|
||||||
|
"timeout_1hour": "1시간",
|
||||||
|
"timeout_1min": "1분",
|
||||||
|
"timeout_30min": "30분",
|
||||||
|
"timeout_5min": "5분",
|
||||||
|
"timeout_off": "끔",
|
||||||
"to": "받는 곳",
|
"to": "받는 곳",
|
||||||
"to_upper": "받는 곳",
|
"to_upper": "받는 곳",
|
||||||
"tools": "도구",
|
"tools": "도구",
|
||||||
|
|||||||
@@ -654,6 +654,7 @@
|
|||||||
"show_qr_code": "Mostrar Código QR",
|
"show_qr_code": "Mostrar Código QR",
|
||||||
"showing_transactions": "Mostrando %d–%d de %d transações (total: %zu)",
|
"showing_transactions": "Mostrando %d–%d de %d transações (total: %zu)",
|
||||||
"simple_background": "Fundo simples",
|
"simple_background": "Fundo simples",
|
||||||
|
"slider_off": "Desligado",
|
||||||
"start_mining": "Iniciar Mineração",
|
"start_mining": "Iniciar Mineração",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"stop_external": "Parar daemon externo",
|
"stop_external": "Parar daemon externo",
|
||||||
@@ -670,6 +671,12 @@
|
|||||||
"time_hours_ago": "há %d horas",
|
"time_hours_ago": "há %d horas",
|
||||||
"time_minutes_ago": "há %d minutos",
|
"time_minutes_ago": "há %d minutos",
|
||||||
"time_seconds_ago": "há %d segundos",
|
"time_seconds_ago": "há %d segundos",
|
||||||
|
"timeout_15min": "15 min",
|
||||||
|
"timeout_1hour": "1 hora",
|
||||||
|
"timeout_1min": "1 min",
|
||||||
|
"timeout_30min": "30 min",
|
||||||
|
"timeout_5min": "5 min",
|
||||||
|
"timeout_off": "Desligado",
|
||||||
"to": "Para",
|
"to": "Para",
|
||||||
"to_upper": "PARA",
|
"to_upper": "PARA",
|
||||||
"tools": "FERRAMENTAS",
|
"tools": "FERRAMENTAS",
|
||||||
|
|||||||
@@ -654,6 +654,7 @@
|
|||||||
"show_qr_code": "Показать QR-код",
|
"show_qr_code": "Показать QR-код",
|
||||||
"showing_transactions": "Показано %d–%d из %d транзакций (всего: %zu)",
|
"showing_transactions": "Показано %d–%d из %d транзакций (всего: %zu)",
|
||||||
"simple_background": "Простой фон",
|
"simple_background": "Простой фон",
|
||||||
|
"slider_off": "Выкл",
|
||||||
"start_mining": "Начать майнинг",
|
"start_mining": "Начать майнинг",
|
||||||
"status": "Статус",
|
"status": "Статус",
|
||||||
"stop_external": "Остановить внешний daemon",
|
"stop_external": "Остановить внешний daemon",
|
||||||
@@ -670,6 +671,12 @@
|
|||||||
"time_hours_ago": "%d часов назад",
|
"time_hours_ago": "%d часов назад",
|
||||||
"time_minutes_ago": "%d минут назад",
|
"time_minutes_ago": "%d минут назад",
|
||||||
"time_seconds_ago": "%d секунд назад",
|
"time_seconds_ago": "%d секунд назад",
|
||||||
|
"timeout_15min": "15 мин",
|
||||||
|
"timeout_1hour": "1 час",
|
||||||
|
"timeout_1min": "1 мин",
|
||||||
|
"timeout_30min": "30 мин",
|
||||||
|
"timeout_5min": "5 мин",
|
||||||
|
"timeout_off": "Выкл",
|
||||||
"to": "Кому",
|
"to": "Кому",
|
||||||
"to_upper": "КОМУ",
|
"to_upper": "КОМУ",
|
||||||
"tools": "ИНСТРУМЕНТЫ",
|
"tools": "ИНСТРУМЕНТЫ",
|
||||||
|
|||||||
@@ -654,6 +654,7 @@
|
|||||||
"show_qr_code": "显示二维码",
|
"show_qr_code": "显示二维码",
|
||||||
"showing_transactions": "显示第 %d–%d 笔,共 %d 笔交易(总计:%zu)",
|
"showing_transactions": "显示第 %d–%d 笔,共 %d 笔交易(总计:%zu)",
|
||||||
"simple_background": "简单背景",
|
"simple_background": "简单背景",
|
||||||
|
"slider_off": "关闭",
|
||||||
"start_mining": "开始挖矿",
|
"start_mining": "开始挖矿",
|
||||||
"status": "状态",
|
"status": "状态",
|
||||||
"stop_external": "停止外部守护进程",
|
"stop_external": "停止外部守护进程",
|
||||||
@@ -670,6 +671,12 @@
|
|||||||
"time_hours_ago": "%d 小时前",
|
"time_hours_ago": "%d 小时前",
|
||||||
"time_minutes_ago": "%d 分钟前",
|
"time_minutes_ago": "%d 分钟前",
|
||||||
"time_seconds_ago": "%d 秒前",
|
"time_seconds_ago": "%d 秒前",
|
||||||
|
"timeout_15min": "15分钟",
|
||||||
|
"timeout_1hour": "1小时",
|
||||||
|
"timeout_1min": "1分钟",
|
||||||
|
"timeout_30min": "30分钟",
|
||||||
|
"timeout_5min": "5分钟",
|
||||||
|
"timeout_off": "关闭",
|
||||||
"to": "至",
|
"to": "至",
|
||||||
"to_upper": "至",
|
"to_upper": "至",
|
||||||
"tools": "工具",
|
"tools": "工具",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ set -e
|
|||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
BUILD_DIR="${SCRIPT_DIR}/build/linux"
|
BUILD_DIR="${SCRIPT_DIR}/build/linux"
|
||||||
APPDIR="${BUILD_DIR}/AppDir"
|
APPDIR="${BUILD_DIR}/AppDir"
|
||||||
VERSION="1.1.1"
|
VERSION="1.2.0"
|
||||||
|
|
||||||
# Colors
|
# Colors
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ set -e
|
|||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
BUILD_DIR="${SCRIPT_DIR}/build/linux"
|
BUILD_DIR="${SCRIPT_DIR}/build/linux"
|
||||||
VERSION="1.1.1"
|
VERSION="1.2.0"
|
||||||
|
|
||||||
# Colors for output
|
# Colors for output
|
||||||
RED='\033[0;31m'
|
RED='\033[0;31m'
|
||||||
|
|||||||
82
src/app.cpp
82
src/app.cpp
@@ -610,13 +610,16 @@ void App::update()
|
|||||||
core_timer_ = 0.0f;
|
core_timer_ = 0.0f;
|
||||||
refreshCoreData();
|
refreshCoreData();
|
||||||
}
|
}
|
||||||
if (transaction_timer_ >= active_tx_interval_) {
|
// Skip balance/tx/address refresh during warmup — RPC calls fail with -28
|
||||||
transaction_timer_ = 0.0f;
|
if (!state_.warming_up) {
|
||||||
refreshTransactionData();
|
if (transaction_timer_ >= active_tx_interval_) {
|
||||||
}
|
transaction_timer_ = 0.0f;
|
||||||
if (address_timer_ >= active_addr_interval_) {
|
refreshTransactionData();
|
||||||
address_timer_ = 0.0f;
|
}
|
||||||
refreshAddressData();
|
if (address_timer_ >= active_addr_interval_) {
|
||||||
|
address_timer_ = 0.0f;
|
||||||
|
refreshAddressData();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (peer_timer_ >= active_peer_interval_) {
|
if (peer_timer_ >= active_peer_interval_) {
|
||||||
peer_timer_ = 0.0f;
|
peer_timer_ = 0.0f;
|
||||||
@@ -1078,16 +1081,17 @@ void App::render()
|
|||||||
int fgVtxStart = fgDL->VtxBuffer.Size;
|
int fgVtxStart = fgDL->VtxBuffer.Size;
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
// Loading overlay — show only while daemon is not yet connected.
|
// Loading overlay — show only on tabs that truly need wallet data.
|
||||||
// Once connected, all tabs render normally; individual tabs show
|
// Tabs that work without full daemon connection:
|
||||||
// inline sync-progress indicators when the chain is still syncing.
|
// Console, Peers, Settings, Market, Mining
|
||||||
// Pages that remain accessible even without a connection:
|
// Tabs that need balance/transaction data (show overlay):
|
||||||
// Console, Peers, Settings
|
// Overview, Send, Receive, History
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
bool pageNeedsDaemon = (current_page_ != ui::NavPage::Console &&
|
bool pageNeedsWalletData = (current_page_ == ui::NavPage::Overview ||
|
||||||
current_page_ != ui::NavPage::Peers &&
|
current_page_ == ui::NavPage::Send ||
|
||||||
current_page_ != ui::NavPage::Settings);
|
current_page_ == ui::NavPage::Receive ||
|
||||||
bool daemonReady = state_.connected; // don't gate on sync state
|
current_page_ == ui::NavPage::History);
|
||||||
|
bool daemonReady = state_.connected && !state_.warming_up;
|
||||||
|
|
||||||
// Don't show lock screen while pool mining — xmrig runs independently
|
// Don't show lock screen while pool mining — xmrig runs independently
|
||||||
// of the wallet and locking would block the mining UI needlessly.
|
// of the wallet and locking would block the mining UI needlessly.
|
||||||
@@ -1095,7 +1099,12 @@ void App::render()
|
|||||||
if (state_.isLocked() && !poolMiningActive) {
|
if (state_.isLocked() && !poolMiningActive) {
|
||||||
// Lock screen — covers tab content just like the loading overlay
|
// Lock screen — covers tab content just like the loading overlay
|
||||||
renderLockScreen();
|
renderLockScreen();
|
||||||
} else if (pageNeedsDaemon && (!daemonReady || (state_.connected && !state_.encryption_state_known))) {
|
} else if (pageNeedsWalletData && state_.warming_up) {
|
||||||
|
// Daemon is reachable but still initializing — show warmup overlay
|
||||||
|
// only on wallet-data tabs. Other tabs render normally.
|
||||||
|
lock_screen_was_visible_ = false;
|
||||||
|
renderLoadingOverlay(contentH);
|
||||||
|
} else if (pageNeedsWalletData && (!daemonReady || (state_.connected && !state_.encryption_state_known))) {
|
||||||
// Track how long we've been waiting for encryption state
|
// Track how long we've been waiting for encryption state
|
||||||
if (state_.connected && !state_.encryption_state_known) {
|
if (state_.connected && !state_.encryption_state_known) {
|
||||||
encryption_check_timer_ += ImGui::GetIO().DeltaTime;
|
encryption_check_timer_ += ImGui::GetIO().DeltaTime;
|
||||||
@@ -1421,7 +1430,16 @@ void App::renderStatusBar()
|
|||||||
// Connection status
|
// Connection status
|
||||||
float dotOpacity = S.drawElement("components.status-bar", "connection-dot").opacity;
|
float dotOpacity = S.drawElement("components.status-bar", "connection-dot").opacity;
|
||||||
if (dotOpacity < 0.0f) dotOpacity = 1.0f;
|
if (dotOpacity < 0.0f) dotOpacity = 1.0f;
|
||||||
if (state_.connected) {
|
if (state_.warming_up) {
|
||||||
|
ImGui::PushFont(ui::material::Type().iconSmall());
|
||||||
|
ImGui::TextColored(ImVec4(1.0f, 0.7f, 0.0f, dotOpacity), ICON_MD_CIRCLE);
|
||||||
|
ImGui::PopFont();
|
||||||
|
ImGui::SameLine(0, sbIconTextGap);
|
||||||
|
// Show truncated warmup status (e.g. "Activating best chain... (Block 12345)")
|
||||||
|
const char* warmupText = state_.warmup_status.empty()
|
||||||
|
? "Warming up..." : state_.warmup_status.c_str();
|
||||||
|
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.0f, 1.0f), "%s", warmupText);
|
||||||
|
} else if (state_.connected) {
|
||||||
ImGui::PushFont(ui::material::Type().iconSmall());
|
ImGui::PushFont(ui::material::Type().iconSmall());
|
||||||
ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, dotOpacity), ICON_MD_CIRCLE);
|
ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, dotOpacity), ICON_MD_CIRCLE);
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
@@ -1467,7 +1485,11 @@ void App::renderStatusBar()
|
|||||||
static double s_prev_time = 0.0;
|
static double s_prev_time = 0.0;
|
||||||
static double s_blocks_per_sec = 0.0;
|
static double s_blocks_per_sec = 0.0;
|
||||||
double now = ImGui::GetTime();
|
double now = ImGui::GetTime();
|
||||||
if (s_prev_time > 0.0 && state_.sync.blocks > s_prev_blocks) {
|
if (s_prev_time == 0.0) {
|
||||||
|
// First sample — seed baseline, don't compute rate yet
|
||||||
|
s_prev_blocks = state_.sync.blocks;
|
||||||
|
s_prev_time = now;
|
||||||
|
} else if (state_.sync.blocks > s_prev_blocks) {
|
||||||
double dt = now - s_prev_time;
|
double dt = now - s_prev_time;
|
||||||
if (dt > 0.5) {
|
if (dt > 0.5) {
|
||||||
double raw = (state_.sync.blocks - s_prev_blocks) / dt;
|
double raw = (state_.sync.blocks - s_prev_blocks) / dt;
|
||||||
@@ -1478,9 +1500,10 @@ void App::renderStatusBar()
|
|||||||
s_prev_blocks = state_.sync.blocks;
|
s_prev_blocks = state_.sync.blocks;
|
||||||
s_prev_time = now;
|
s_prev_time = now;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (now - s_prev_time > 10.0) {
|
||||||
s_prev_blocks = state_.sync.blocks;
|
// No new blocks for 10s — decay the displayed rate
|
||||||
s_prev_time = now;
|
s_blocks_per_sec *= 0.5;
|
||||||
|
s_prev_time = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s_blocks_per_sec > 0.1) {
|
if (s_blocks_per_sec > 0.1) {
|
||||||
@@ -2769,6 +2792,21 @@ void App::renderLoadingOverlay(float contentH)
|
|||||||
curY += ts.y + gap;
|
curY += ts.y + gap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// 2b. Warmup description (subtitle explaining what's happening)
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
if (state_.warming_up && !state_.warmup_description.empty()) {
|
||||||
|
const char* descText = state_.warmup_description.c_str();
|
||||||
|
ImFont* capFont = Type().caption();
|
||||||
|
if (!capFont) capFont = ImGui::GetFont();
|
||||||
|
// Wrap to barW so long descriptions don't overflow
|
||||||
|
ImVec2 ts = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, barW, descText);
|
||||||
|
dl->AddText(capFont, capFont->LegacySize,
|
||||||
|
ImVec2(wp.x + cx - ts.x * 0.5f, curY),
|
||||||
|
IM_COL32(160, 160, 160, 200), descText, nullptr, barW);
|
||||||
|
curY += ts.y + gap;
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
// 3. Sync progress bar (if connected and syncing)
|
// 3. Sync progress bar (if connected and syncing)
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
|||||||
@@ -49,6 +49,40 @@ namespace dragonx {
|
|||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Warmup Message Translation
|
||||||
|
// Maps raw daemon RPC warmup messages to user-friendly text.
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
struct WarmupText {
|
||||||
|
const char* title;
|
||||||
|
const char* description;
|
||||||
|
};
|
||||||
|
|
||||||
|
static WarmupText translateWarmup(const std::string& raw)
|
||||||
|
{
|
||||||
|
if (raw.find("Loading") != std::string::npos)
|
||||||
|
return {"Loading blockchain data...",
|
||||||
|
"Reading the block database from disk. This may take a few minutes after updates."};
|
||||||
|
if (raw.find("Verifying") != std::string::npos)
|
||||||
|
return {"Verifying blockchain...",
|
||||||
|
"Checking recent blocks to make sure your chain data is valid."};
|
||||||
|
if (raw.find("Activating") != std::string::npos)
|
||||||
|
return {"Processing blocks...",
|
||||||
|
"Applying blocks to build the current chain state."};
|
||||||
|
if (raw.find("Rewinding") != std::string::npos)
|
||||||
|
return {"Reorganizing chain...",
|
||||||
|
"A chain reorganization was detected. Reverting to the correct chain."};
|
||||||
|
if (raw.find("Rescanning") != std::string::npos)
|
||||||
|
return {"Scanning for transactions...",
|
||||||
|
"Searching the blockchain for transactions belonging to your wallet. This can take a while."};
|
||||||
|
if (raw.find("Pruning") != std::string::npos)
|
||||||
|
return {"Optimizing storage...",
|
||||||
|
"Removing old block data to free up disk space."};
|
||||||
|
// Fallback: use the raw message
|
||||||
|
return {raw.c_str(), ""};
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Connection Management
|
// Connection Management
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -150,12 +184,31 @@ void App::tryConnect()
|
|||||||
worker_->post([this, config, daemonStarting, externalDetected, attempt]() -> rpc::RPCWorker::MainCb {
|
worker_->post([this, config, daemonStarting, externalDetected, attempt]() -> rpc::RPCWorker::MainCb {
|
||||||
bool connected = rpc_->connect(config.host, config.port, config.rpcuser, config.rpcpassword);
|
bool connected = rpc_->connect(config.host, config.port, config.rpcuser, config.rpcpassword);
|
||||||
std::string connectErr = rpc_->getLastConnectError();
|
std::string connectErr = rpc_->getLastConnectError();
|
||||||
|
bool warmingUp = rpc_->isWarmingUp();
|
||||||
|
std::string warmupStatus = rpc_->getWarmupStatus();
|
||||||
|
|
||||||
return [this, config, connected, daemonStarting, externalDetected, attempt, connectErr]() {
|
return [this, config, connected, warmingUp, warmupStatus, daemonStarting, externalDetected, attempt, connectErr]() {
|
||||||
if (connected) {
|
if (connected) {
|
||||||
VERBOSE_LOGF("[connect #%d] Connected successfully\n", attempt);
|
VERBOSE_LOGF("[connect #%d] Connected successfully%s\n", attempt,
|
||||||
|
warmingUp ? " (daemon warming up)" : "");
|
||||||
saved_config_ = config; // save for fast-lane connection
|
saved_config_ = config; // save for fast-lane connection
|
||||||
onConnected();
|
onConnected();
|
||||||
|
if (warmingUp) {
|
||||||
|
// Daemon is reachable and auth works, but RPC calls will
|
||||||
|
// fail until warmup completes. Set the warmup state so
|
||||||
|
// the UI shows status instead of a blocking overlay.
|
||||||
|
state_.warming_up = true;
|
||||||
|
auto wt = translateWarmup(warmupStatus);
|
||||||
|
state_.warmup_status = wt.title;
|
||||||
|
state_.warmup_description = wt.description;
|
||||||
|
// Append current block height from daemon output
|
||||||
|
if (embedded_daemon_) {
|
||||||
|
int h = embedded_daemon_->getLastBlockHeight();
|
||||||
|
if (h > 0)
|
||||||
|
state_.warmup_status += " (Block " + std::to_string(h) + ")";
|
||||||
|
}
|
||||||
|
connection_status_ = state_.warmup_status;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// HTTP 401 = authentication failure. The daemon is running
|
// HTTP 401 = authentication failure. The daemon is running
|
||||||
// but our rpcuser/rpcpassword don't match. Don't retry
|
// but our rpcuser/rpcpassword don't match. Don't retry
|
||||||
@@ -203,18 +256,6 @@ void App::tryConnect()
|
|||||||
"RPC authentication failed (HTTP 401). "
|
"RPC authentication failed (HTTP 401). "
|
||||||
"The rpcuser/rpcpassword in DRAGONX.conf don't match the running daemon. "
|
"The rpcuser/rpcpassword in DRAGONX.conf don't match the running daemon. "
|
||||||
"Restart the daemon or correct the credentials.");
|
"Restart the daemon or correct the credentials.");
|
||||||
} else if (connectErr.find("Loading") != std::string::npos ||
|
|
||||||
connectErr.find("Verifying") != std::string::npos ||
|
|
||||||
connectErr.find("Activating") != std::string::npos ||
|
|
||||||
connectErr.find("Rewinding") != std::string::npos ||
|
|
||||||
connectErr.find("Rescanning") != std::string::npos ||
|
|
||||||
connectErr.find("Pruning") != std::string::npos) {
|
|
||||||
// Daemon is reachable but still in warmup (Loading block index, etc.)
|
|
||||||
// Check this BEFORE daemonStarting so the actual warmup status is shown.
|
|
||||||
state_.connected = false;
|
|
||||||
connection_status_ = connectErr;
|
|
||||||
VERBOSE_LOGF("[connect #%d] Daemon warmup: %s\n", attempt, connectErr.c_str());
|
|
||||||
core_timer_ = CORE_INTERVAL_DEFAULT - 1.0f;
|
|
||||||
} else if (daemonStarting) {
|
} else if (daemonStarting) {
|
||||||
state_.connected = false;
|
state_.connected = false;
|
||||||
// Show the actual RPC error alongside the waiting message so
|
// Show the actual RPC error alongside the waiting message so
|
||||||
@@ -228,6 +269,15 @@ void App::tryConnect()
|
|||||||
attempt, connectErr.c_str());
|
attempt, connectErr.c_str());
|
||||||
core_timer_ = CORE_INTERVAL_DEFAULT - 1.0f;
|
core_timer_ = CORE_INTERVAL_DEFAULT - 1.0f;
|
||||||
} else if (externalDetected) {
|
} else if (externalDetected) {
|
||||||
|
state_.connected = false;
|
||||||
|
if (!connectErr.empty()) {
|
||||||
|
connection_status_ = "Connecting to daemon — " + connectErr;
|
||||||
|
} else {
|
||||||
|
connection_status_ = "Connecting to external daemon...";
|
||||||
|
}
|
||||||
|
VERBOSE_LOGF("[connect #%d] External daemon detected but RPC failed (%s), will retry...\n",
|
||||||
|
attempt, connectErr.c_str());
|
||||||
|
core_timer_ = CORE_INTERVAL_DEFAULT - 1.0f;
|
||||||
} else {
|
} else {
|
||||||
onDisconnected("Connection failed");
|
onDisconnected("Connection failed");
|
||||||
VERBOSE_LOGF("[connect #%d] RPC connection failed — no daemon starting, no external detected\n", attempt);
|
VERBOSE_LOGF("[connect #%d] RPC connection failed — no daemon starting, no external detected\n", attempt);
|
||||||
@@ -376,6 +426,8 @@ void App::onConnected()
|
|||||||
void App::onDisconnected(const std::string& reason)
|
void App::onDisconnected(const std::string& reason)
|
||||||
{
|
{
|
||||||
state_.connected = false;
|
state_.connected = false;
|
||||||
|
state_.warming_up = false;
|
||||||
|
state_.warmup_status.clear();
|
||||||
state_.clear();
|
state_.clear();
|
||||||
connection_status_ = reason;
|
connection_status_ = reason;
|
||||||
|
|
||||||
@@ -490,6 +542,13 @@ void App::refreshData()
|
|||||||
{
|
{
|
||||||
if (!state_.connected || !rpc_ || !worker_) return;
|
if (!state_.connected || !rpc_ || !worker_) return;
|
||||||
|
|
||||||
|
// During warmup, only poll for warmup completion via refreshCoreData.
|
||||||
|
// Other RPC calls (balance, addresses, transactions) will fail with -28.
|
||||||
|
if (state_.warming_up) {
|
||||||
|
refreshCoreData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Dispatch each category independently — results trickle into the UI
|
// Dispatch each category independently — results trickle into the UI
|
||||||
// as each completes, rather than waiting for the slowest phase.
|
// as each completes, rather than waiting for the slowest phase.
|
||||||
refreshCoreData();
|
refreshCoreData();
|
||||||
@@ -518,6 +577,60 @@ void App::refreshCoreData()
|
|||||||
{
|
{
|
||||||
if (!state_.connected) return;
|
if (!state_.connected) return;
|
||||||
|
|
||||||
|
// During warmup, poll getinfo to detect when warmup ends.
|
||||||
|
// Most RPC calls (balance, blockchain info) will fail with -28 during warmup.
|
||||||
|
if (state_.warming_up) {
|
||||||
|
if (core_refresh_in_progress_.exchange(true)) return;
|
||||||
|
worker_->post([this]() -> rpc::RPCWorker::MainCb {
|
||||||
|
json info;
|
||||||
|
bool ok = false;
|
||||||
|
std::string errMsg;
|
||||||
|
try {
|
||||||
|
info = rpc_->call("getinfo");
|
||||||
|
ok = true;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
errMsg = e.what();
|
||||||
|
}
|
||||||
|
return [this, info, ok, errMsg]() {
|
||||||
|
if (ok) {
|
||||||
|
// Warmup finished — daemon is fully ready
|
||||||
|
state_.warming_up = false;
|
||||||
|
state_.warmup_status.clear();
|
||||||
|
state_.warmup_description.clear();
|
||||||
|
connection_status_ = "Connected";
|
||||||
|
VERBOSE_LOGF("[warmup] Daemon ready, warmup complete\n");
|
||||||
|
// Parse initial info
|
||||||
|
try {
|
||||||
|
if (info.contains("version"))
|
||||||
|
state_.daemon_version = info["version"].get<int>();
|
||||||
|
if (info.contains("blocks"))
|
||||||
|
state_.sync.blocks = info["blocks"].get<int>();
|
||||||
|
if (info.contains("longestchain")) {
|
||||||
|
int lc = info["longestchain"].get<int>();
|
||||||
|
if (lc > 0) state_.longestchain = lc;
|
||||||
|
}
|
||||||
|
} catch (...) {}
|
||||||
|
// Trigger full data refresh now that daemon is ready
|
||||||
|
refreshData();
|
||||||
|
} else {
|
||||||
|
// Still warming up — update status
|
||||||
|
auto wt = translateWarmup(errMsg);
|
||||||
|
state_.warmup_status = wt.title;
|
||||||
|
state_.warmup_description = wt.description;
|
||||||
|
if (embedded_daemon_) {
|
||||||
|
int h = embedded_daemon_->getLastBlockHeight();
|
||||||
|
if (h > 0)
|
||||||
|
state_.warmup_status += " (Block " + std::to_string(h) + ")";
|
||||||
|
}
|
||||||
|
connection_status_ = state_.warmup_status;
|
||||||
|
VERBOSE_LOGF("[warmup] Still warming up: %s\n", errMsg.c_str());
|
||||||
|
}
|
||||||
|
core_refresh_in_progress_.store(false, std::memory_order_release);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Use fast-lane on Overview for snappier balance updates
|
// Use fast-lane on Overview for snappier balance updates
|
||||||
bool useFast = (current_page_ == ui::NavPage::Overview);
|
bool useFast = (current_page_ == ui::NavPage::Overview);
|
||||||
auto* w = useFast && fast_worker_ && fast_worker_->isRunning()
|
auto* w = useFast && fast_worker_ && fast_worker_->isRunning()
|
||||||
|
|||||||
@@ -116,6 +116,26 @@ public:
|
|||||||
* @brief Get last N lines of daemon output (thread-safe snapshot)
|
* @brief Get last N lines of daemon output (thread-safe snapshot)
|
||||||
*/
|
*/
|
||||||
std::vector<std::string> getRecentLines(int maxLines = 8) const;
|
std::vector<std::string> getRecentLines(int maxLines = 8) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Extract the latest block height from daemon output (thread-safe).
|
||||||
|
* Parses the last "height=N" from UpdateTip lines without copying
|
||||||
|
* the entire output buffer. Returns -1 if no UpdateTip found.
|
||||||
|
*/
|
||||||
|
int getLastBlockHeight() const {
|
||||||
|
std::lock_guard<std::mutex> lk(output_mutex_);
|
||||||
|
// Search backwards from the end for "height="
|
||||||
|
size_t pos = process_output_.rfind("height=");
|
||||||
|
if (pos == std::string::npos) return -1;
|
||||||
|
pos += 7; // skip "height="
|
||||||
|
int h = 0;
|
||||||
|
for (size_t i = pos; i < process_output_.size(); ++i) {
|
||||||
|
char c = process_output_[i];
|
||||||
|
if (c >= '0' && c <= '9') h = h * 10 + (c - '0');
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
return h > 0 ? h : -1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Whether start() detected an existing daemon on the RPC port.
|
* @brief Whether start() detected an existing daemon on the RPC port.
|
||||||
|
|||||||
@@ -180,6 +180,9 @@ struct PoolMiningState {
|
|||||||
struct WalletState {
|
struct WalletState {
|
||||||
// Connection
|
// Connection
|
||||||
bool connected = false;
|
bool connected = false;
|
||||||
|
bool warming_up = false; // daemon reachable but in RPC warmup (error -28)
|
||||||
|
std::string warmup_status; // user-friendly title, e.g. "Processing blocks..."
|
||||||
|
std::string warmup_description; // subtitle explaining the stage
|
||||||
int daemon_version = 0;
|
int daemon_version = 0;
|
||||||
std::string daemon_subversion;
|
std::string daemon_subversion;
|
||||||
int protocol_version = 0;
|
int protocol_version = 0;
|
||||||
@@ -250,6 +253,9 @@ struct WalletState {
|
|||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
connected = false;
|
connected = false;
|
||||||
|
warming_up = false;
|
||||||
|
warmup_status.clear();
|
||||||
|
warmup_description.clear();
|
||||||
daemon_version = 0;
|
daemon_version = 0;
|
||||||
daemon_subversion.clear();
|
daemon_subversion.clear();
|
||||||
protocol_version = 0;
|
protocol_version = 0;
|
||||||
|
|||||||
@@ -2736,56 +2736,73 @@ unsigned char res_lang_de_json[] = {
|
|||||||
0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x3a, 0x20, 0x22, 0x45, 0x69,
|
0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x3a, 0x20, 0x22, 0x45, 0x69,
|
||||||
0x6e, 0x66, 0x61, 0x63, 0x68, 0x65, 0x72, 0x20, 0x48, 0x69, 0x6e, 0x74,
|
0x6e, 0x66, 0x61, 0x63, 0x68, 0x65, 0x72, 0x20, 0x48, 0x69, 0x6e, 0x74,
|
||||||
0x65, 0x72, 0x67, 0x72, 0x75, 0x6e, 0x64, 0x22, 0x2c, 0x0a, 0x20, 0x20,
|
0x65, 0x72, 0x67, 0x72, 0x75, 0x6e, 0x64, 0x22, 0x2c, 0x0a, 0x20, 0x20,
|
||||||
0x20, 0x20, 0x22, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6d, 0x69, 0x6e,
|
0x20, 0x20, 0x22, 0x73, 0x6c, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6f, 0x66,
|
||||||
0x69, 0x6e, 0x67, 0x22, 0x3a, 0x20, 0x22, 0x4d, 0x69, 0x6e, 0x69, 0x6e,
|
0x66, 0x22, 0x3a, 0x20, 0x22, 0x41, 0x75, 0x73, 0x22, 0x2c, 0x0a, 0x20,
|
||||||
0x67, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x6e, 0x22, 0x2c, 0x0a,
|
0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6d, 0x69,
|
||||||
0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22,
|
|
||||||
0x3a, 0x20, 0x22, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x2c, 0x0a,
|
|
||||||
0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x65, 0x78,
|
|
||||||
0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x22, 0x3a, 0x20, 0x22, 0x45, 0x78,
|
|
||||||
0x74, 0x65, 0x72, 0x6e, 0x65, 0x6e, 0x20, 0x44, 0x61, 0x65, 0x6d, 0x6f,
|
|
||||||
0x6e, 0x20, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x6e, 0x22, 0x2c, 0x0a,
|
|
||||||
0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x6d, 0x69,
|
|
||||||
0x6e, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x20, 0x22, 0x4d, 0x69, 0x6e, 0x69,
|
0x6e, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x20, 0x22, 0x4d, 0x69, 0x6e, 0x69,
|
||||||
0x6e, 0x67, 0x20, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x6e, 0x22, 0x2c,
|
0x6e, 0x67, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x6e, 0x22, 0x2c,
|
||||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74,
|
0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
|
||||||
0x74, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63,
|
0x22, 0x3a, 0x20, 0x22, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x2c,
|
||||||
0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3a, 0x20, 0x22, 0x54, 0x72, 0x61, 0x6e,
|
0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x65,
|
||||||
0x73, 0x61, 0x6b, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x77, 0x69, 0x72, 0x64,
|
0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x22, 0x3a, 0x20, 0x22, 0x45,
|
||||||
0x20, 0xc3, 0xbc, 0x62, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x6c,
|
0x78, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x6e, 0x20, 0x44, 0x61, 0x65, 0x6d,
|
||||||
0x74, 0x2e, 0x2e, 0x2e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22,
|
0x6f, 0x6e, 0x20, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x6e, 0x22, 0x2c,
|
||||||
0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x3a, 0x20, 0x22, 0x45,
|
0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x6d,
|
||||||
0x72, 0x66, 0x6f, 0x6c, 0x67, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
0x69, 0x6e, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x20, 0x22, 0x4d, 0x69, 0x6e,
|
||||||
0x22, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x3a, 0x20, 0x22,
|
0x69, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x6e, 0x22,
|
||||||
0x5a, 0x75, 0x73, 0x61, 0x6d, 0x6d, 0x65, 0x6e, 0x66, 0x61, 0x73, 0x73,
|
0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x75, 0x62, 0x6d, 0x69,
|
||||||
0x75, 0x6e, 0x67, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73,
|
0x74, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61,
|
||||||
0x79, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x20, 0x22, 0x53, 0x79,
|
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3a, 0x20, 0x22, 0x54, 0x72, 0x61,
|
||||||
0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x73, 0x69, 0x65, 0x72, 0x65,
|
0x6e, 0x73, 0x61, 0x6b, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x77, 0x69, 0x72,
|
||||||
0x2e, 0x2e, 0x2e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74,
|
0x64, 0x20, 0xc3, 0xbc, 0x62, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x74, 0x65,
|
||||||
0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, 0x3a,
|
0x6c, 0x74, 0x2e, 0x2e, 0x2e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||||
0x20, 0x22, 0x54, 0x2d, 0x41, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x6e,
|
0x22, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x3a, 0x20, 0x22,
|
||||||
0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x65, 0x73, 0x74,
|
0x45, 0x72, 0x66, 0x6f, 0x6c, 0x67, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20,
|
||||||
0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22,
|
0x20, 0x22, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x3a, 0x20,
|
||||||
0x3a, 0x20, 0x22, 0x54, 0x65, 0x73, 0x74, 0x65, 0x6e, 0x22, 0x2c, 0x0a,
|
0x22, 0x5a, 0x75, 0x73, 0x61, 0x6d, 0x6d, 0x65, 0x6e, 0x66, 0x61, 0x73,
|
||||||
0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x22, 0x3a,
|
0x73, 0x75, 0x6e, 0x67, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22,
|
||||||
0x20, 0x22, 0x44, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x22, 0x2c, 0x0a, 0x20,
|
0x73, 0x79, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x20, 0x22, 0x53,
|
||||||
0x20, 0x20, 0x20, 0x22, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x5f, 0x65, 0x66,
|
0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x73, 0x69, 0x65, 0x72,
|
||||||
0x66, 0x65, 0x63, 0x74, 0x73, 0x22, 0x3a, 0x20, 0x22, 0x44, 0x65, 0x73,
|
0x65, 0x2e, 0x2e, 0x2e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22,
|
||||||
0x69, 0x67, 0x6e, 0x2d, 0x45, 0x66, 0x66, 0x65, 0x6b, 0x74, 0x65, 0x22,
|
0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22,
|
||||||
0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x69, 0x6d, 0x65, 0x5f,
|
0x3a, 0x20, 0x22, 0x54, 0x2d, 0x41, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65,
|
||||||
0x64, 0x61, 0x79, 0x73, 0x5f, 0x61, 0x67, 0x6f, 0x22, 0x3a, 0x20, 0x22,
|
0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x65, 0x73,
|
||||||
0x76, 0x6f, 0x72, 0x20, 0x25, 0x64, 0x20, 0x54, 0x61, 0x67, 0x65, 0x6e,
|
0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||||
|
0x22, 0x3a, 0x20, 0x22, 0x54, 0x65, 0x73, 0x74, 0x65, 0x6e, 0x22, 0x2c,
|
||||||
|
0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x22,
|
||||||
|
0x3a, 0x20, 0x22, 0x44, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x22, 0x2c, 0x0a,
|
||||||
|
0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x5f, 0x65,
|
||||||
|
0x66, 0x66, 0x65, 0x63, 0x74, 0x73, 0x22, 0x3a, 0x20, 0x22, 0x44, 0x65,
|
||||||
|
0x73, 0x69, 0x67, 0x6e, 0x2d, 0x45, 0x66, 0x66, 0x65, 0x6b, 0x74, 0x65,
|
||||||
0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x69, 0x6d, 0x65,
|
0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x69, 0x6d, 0x65,
|
||||||
0x5f, 0x68, 0x6f, 0x75, 0x72, 0x73, 0x5f, 0x61, 0x67, 0x6f, 0x22, 0x3a,
|
0x5f, 0x64, 0x61, 0x79, 0x73, 0x5f, 0x61, 0x67, 0x6f, 0x22, 0x3a, 0x20,
|
||||||
0x20, 0x22, 0x76, 0x6f, 0x72, 0x20, 0x25, 0x64, 0x20, 0x53, 0x74, 0x75,
|
0x22, 0x76, 0x6f, 0x72, 0x20, 0x25, 0x64, 0x20, 0x54, 0x61, 0x67, 0x65,
|
||||||
0x6e, 0x64, 0x65, 0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22,
|
0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x69, 0x6d,
|
||||||
0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73,
|
0x65, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x73, 0x5f, 0x61, 0x67, 0x6f, 0x22,
|
||||||
0x5f, 0x61, 0x67, 0x6f, 0x22, 0x3a, 0x20, 0x22, 0x76, 0x6f, 0x72, 0x20,
|
0x3a, 0x20, 0x22, 0x76, 0x6f, 0x72, 0x20, 0x25, 0x64, 0x20, 0x53, 0x74,
|
||||||
0x25, 0x64, 0x20, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x6e, 0x22, 0x2c,
|
|
||||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73,
|
|
||||||
0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x5f, 0x61, 0x67, 0x6f, 0x22, 0x3a,
|
|
||||||
0x20, 0x22, 0x76, 0x6f, 0x72, 0x20, 0x25, 0x64, 0x20, 0x53, 0x65, 0x6b,
|
|
||||||
0x75, 0x6e, 0x64, 0x65, 0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
0x75, 0x6e, 0x64, 0x65, 0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||||
|
0x22, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65,
|
||||||
|
0x73, 0x5f, 0x61, 0x67, 0x6f, 0x22, 0x3a, 0x20, 0x22, 0x76, 0x6f, 0x72,
|
||||||
|
0x20, 0x25, 0x64, 0x20, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x6e, 0x22,
|
||||||
|
0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x69, 0x6d, 0x65, 0x5f,
|
||||||
|
0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x5f, 0x61, 0x67, 0x6f, 0x22,
|
||||||
|
0x3a, 0x20, 0x22, 0x76, 0x6f, 0x72, 0x20, 0x25, 0x64, 0x20, 0x53, 0x65,
|
||||||
|
0x6b, 0x75, 0x6e, 0x64, 0x65, 0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20,
|
||||||
|
0x20, 0x22, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x31, 0x35,
|
||||||
|
0x6d, 0x69, 0x6e, 0x22, 0x3a, 0x20, 0x22, 0x31, 0x35, 0x20, 0x4d, 0x69,
|
||||||
|
0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x69, 0x6d,
|
||||||
|
0x65, 0x6f, 0x75, 0x74, 0x5f, 0x31, 0x68, 0x6f, 0x75, 0x72, 0x22, 0x3a,
|
||||||
|
0x20, 0x22, 0x31, 0x20, 0x53, 0x74, 0x75, 0x6e, 0x64, 0x65, 0x22, 0x2c,
|
||||||
|
0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75,
|
||||||
|
0x74, 0x5f, 0x31, 0x6d, 0x69, 0x6e, 0x22, 0x3a, 0x20, 0x22, 0x31, 0x20,
|
||||||
|
0x4d, 0x69, 0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74,
|
||||||
|
0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x33, 0x30, 0x6d, 0x69, 0x6e,
|
||||||
|
0x22, 0x3a, 0x20, 0x22, 0x33, 0x30, 0x20, 0x4d, 0x69, 0x6e, 0x22, 0x2c,
|
||||||
|
0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75,
|
||||||
|
0x74, 0x5f, 0x35, 0x6d, 0x69, 0x6e, 0x22, 0x3a, 0x20, 0x22, 0x35, 0x20,
|
||||||
|
0x4d, 0x69, 0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74,
|
||||||
|
0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x6f, 0x66, 0x66, 0x22, 0x3a,
|
||||||
|
0x20, 0x22, 0x41, 0x75, 0x73, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||||
0x22, 0x74, 0x6f, 0x22, 0x3a, 0x20, 0x22, 0x41, 0x6e, 0x22, 0x2c, 0x0a,
|
0x22, 0x74, 0x6f, 0x22, 0x3a, 0x20, 0x22, 0x41, 0x6e, 0x22, 0x2c, 0x0a,
|
||||||
0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x6f, 0x5f, 0x75, 0x70, 0x70, 0x65,
|
0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x6f, 0x5f, 0x75, 0x70, 0x70, 0x65,
|
||||||
0x72, 0x22, 0x3a, 0x20, 0x22, 0x41, 0x4e, 0x22, 0x2c, 0x0a, 0x20, 0x20,
|
0x72, 0x22, 0x3a, 0x20, 0x22, 0x41, 0x4e, 0x22, 0x2c, 0x0a, 0x20, 0x20,
|
||||||
@@ -3402,4 +3419,4 @@ unsigned char res_lang_de_json[] = {
|
|||||||
0x73, 0x65, 0x73, 0x22, 0x3a, 0x20, 0x22, 0x5a, 0x2d, 0x41, 0x64, 0x72,
|
0x73, 0x65, 0x73, 0x22, 0x3a, 0x20, 0x22, 0x5a, 0x2d, 0x41, 0x64, 0x72,
|
||||||
0x65, 0x73, 0x73, 0x65, 0x6e, 0x22, 0x0a, 0x7d, 0x0a
|
0x65, 0x73, 0x73, 0x65, 0x6e, 0x22, 0x0a, 0x7d, 0x0a
|
||||||
};
|
};
|
||||||
unsigned int res_lang_de_json_len = 40821;
|
unsigned int res_lang_de_json_len = 41025;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -73,6 +73,16 @@ bool RPCClient::connect(const std::string& host, const std::string& port,
|
|||||||
impl_->url = "http://" + host + ":" + port + "/";
|
impl_->url = "http://" + host + ":" + port + "/";
|
||||||
VERBOSE_LOGF("Connecting to dragonxd at %s\n", impl_->url.c_str());
|
VERBOSE_LOGF("Connecting to dragonxd at %s\n", impl_->url.c_str());
|
||||||
|
|
||||||
|
// Clean up previous curl handle/headers to avoid leaks on retries
|
||||||
|
if (impl_->headers) {
|
||||||
|
curl_slist_free_all(impl_->headers);
|
||||||
|
impl_->headers = nullptr;
|
||||||
|
}
|
||||||
|
if (impl_->curl) {
|
||||||
|
curl_easy_cleanup(impl_->curl);
|
||||||
|
impl_->curl = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize curl handle
|
// Initialize curl handle
|
||||||
impl_->curl = curl_easy_init();
|
impl_->curl = curl_easy_init();
|
||||||
if (!impl_->curl) {
|
if (!impl_->curl) {
|
||||||
@@ -81,7 +91,7 @@ bool RPCClient::connect(const std::string& host, const std::string& port,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set up headers - daemon expects text/plain, not application/json
|
// Set up headers - daemon expects text/plain, not application/json
|
||||||
impl_->headers = curl_slist_append(impl_->headers, "Content-Type: text/plain");
|
impl_->headers = curl_slist_append(nullptr, "Content-Type: text/plain");
|
||||||
std::string auth_header = "Authorization: Basic " + auth_;
|
std::string auth_header = "Authorization: Basic " + auth_;
|
||||||
impl_->headers = curl_slist_append(impl_->headers, auth_header.c_str());
|
impl_->headers = curl_slist_append(impl_->headers, auth_header.c_str());
|
||||||
|
|
||||||
@@ -97,6 +107,8 @@ bool RPCClient::connect(const std::string& host, const std::string& port,
|
|||||||
json result = call("getinfo");
|
json result = call("getinfo");
|
||||||
if (result.contains("version")) {
|
if (result.contains("version")) {
|
||||||
connected_ = true;
|
connected_ = true;
|
||||||
|
warming_up_ = false;
|
||||||
|
warmup_status_.clear();
|
||||||
last_connect_error_.clear();
|
last_connect_error_.clear();
|
||||||
DEBUG_LOGF("Connected to dragonxd v%d\n", result["version"].get<int>());
|
DEBUG_LOGF("Connected to dragonxd v%d\n", result["version"].get<int>());
|
||||||
return true;
|
return true;
|
||||||
@@ -104,7 +116,9 @@ bool RPCClient::connect(const std::string& host, const std::string& port,
|
|||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
last_connect_error_ = e.what();
|
last_connect_error_ = e.what();
|
||||||
// Daemon warmup messages (Loading block index, Verifying blocks, etc.)
|
// Daemon warmup messages (Loading block index, Verifying blocks, etc.)
|
||||||
// are normal startup progress — don't label them "Connection failed".
|
// are normal startup progress — the daemon is reachable and auth works,
|
||||||
|
// it just hasn't finished initializing yet. Mark as connected+warmup
|
||||||
|
// so the wallet can show the UI instead of a blocking overlay.
|
||||||
std::string msg = e.what();
|
std::string msg = e.what();
|
||||||
bool isWarmup = (msg.find("Loading") != std::string::npos ||
|
bool isWarmup = (msg.find("Loading") != std::string::npos ||
|
||||||
msg.find("Verifying") != std::string::npos ||
|
msg.find("Verifying") != std::string::npos ||
|
||||||
@@ -113,13 +127,19 @@ bool RPCClient::connect(const std::string& host, const std::string& port,
|
|||||||
msg.find("Rescanning") != std::string::npos ||
|
msg.find("Rescanning") != std::string::npos ||
|
||||||
msg.find("Pruning") != std::string::npos);
|
msg.find("Pruning") != std::string::npos);
|
||||||
if (isWarmup) {
|
if (isWarmup) {
|
||||||
DEBUG_LOGF("Daemon starting: %s\n", msg.c_str());
|
connected_ = true;
|
||||||
|
warming_up_ = true;
|
||||||
|
warmup_status_ = msg;
|
||||||
|
DEBUG_LOGF("Daemon warming up: %s\n", msg.c_str());
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
DEBUG_LOGF("Connection failed: %s\n", msg.c_str());
|
DEBUG_LOGF("Connection failed: %s\n", msg.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connected_ = false;
|
connected_ = false;
|
||||||
|
warming_up_ = false;
|
||||||
|
warmup_status_.clear();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,6 +147,8 @@ void RPCClient::disconnect()
|
|||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lk(curl_mutex_);
|
std::lock_guard<std::recursive_mutex> lk(curl_mutex_);
|
||||||
connected_ = false;
|
connected_ = false;
|
||||||
|
warming_up_ = false;
|
||||||
|
warmup_status_.clear();
|
||||||
if (impl_->curl) {
|
if (impl_->curl) {
|
||||||
curl_easy_cleanup(impl_->curl);
|
curl_easy_cleanup(impl_->curl);
|
||||||
impl_->curl = nullptr;
|
impl_->curl = nullptr;
|
||||||
|
|||||||
@@ -53,6 +53,19 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool isConnected() const { return connected_; }
|
bool isConnected() const { return connected_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief True if the last connect() succeeded but daemon returned a warmup error.
|
||||||
|
* The curl handle is valid and auth succeeded — RPC calls will throw warmup errors
|
||||||
|
* until the daemon finishes initializing.
|
||||||
|
*/
|
||||||
|
bool isWarmingUp() const { return warming_up_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The warmup status message (e.g. "Activating best chain...").
|
||||||
|
* Empty when not in warmup.
|
||||||
|
*/
|
||||||
|
const std::string& getWarmupStatus() const { return warmup_status_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the error message from the last failed connect() attempt.
|
* @brief Get the error message from the last failed connect() attempt.
|
||||||
*/
|
*/
|
||||||
@@ -182,6 +195,8 @@ private:
|
|||||||
std::string port_;
|
std::string port_;
|
||||||
std::string auth_; // Base64 encoded "user:password"
|
std::string auth_; // Base64 encoded "user:password"
|
||||||
bool connected_ = false;
|
bool connected_ = false;
|
||||||
|
bool warming_up_ = false;
|
||||||
|
std::string warmup_status_;
|
||||||
std::string last_connect_error_;
|
std::string last_connect_error_;
|
||||||
mutable std::recursive_mutex curl_mutex_; // serializes all curl handle access
|
mutable std::recursive_mutex curl_mutex_; // serializes all curl handle access
|
||||||
|
|
||||||
|
|||||||
@@ -33,13 +33,13 @@ I18n::I18n()
|
|||||||
// Cyrillic glyphs are included in the Ubuntu font glyph ranges.
|
// Cyrillic glyphs are included in the Ubuntu font glyph ranges.
|
||||||
registerLanguage("en", "English");
|
registerLanguage("en", "English");
|
||||||
registerLanguage("es", "Español");
|
registerLanguage("es", "Español");
|
||||||
registerLanguage("zh", "Chinese");
|
registerLanguage("zh", "中文");
|
||||||
registerLanguage("ru", "Russian");
|
registerLanguage("ru", "Русский");
|
||||||
registerLanguage("de", "Deutsch");
|
registerLanguage("de", "Deutsch");
|
||||||
registerLanguage("fr", "Français");
|
registerLanguage("fr", "Français");
|
||||||
registerLanguage("pt", "Português");
|
registerLanguage("pt", "Português");
|
||||||
registerLanguage("ja", "Japanese");
|
registerLanguage("ja", "日本語");
|
||||||
registerLanguage("ko", "Korean");
|
registerLanguage("ko", "한국어");
|
||||||
|
|
||||||
// Load default English strings (built-in fallback)
|
// Load default English strings (built-in fallback)
|
||||||
loadLanguage("en");
|
loadLanguage("en");
|
||||||
|
|||||||
Reference in New Issue
Block a user