fix(history): show shielding moves as neutral "Shielded", not red "Sent"

A coinbase/transparent→shielded move (often auto-shield) was drawn with a red
"Sent" label and send arrow yet a green "+X" amount — an internally
contradictory row that reads as "money leaving" to anyone who auto-shielded. It
now renders as a neutral "Shielded" row (shield icon + the shielded accent
colour for label and amount), and since a shield is your own funds going
private — not a spend — it's excluded from the Sent card total and the Sent
filter (it merges into one row visible under "All").

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-18 08:57:38 -05:00
co-authored by Claude Opus 4.8
parent 09f39f9183
commit 8eee351faa
3 changed files with 16 additions and 11 deletions
+4 -1
View File
@@ -120,7 +120,10 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo
CancelMiningBenchmark(app, false); // don't leave a pool benchmark running after switching to solo (L-11)
app->stopPoolMining();
}
if (soloHov) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
if (soloHov) {
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
material::Tooltip("%s", TR("mining_solo_tip"));
}
}
ImGui::SetCursorScreenPos(poolMin);
+11 -10
View File
@@ -98,7 +98,7 @@ static void DrawTxIcon(ImDrawList* dl, const std::string& type,
} else if (type == "receive") {
icon = ICON_MD_CALL_RECEIVED;
} else if (type == "shield") {
icon = ICON_MD_CALL_MADE;
icon = ICON_MD_SHIELD; // a shield, not an outgoing arrow — it's an internal privacy move
} else {
icon = ICON_MD_CONSTRUCTION;
}
@@ -185,6 +185,9 @@ void RenderTransactionsTab(App* app)
ImU32 greenCol = Success();
ImU32 redCol = Error();
ImU32 goldCol = Warning();
// Neutral colour for a shielding move (your own funds going transparent→private): neither the
// red of a spend nor the green of income, so a shield never reads as "money leaving".
ImU32 shieldCol = schema::UI().resolveColor("var(--accent-shielded)", OnSurfaceMedium());
std::string txLoadingText = app->transactionRefreshProgressText();
bool txLoading = !txLoadingText.empty();
@@ -248,11 +251,9 @@ void RenderTransactionsTab(App* app)
if (send_i >= 0 && recv_i >= 0) {
isShieldLeg[send_i] = true;
isShieldLeg[recv_i] = true;
// The list shows the merged shield under the "Sent" filter, so count it there too —
// otherwise the Sent card reads 0 while the Sent list is non-empty. Use the shielded
// (receive-leg) amount, which is what the merged row displays.
sendCount++;
sendTotal += std::abs(state.transactions[recv_i].amount);
// A shield moves your OWN funds transparent→private — it isn't a spend, so it is NOT
// counted toward the Sent card/total (and no longer appears under the Sent filter).
// It merges into one neutral "Shielded" row, visible under "All".
}
}
@@ -543,7 +544,7 @@ void RenderTransactionsTab(App* app)
for (size_t i = 0; i < display_txns.size(); i++) {
const auto& dtx = display_txns[i];
if (type_filter != 0) {
if (type_filter == 1 && dtx.display_type != "send" && dtx.display_type != "shield") continue;
if (type_filter == 1 && dtx.display_type != "send") continue; // Sent = real spends; shields aren't sends
if (type_filter == 2 && dtx.display_type != "receive") continue;
if (type_filter == 3 && dtx.display_type != "generate" && dtx.display_type != "immature" && dtx.display_type != "mined") continue;
if (type_filter == 4 && !chatStore.hasChatTxid(dtx.txid)) continue; // chat-only
@@ -722,7 +723,7 @@ void RenderTransactionsTab(App* app)
ImU32 iconCol;
const char* typeStr;
if (shieldedDisplay) {
iconCol = redCol; typeStr = TR("sent_type");
iconCol = shieldCol; typeStr = TR("shielded_self_type");
} else if (tx.display_type == "receive") {
iconCol = greenCol; typeStr = TR("recv_type");
} else if (tx.display_type == "send") {
@@ -750,7 +751,7 @@ void RenderTransactionsTab(App* app)
float cy = rowPos.y + Layout::spacingMd();
// Icon
DrawTxIcon(dl, shieldedDisplay ? "send" : tx.display_type,
DrawTxIcon(dl, shieldedDisplay ? "shield" : tx.display_type,
cx + rowIconSz, cy + body2->LegacySize * 0.5f, rowIconSz, iconCol);
// Type label
@@ -769,7 +770,7 @@ void RenderTransactionsTab(App* app)
OnSurfaceMedium(), addr_display.c_str());
// Amount (right-aligned, first line)
ImU32 amtCol = (tx.amount >= 0) ? greenCol : redCol;
ImU32 amtCol = shieldedDisplay ? shieldCol : ((tx.amount >= 0) ? greenCol : redCol);
if (tx.amount >= 0)
snprintf(buf, sizeof(buf), "+%.8f", tx.amount);
else
+1
View File
@@ -1207,6 +1207,7 @@ void I18n::loadBuiltinEnglish()
strings_["tx_chat_badge"] = "Message";
strings_["recv_type"] = "Recv";
strings_["sent_type"] = "Sent";
strings_["shielded_self_type"] = "Shielded";
strings_["immature_type"] = "Immature";
strings_["mined_type"] = "Mined";
strings_["mature"] = "Mature";