fix(ui): prevent stuck spinners and double-submits in dialogs
Address a cluster of low-severity robustness nits where an async callback could be skipped (leaving a spinner spinning forever) or a synchronous action could be re-fired mid-flight: - app_network: add catch(...) fallbacks around the importPrivateKey and submitZSendMany worker lambdas so a non-std throw can't escape the worker and skip the main-thread callback (stuck "Importing…"/"Sending…"). - export_transactions: re-entrancy guard disabling Export while a write is in flight. - bootstrap_download: disable Cancel once clicked so the request is visibly acknowledged and can't be re-fired before the worker stops. - network_tab: give clear "already in list" feedback on a duplicate server (keep the inputs, clear only the stale invalid-URL error) and disable/relabel Refresh while a probe is in flight. - key_export: offer Retry from the error branch (falls back to Reveal) and guard against an empty/malformed address before dispatching. - receive_tab: only enter the "generating…" state when a dispatch is actually possible (guards a stuck spinner when disconnected). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,9 @@ namespace ui {
|
||||
static bool s_open = false;
|
||||
static char s_filename[256] = "";
|
||||
static std::string s_status;
|
||||
// Re-entrancy guard: true while a CSV write is in flight (disables the
|
||||
// Export button so a second synchronous write can't be kicked off).
|
||||
static bool s_exporting = false;
|
||||
|
||||
// Helper to escape CSV field
|
||||
static std::string escapeCSV(const std::string& field)
|
||||
@@ -91,14 +94,16 @@ void ExportTransactionsDialog::render(App* app)
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Export button
|
||||
// Export button (disabled while a write is already in flight)
|
||||
ImGui::BeginDisabled(s_exporting);
|
||||
if (material::StyledButton(TR("export"), ImVec2(exportBtn.width, 0), S.resolveFont(exportBtn.font))) {
|
||||
if (state.transactions.empty()) {
|
||||
Notifications::instance().warning(TR("export_tx_none"));
|
||||
} else {
|
||||
s_exporting = true;
|
||||
std::string configDir = util::Platform::getConfigDir();
|
||||
std::string filepath = configDir + "/" + s_filename;
|
||||
|
||||
|
||||
std::ofstream file(filepath);
|
||||
if (!file.is_open()) {
|
||||
s_status = "Failed to create file";
|
||||
@@ -138,13 +143,15 @@ void ExportTransactionsDialog::render(App* app)
|
||||
|
||||
file.close();
|
||||
|
||||
s_status = "Exported " + std::to_string(state.transactions.size()) +
|
||||
s_status = "Exported " + std::to_string(state.transactions.size()) +
|
||||
" transactions to: " + filepath;
|
||||
Notifications::instance().success(TR("export_tx_success"), 5.0f);
|
||||
}
|
||||
s_exporting = false;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (material::StyledButton("Close", ImVec2(closeBtn.width, 0), S.resolveFont(closeBtn.font))) {
|
||||
s_open = false;
|
||||
|
||||
Reference in New Issue
Block a user