Compare commits
2
Commits
ee6e99965c
...
3f127f9b62
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f127f9b62 | ||
|
|
f2d9590058 |
@@ -1057,7 +1057,27 @@ void RenderSharedRecentTx(App* app, float recentH, float availW, float hs, float
|
||||
int count = std::min({4, maxRows, (int)txs.size()}); // show only the most recent (state.transactions is newest-first)
|
||||
|
||||
if (count == 0) {
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), TR("no_transactions_yet"));
|
||||
const bool roomForIcon = ImGui::GetContentRegionAvail().y >= 100.0f * dp;
|
||||
if (!app->isConnected()) {
|
||||
// Disconnected: not "empty" and not "loading" — keep a plain caption.
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(),
|
||||
TR(app->isLiteBuild() ? "lite_no_wallet" : "not_connected"));
|
||||
} else {
|
||||
const std::string txLoadingText = app->transactionRefreshProgressText();
|
||||
// In lite, transactionRefreshProgressText() is empty and last_tx_update is stamped differently,
|
||||
// so treat only an active sync as "loading" there.
|
||||
const bool loadingOrSyncing = !txLoadingText.empty() ||
|
||||
(state.sync.syncing && !state.sync.isSynced()) ||
|
||||
(!app->isLiteBuild() && state.last_tx_update == 0);
|
||||
if (loadingOrSyncing) {
|
||||
char lbuf[192];
|
||||
const char* base = !txLoadingText.empty() ? txLoadingText.c_str() : TR("loading_transactions");
|
||||
snprintf(lbuf, sizeof(lbuf), "%s%s", base, material::LoadingDots());
|
||||
material::DrawEmptyState(roomForIcon ? ICON_MD_HOURGLASS_EMPTY : nullptr, lbuf);
|
||||
} else {
|
||||
material::DrawEmptyState(roomForIcon ? ICON_MD_RECEIPT_LONG : nullptr, TR("no_transactions_yet"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||
ImFont* capFont = Type().caption();
|
||||
|
||||
@@ -32,6 +32,7 @@ static uintptr_t s_qr_texture = 0;
|
||||
static int s_qr_width = 0;
|
||||
static int s_qr_height = 0;
|
||||
static bool s_uri_dirty = true;
|
||||
static bool s_creating_address = false; // true while a Create-shielded-address RPC is in flight
|
||||
|
||||
// Helper to build payment URI
|
||||
static std::string buildPaymentUri()
|
||||
@@ -48,7 +49,8 @@ void RequestPaymentDialog::show(const std::string& address)
|
||||
s_label[0] = '\0';
|
||||
s_payment_uri.clear();
|
||||
s_uri_dirty = true;
|
||||
|
||||
s_creating_address = false;
|
||||
|
||||
// Only a shielded address can be paid (DragonX is a private chain): never prefill a mining address.
|
||||
if (!address.empty() && !util::isTransparentAddress(address)) {
|
||||
strncpy(s_address, address.c_str(), sizeof(s_address) - 1);
|
||||
@@ -67,6 +69,7 @@ void RequestPaymentDialog::show(const std::string& address)
|
||||
void RequestPaymentDialog::hide()
|
||||
{
|
||||
s_open = false;
|
||||
s_creating_address = false;
|
||||
s_payment_uri.clear();
|
||||
if (s_qr_texture != 0) {
|
||||
FreeQRTexture(s_qr_texture);
|
||||
@@ -100,17 +103,54 @@ void RequestPaymentDialog::render(App* app)
|
||||
ImGui::Spacing();
|
||||
|
||||
// Address selection
|
||||
ImGui::Text("%s", TR("request_receive_address"));
|
||||
|
||||
std::string addr_display = s_address[0] ? s_address : TR("request_select_address");
|
||||
if (addr_display.length() > static_cast<size_t>(zAddrLbl.truncate)) {
|
||||
addr_display = addr_display.substr(0, zAddrFrontLbl.truncate) + "..." + addr_display.substr(addr_display.length() - zAddrBackLbl.truncate);
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(-1);
|
||||
if (ImGui::BeginCombo("##Address", addr_display.c_str())) {
|
||||
// Z-addresses
|
||||
if (!state.z_addresses.empty()) {
|
||||
const bool noShielded = state.z_addresses.empty();
|
||||
if (noShielded) {
|
||||
// Nothing to request to yet — offer to create a shielded address instead of dead-ending.
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(material::OnSurfaceMedium()));
|
||||
ImGui::TextWrapped("%s", TR("request_no_shielded"));
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Spacing();
|
||||
if (s_creating_address) {
|
||||
ImGui::BeginDisabled();
|
||||
material::TactileButton(TR("generating"), ImVec2(0, 0), S.resolveFont(actionBtn.font));
|
||||
ImGui::EndDisabled();
|
||||
} else {
|
||||
ImGui::BeginDisabled(!app->isConnected());
|
||||
if (material::TactileButton(TR("request_create_shielded"), ImVec2(0, 0), S.resolveFont(actionBtn.font))) {
|
||||
if (app->isConnected()) {
|
||||
s_creating_address = true;
|
||||
app->createNewZAddress([](const std::string& addr) {
|
||||
s_creating_address = false;
|
||||
if (addr.empty()) {
|
||||
Notifications::instance().error(TR("failed_create_shielded"));
|
||||
} else {
|
||||
Notifications::instance().success(TR("new_shielded_created"));
|
||||
// Pre-select the new address if the dialog is still open and none chosen.
|
||||
if (s_open && s_address[0] == '\0') {
|
||||
strncpy(s_address, addr.c_str(), sizeof(s_address) - 1);
|
||||
s_address[sizeof(s_address) - 1] = '\0';
|
||||
s_uri_dirty = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
} else {
|
||||
ImGui::Text("%s", TR("request_receive_address"));
|
||||
|
||||
std::string addr_display = s_address[0] ? s_address : std::string(TR("request_select_address"));
|
||||
if (addr_display.length() > static_cast<size_t>(zAddrLbl.truncate)) {
|
||||
addr_display = addr_display.substr(0, zAddrFrontLbl.truncate) + "..." + addr_display.substr(addr_display.length() - zAddrBackLbl.truncate);
|
||||
}
|
||||
if (s_address[0]) {
|
||||
std::string l = app->getAddressLabel(s_address);
|
||||
if (!l.empty()) addr_display = l + " " + addr_display;
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(-1);
|
||||
if (ImGui::BeginCombo("##Address", addr_display.c_str())) {
|
||||
ImGui::TextDisabled("%s", TR("request_shielded_addrs"));
|
||||
for (size_t i = 0; i < state.z_addresses.size(); i++) {
|
||||
const auto& addr = state.z_addresses[i];
|
||||
@@ -118,19 +158,20 @@ void RequestPaymentDialog::render(App* app)
|
||||
if (label.length() > static_cast<size_t>(zAddrLbl.truncate)) {
|
||||
label = label.substr(0, zAddrFrontLbl.truncate) + "..." + label.substr(label.length() - zAddrBackLbl.truncate);
|
||||
}
|
||||
|
||||
if (ImGui::Selectable(label.c_str(), addr.address == s_address)) {
|
||||
std::string clbl = app->getAddressLabel(addr.address);
|
||||
std::string shown = (clbl.empty() ? label : (clbl + " " + label)) + "##" + addr.address;
|
||||
|
||||
if (ImGui::Selectable(shown.c_str(), addr.address == s_address)) {
|
||||
strncpy(s_address, addr.address.c_str(), sizeof(s_address) - 1);
|
||||
s_uri_dirty = true;
|
||||
}
|
||||
}
|
||||
// No mining (transparent) addresses: they only receive mining rewards, so a request to one
|
||||
// could never be paid.
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
// No mining (transparent) addresses: they only receive mining rewards, so a request to one
|
||||
// could never be paid.
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
// Amount (optional)
|
||||
|
||||
+66
-45
@@ -77,7 +77,6 @@ static constexpr double FEE_LOW = DRAGONX_DEFAULT_FEE * 0.5;
|
||||
static constexpr double FEE_NORMAL = DRAGONX_DEFAULT_FEE;
|
||||
static constexpr double FEE_HIGH = DRAGONX_DEFAULT_FEE * 2.0;
|
||||
|
||||
static bool s_clear_confirm_pending = false;
|
||||
|
||||
// Source dropdown preview string
|
||||
static std::string s_source_preview;
|
||||
@@ -268,9 +267,13 @@ static void RenderSourceDropdown(App* app, float width) {
|
||||
const char* tag = isZ ? "[Z]" : "[T]";
|
||||
std::string trunc = util::truncateMiddle(addr.address,
|
||||
static_cast<int>(std::max(S.drawElement("tabs.send", "addr-preview-trunc-min").size, width / S.drawElement("tabs.send", "addr-preview-trunc-divisor").size)));
|
||||
snprintf(buf, sizeof(buf), "%s %s — %s",
|
||||
tag, trunc.c_str(),
|
||||
FormatSourceBalance(addr.balance, addr.spendableBalance).c_str());
|
||||
std::string lbl = app->getAddressLabel(addr.address);
|
||||
if (!lbl.empty())
|
||||
snprintf(buf, sizeof(buf), "%s %s (%s) — %s", tag, lbl.c_str(), trunc.c_str(),
|
||||
FormatSourceBalance(addr.balance, addr.spendableBalance).c_str());
|
||||
else
|
||||
snprintf(buf, sizeof(buf), "%s %s — %s", tag, trunc.c_str(),
|
||||
FormatSourceBalance(addr.balance, addr.spendableBalance).c_str());
|
||||
s_source_preview = buf;
|
||||
} else if (s_from_address[0] != '\0') {
|
||||
// A pre-filled source that the dropdown list doesn't carry (zero-balance / view-only / list still
|
||||
@@ -313,9 +316,13 @@ static void RenderSourceDropdown(App* app, float width) {
|
||||
const char* tag = isZ ? "[Z]" : "[T]";
|
||||
|
||||
std::string trunc = util::truncateMiddle(addr.address, (int)addrTruncLen);
|
||||
snprintf(buf, sizeof(buf), "%s %s — %s",
|
||||
tag, trunc.c_str(),
|
||||
FormatSourceBalance(addr.balance, addr.spendableBalance).c_str());
|
||||
std::string lbl = app->getAddressLabel(addr.address);
|
||||
if (!lbl.empty())
|
||||
snprintf(buf, sizeof(buf), "%s %s (%s) — %s", tag, lbl.c_str(), trunc.c_str(),
|
||||
FormatSourceBalance(addr.balance, addr.spendableBalance).c_str());
|
||||
else
|
||||
snprintf(buf, sizeof(buf), "%s %s — %s", tag, trunc.c_str(),
|
||||
FormatSourceBalance(addr.balance, addr.spendableBalance).c_str());
|
||||
|
||||
ImGui::PushID(static_cast<int>(i));
|
||||
if (ImGui::Selectable(buf, isCurrent)) {
|
||||
@@ -324,9 +331,12 @@ static void RenderSourceDropdown(App* app, float width) {
|
||||
addr.address.c_str());
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
material::Tooltip("%s\n%s",
|
||||
addr.address.c_str(),
|
||||
FormatSourceBalance(addr.balance, addr.spendableBalance).c_str());
|
||||
if (!lbl.empty())
|
||||
material::Tooltip("%s\n%s\n%s", lbl.c_str(), addr.address.c_str(),
|
||||
FormatSourceBalance(addr.balance, addr.spendableBalance).c_str());
|
||||
else
|
||||
material::Tooltip("%s\n%s", addr.address.c_str(),
|
||||
FormatSourceBalance(addr.balance, addr.spendableBalance).c_str());
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
@@ -335,6 +345,20 @@ static void RenderSourceDropdown(App* app, float width) {
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::PopFont();
|
||||
|
||||
// Warn when the chosen source is a transparent address: spending from it is not private on this
|
||||
// ac_private chain (sender, amount and recipient become visible on-chain).
|
||||
if (app->isConnected() && s_from_address[0] != '\0') {
|
||||
bool srcT = (s_selected_from_idx >= 0 && s_selected_from_idx < (int)state.addresses.size())
|
||||
? (state.addresses[s_selected_from_idx].type != "shielded")
|
||||
: IsValidTransparentAddr(s_from_address);
|
||||
if (srcT) {
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(Warning()));
|
||||
Type().textWrapped(TypeStyle::Caption, TR("send_transparent_source_warn"));
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -1033,6 +1057,12 @@ static void RenderActionButtons(App* app, float width, float vScale,
|
||||
float sendBtnW = width - cancelBtnW - btnGap;
|
||||
float btnH = std::max(schema::UI().drawElement("tabs.send", "action-btn-min-height").size, schema::UI().drawElement("tabs.send", "action-btn-height").size * vScale);
|
||||
|
||||
// Explain why Review/Send is greyed while syncing (the fields above are usable now).
|
||||
if (state.sync.syncing) {
|
||||
Type().textColored(TypeStyle::Caption, Warning(), TR("send_available_when_synced"));
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
}
|
||||
|
||||
// ---- Send button ----
|
||||
if (!can_send) {
|
||||
// The disabled fill must contrast the surface: a white overlay reads on dark skins but vanished
|
||||
@@ -1081,38 +1111,20 @@ static void RenderActionButtons(App* app, float width, float vScale,
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImGui::ColorConvertU32ToFloat4(OnSurfaceDisabled()));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, S.drawElement("tabs.send", "cancel-btn-border-size").size);
|
||||
char clearId[64];
|
||||
snprintf(clearId, sizeof(clearId), "%s##Cancel%s", TR("cancel"), suffix);
|
||||
// This button never cancels an in-progress send (there is none here) — it clears the form. Relabel it
|
||||
// honestly, disable it when the form is empty, and drop the redundant confirm (ClearFormWithUndo
|
||||
// already offers a timed Undo). The "##Cancel" id suffix is kept so the widget id stays stable.
|
||||
snprintf(clearId, sizeof(clearId), "%s##Cancel%s", TR("send_clear_form"), suffix);
|
||||
ImGui::BeginDisabled(!FormHasData());
|
||||
if (TactileButton(clearId, ImVec2(cancelBtnW, btnH), S.resolveFont(S.button("tabs.send", "clear-button").font))) {
|
||||
if (FormHasData()) {
|
||||
s_clear_confirm_pending = true;
|
||||
} else {
|
||||
ClearFormWithUndo();
|
||||
}
|
||||
ClearFormWithUndo();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
|
||||
material::Tooltip("%s", TR("send_clear_form_tip"));
|
||||
ImGui::PopStyleVar(); // FrameBorderSize
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
// Clear confirmation popup
|
||||
char confirmClearId[32];
|
||||
snprintf(confirmClearId, sizeof(confirmClearId), "##ConfirmClear%s", suffix);
|
||||
if (s_clear_confirm_pending) {
|
||||
ImGui::OpenPopup(confirmClearId);
|
||||
s_clear_confirm_pending = false;
|
||||
}
|
||||
if (ImGui::BeginPopup(confirmClearId)) {
|
||||
ImGui::Text("%s", TR("send_clear_fields"));
|
||||
ImGui::Spacing();
|
||||
if (TactileButton(TR("send_yes_clear"), ImVec2(schema::UI().drawElement("tabs.send", "clear-confirm-yes-width").size * dp, 0), S.resolveFont("button"))) {
|
||||
ClearFormWithUndo();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (TactileButton(TR("send_keep"), ImVec2(schema::UI().drawElement("tabs.send", "clear-confirm-keep-width").size * dp, 0), S.resolveFont("button"))) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// Undo button (shown briefly after clear)
|
||||
if (s_undo_timestamp > 0.0) {
|
||||
double undoElapsed = ImGui::GetTime() - s_undo_timestamp;
|
||||
@@ -1270,9 +1282,9 @@ void RenderSendTab(App* app)
|
||||
|
||||
RenderSyncBanner(state);
|
||||
|
||||
bool sendSyncing = state.sync.syncing && !state.sync.isSynced();
|
||||
ImGui::BeginDisabled(sendSyncing);
|
||||
|
||||
// The form stays EDITABLE while syncing so the user can pre-fill it; only the Review/Send button is
|
||||
// gated (can_send includes !syncing, and the confirm step re-checks live state), so nothing can be
|
||||
// sent until the wallet is synced.
|
||||
ImVec2 sendAvail = ImGui::GetContentRegionAvail();
|
||||
float hs = Layout::hScale(sendAvail.x);
|
||||
float vScale = Layout::vScale(sendAvail.y);
|
||||
@@ -1329,7 +1341,6 @@ void RenderSendTab(App* app)
|
||||
// Zero balance CTA
|
||||
if (RenderZeroBalanceCTA(app, dl, formW)) {
|
||||
ImGui::EndGroup();
|
||||
ImGui::EndDisabled(); // sendSyncing guard
|
||||
ImGui::EndChild();
|
||||
return;
|
||||
}
|
||||
@@ -1404,7 +1415,9 @@ void RenderSendTab(App* app)
|
||||
Type().textColored(TypeStyle::Caption, Error(), TR("send_mining_addr_short"));
|
||||
if (vt && ImGui::IsItemHovered())
|
||||
material::Tooltip("%s", TR("send_mining_addr_blocked"));
|
||||
} else if (!checkPreview) {
|
||||
} else if (!checkPreview && ImGui::GetActiveID() != ImGui::GetID("##ToAddr")) {
|
||||
// Defer the "invalid" error until the field loses focus, so it doesn't flash red
|
||||
// from the first keystroke while the user is still typing the address.
|
||||
ImGui::SameLine();
|
||||
Type().textColored(TypeStyle::Caption, Error(), TR("invalid_address"));
|
||||
}
|
||||
@@ -1612,8 +1625,11 @@ void RenderSendTab(App* app)
|
||||
}
|
||||
}
|
||||
|
||||
// ---- MEMO (shielded only) ----
|
||||
if (is_valid_z || s_to_address[0] == '\0') {
|
||||
// ---- MEMO (shielded only; shown disabled + explained for non-shielded recipients) ----
|
||||
{
|
||||
// Keep the memo visible (and the typed text intact) even for a transparent recipient, with an
|
||||
// explainer, instead of silently hiding the field and dropping what the user wrote.
|
||||
const bool memoDisabled = (s_to_address[0] != '\0' && !is_valid_z);
|
||||
ImGui::Dummy(ImVec2(0, innerGap * 0.5f));
|
||||
{
|
||||
ImVec2 divPos = ImGui::GetCursorScreenPos();
|
||||
@@ -1628,8 +1644,10 @@ void RenderSendTab(App* app)
|
||||
|
||||
float memoInputH = std::max(S.drawElement("tabs.send", "memo-min-height").size, S.drawElement("tabs.send", "memo-base-height").size * vScale);
|
||||
ImGui::PushItemWidth(colW);
|
||||
if (memoDisabled) ImGui::BeginDisabled();
|
||||
ImGui::InputTextMultiline("##Memo", s_memo, sizeof(s_memo),
|
||||
ImVec2(colW, memoInputH));
|
||||
if (memoDisabled) ImGui::EndDisabled();
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
// The Sapling memo limit is in BYTES (multi-byte UTF-8 fills it faster than visible chars);
|
||||
@@ -1639,6 +1657,10 @@ void RenderSendTab(App* app)
|
||||
bool memoAtCap = memo_len + 1 >= memoMax;
|
||||
snprintf(buf, sizeof(buf), TR("byte_count_fmt"), memo_len, memoMax);
|
||||
Type().textColored(TypeStyle::Caption, memoAtCap ? Warning() : OnSurfaceDisabled(), buf);
|
||||
if (memoDisabled) {
|
||||
const char* mk = s_memo[0] ? "send_memo_will_not_be_sent" : "send_memo_requires_shielded";
|
||||
Type().textColored(TypeStyle::Caption, Warning(), TR(mk));
|
||||
}
|
||||
}
|
||||
|
||||
// Divider before action buttons
|
||||
@@ -1706,7 +1728,6 @@ void RenderSendTab(App* app)
|
||||
RenderRecentSends(state, formW, capFont, app);
|
||||
|
||||
ImGui::EndGroup();
|
||||
ImGui::EndDisabled(); // sendSyncing guard
|
||||
// Round to nearest pixel to prevent sub-pixel oscillation (vibration)
|
||||
// when content like "recent sends" elapsed-time text changes width each frame
|
||||
float measuredH = ImGui::GetCursorPosY() - contentStartY;
|
||||
|
||||
@@ -1151,6 +1151,8 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["recent_sends"] = "RECENT SENDS";
|
||||
strings_["recipient"] = "RECIPIENT";
|
||||
strings_["memo_optional"] = "MEMO (OPTIONAL)";
|
||||
strings_["send_memo_requires_shielded"] = "Memos require a shielded (z) recipient address.";
|
||||
strings_["send_memo_will_not_be_sent"] = "This memo will not be sent: memos only work with shielded (z) recipients.";
|
||||
strings_["no_recent_sends"] = "No recent sends";
|
||||
strings_["network_fee"] = "NETWORK FEE";
|
||||
strings_["amount_details"] = "AMOUNT DETAILS";
|
||||
@@ -2575,6 +2577,8 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["request_payment_uri"] = "Payment URI:";
|
||||
strings_["request_receive_address"] = "Receive Address:";
|
||||
strings_["request_select_address"] = "Select address...";
|
||||
strings_["request_no_shielded"] = "You don't have a shielded address yet. Create one to receive payments.";
|
||||
strings_["request_create_shielded"] = "Create a shielded address";
|
||||
strings_["request_shielded_addrs"] = "-- Shielded Addresses --";
|
||||
strings_["request_title"] = "Request Payment";
|
||||
strings_["request_uri_copied"] = "Payment URI copied to clipboard";
|
||||
@@ -2584,6 +2588,8 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["send_amount_details"] = "AMOUNT DETAILS";
|
||||
strings_["send_amount_upper"] = "AMOUNT";
|
||||
strings_["send_clear_fields"] = "Clear all form fields?";
|
||||
strings_["send_clear_form"] = "Clear form";
|
||||
strings_["send_clear_form_tip"] = "Clear the recipient, amount and memo. You can undo for a few seconds.";
|
||||
strings_["send_copy_error"] = "Copy Error";
|
||||
strings_["send_dismiss"] = "Dismiss";
|
||||
strings_["send_error_copied"] = "Error copied to clipboard";
|
||||
@@ -2600,6 +2606,7 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["send_network_fee_label"] = "Network fee:";
|
||||
strings_["send_total_label"] = "Total:";
|
||||
strings_["send_no_balance"] = "No balance";
|
||||
strings_["send_transparent_source_warn"] = "Sending from a transparent address is not private: the sender, amount and recipient are visible on-chain. Use a shielded (Z) address for privacy.";
|
||||
strings_["send_no_recent"] = "No recent sends";
|
||||
strings_["send_recent_sends"] = "RECENT SENDS";
|
||||
strings_["send_recipient"] = "RECIPIENT";
|
||||
@@ -2615,6 +2622,7 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["send_tooltip_not_connected"] = "Not connected to daemon";
|
||||
strings_["send_tooltip_select_source"] = "Select a source address first";
|
||||
strings_["send_tooltip_syncing"] = "Wait for blockchain to sync";
|
||||
strings_["send_available_when_synced"] = "You can fill in the details now. Sending is available once the wallet is fully synced.";
|
||||
strings_["send_tooltip_view_only"] = "View-only address — no spending key, cannot send";
|
||||
strings_["send_total"] = "Total";
|
||||
strings_["send_tx_failed"] = "Transaction failed";
|
||||
|
||||
Reference in New Issue
Block a user