diff --git a/src/ui/windows/address_transfer_dialog.h b/src/ui/windows/address_transfer_dialog.h index 8fed81e..c948921 100644 --- a/src/ui/windows/address_transfer_dialog.h +++ b/src/ui/windows/address_transfer_dialog.h @@ -141,6 +141,14 @@ public: double newFromBal = s_info.fromBalance - totalDeduct; double newToBal = s_info.toBalance + amount; bool amountValid = amount > 0 && totalDeduct <= s_info.fromBalance; + // Mirror the Send tab's guards (this dialog previously enabled Confirm on amount alone, so a + // disconnected / syncing / locked wallet would fail only at submit). These are own-address + // moves, but gating is strictly more restrictive — the transfer logic below is unchanged. + const auto& ws = s_app->getWalletState(); + const bool connected = s_app->isConnected(); + const bool syncing = ws.sync.syncing; + const bool liteLocked = s_app->liteWallet() && ws.isLocked(); + const bool canTransfer = amountValid && !s_sending && connected && !syncing && !liteLocked; ImGui::Separator(); ImGui::Spacing(); @@ -167,6 +175,19 @@ public: ImGui::TextWrapped("%s", TR("insufficient_funds")); ImGui::PopStyleColor(); } + if (amount <= 0.0) { + ImGui::Spacing(); + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(Error())); + ImGui::TextWrapped("%s", TR("send_tooltip_enter_amount")); + ImGui::PopStyleColor(); + } else if (amountValid && (!connected || syncing || liteLocked)) { + ImGui::Spacing(); + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(Warning())); + ImGui::TextWrapped("%s", !connected ? TR("send_tooltip_not_connected") + : syncing ? TR("send_tooltip_syncing") + : TR("transfer_tooltip_locked")); + ImGui::PopStyleColor(); + } if (amountValid && newFromBal < 1e-9) { ImGui::Spacing(); // Full-balance send: same warning-icon treatment as the de-shielding header above, @@ -204,13 +225,13 @@ public: bool outConfirm = false; bool outClose = false; DialogActionFooter(s_sending ? sendingLabel : confirmLabel, - amountValid && !s_sending, + canTransfer, cancelLabel, outConfirm, outClose, btnW); if (outClose) { s_open = false; } - if (outConfirm) { + if (outConfirm && canTransfer) { // re-check live state (the dialog persists across frames) s_sending = true; s_app->sendTransaction(s_info.fromAddr, s_info.toAddr, amount, s_fee, "", @@ -296,12 +317,19 @@ private: Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), TR("transfer_sent_desc")); ImGui::Spacing(); { - char buf[128]; - snprintf(buf, sizeof(buf), TR("shield_operation_id"), - s_resultMsg.size() > 40 - ? (s_resultMsg.substr(0, 20) + "..." + s_resultMsg.substr(s_resultMsg.size() - 10)).c_str() - : s_resultMsg.c_str()); + // The result is a TRANSACTION id, not an "Operation ID" — label it correctly and make + // it copyable. + std::string disp = s_resultMsg.size() > 40 + ? (s_resultMsg.substr(0, 20) + "..." + s_resultMsg.substr(s_resultMsg.size() - 10)) + : s_resultMsg; + char buf[160]; + snprintf(buf, sizeof(buf), TR("transfer_txid_label"), disp.c_str()); Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), buf); + ImGui::SameLine(); + if (ImGui::SmallButton(TR("grpb_copy"))) { + ImGui::SetClipboardText(s_resultMsg.c_str()); + Notifications::instance().info(TR("copied_to_clipboard")); + } } } else { ImGui::Spacing(); diff --git a/src/util/i18n.cpp b/src/util/i18n.cpp index 4011fb3..8021013 100644 --- a/src/util/i18n.cpp +++ b/src/util/i18n.cpp @@ -1118,6 +1118,8 @@ void I18n::loadBuiltinEnglish() strings_["confirm_transfer"] = "Confirm Transfer"; strings_["transfer_sent"] = "Transfer Sent"; strings_["transfer_sent_desc"] = "Your transfer has been submitted to the network."; + strings_["transfer_txid_label"] = "Transaction ID: %s"; + strings_["transfer_tooltip_locked"] = "Wallet is locked — unlock it to transfer"; strings_["transfer_failed"] = "Transfer Failed"; // Misc dialog strings