2 Commits
Author SHA1 Message Date
DanSandClaude Opus 4.8 2fdf33f99b feat(chat): "Request" pill on unsolicited conversations in the list
The conversation list now marks a pending stranger request with a small
"Request" pill (bottom-right of the row), so it's distinguishable before you
open it; the preview text is clipped to the pill's left so they don't overlap.
Completes the request-affordance pair with the in-thread Accept/Block card.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-18 18:27:44 -05:00
DanSandClaude Opus 4.8 092a802959 feat(balance): pending chip in the Donut, Shield and Timeline layouts too
Extends the labeled "Pending +X" chip (with its awaiting-confirmations tooltip)
to three more layouts via a shared DrawPendingChip() helper. It draws on the
ticker line (the USD value sits on the line below in these layouts, so there's no
overlap) and is skipped if it would overflow the card. The remaining draw-list
layouts with right-aligned content (Consolidated, Minimal, the grid variants)
are left for a follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-18 18:27:44 -05:00
5 changed files with 46 additions and 1 deletions
+12
View File
@@ -97,6 +97,18 @@ bool FormatTotalEarnings(App* app, std::string& outText, ImU32& outCol) {
}
// Render compact hero line: logo + balance + USD + mining on one line
float DrawPendingChip(ImDrawList* dl, ImVec2 pos, float maxRight, ImFont* font, float fontSize) {
using namespace material;
if (s_dispUnconfirmed <= 1e-9) return 0.0f;
char buf[64];
snprintf(buf, sizeof(buf), TR("baltab_pending_chip"), s_dispUnconfirmed);
const float w = font->CalcTextSizeA(fontSize, 10000.0f, 0.0f, buf).x;
if (pos.x + w > maxRight) return 0.0f; // would overflow the card — skip rather than overlap
dl->AddText(font, fontSize, pos, WithAlpha(Warning(), 230), buf);
if (IsRectHovered(pos, ImVec2(pos.x + w, pos.y + fontSize))) Tooltip("%s", TR("baltab_pending_tip"));
return w;
}
void RenderCompactHero(App* app, ImDrawList* dl, float availW, float hs, float vs, float heroHeightOverride) {
using namespace material;
char buf[64];
+5
View File
@@ -33,6 +33,11 @@ void RenderSharedAddressList(App* app, float listH, float availW, float glassRou
void RenderSharedRecentTx(App* app, float recentH, float availW, float hs, float vs);
void RenderSyncBar(App* app, ImDrawList* dl, float vs);
// Draws a labeled "Pending +X" chip (unconfirmed balance) at `pos`, via the draw list only, when there
// is unconfirmed balance AND it fits left of `maxRight`. Returns the width drawn (0 if skipped). Adds a
// hover tooltip. Shared by the balance layouts so pending isn't visible in only one of them.
float DrawPendingChip(ImDrawList* dl, ImVec2 pos, float maxRight, ImFont* font, float fontSize);
// Total period-earnings for the whole wallet, formatted per the earnings settings. Returns false
// (and leaves out* untouched) when the global earnings toggle is off, so layouts show it only when
// the user has opted into the earnings view. Shared by RenderCompactHero and the layout totals.
+15
View File
@@ -748,6 +748,11 @@ static void RenderBalanceDonut(App* app) {
dl->AddText(capFont, capFont->LegacySize,
ImVec2(ImGui::GetCursorScreenPos().x, tickerY),
OnSurfaceMedium(), DRAGONX_TICKER);
DrawPendingChip(dl, ImVec2(ImGui::GetCursorScreenPos().x
+ capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, DRAGONX_TICKER).x
+ Layout::spacingMd(), tickerY),
ImGui::GetCursorScreenPos().x + ImGui::GetContentRegionAvail().x,
capFont, capFont->LegacySize);
ImGui::NewLine();
// USD value
@@ -1663,6 +1668,11 @@ static void RenderBalanceShield(App* app) {
dl->AddText(capFont, capFont->LegacySize,
ImVec2(ImGui::GetCursorScreenPos().x, pos.y + heroSz.y - capFont->LegacySize),
OnSurfaceMedium(), DRAGONX_TICKER);
DrawPendingChip(dl, ImVec2(ImGui::GetCursorScreenPos().x
+ capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, DRAGONX_TICKER).x
+ Layout::spacingMd(), pos.y + heroSz.y - capFont->LegacySize),
ImGui::GetCursorScreenPos().x + ImGui::GetContentRegionAvail().x,
capFont, capFont->LegacySize);
ImGui::NewLine();
double usd_value = state.getBalanceUSD();
@@ -1852,6 +1862,11 @@ static void RenderBalanceTimeline(App* app) {
dl->AddText(capFont, capFont->LegacySize,
ImVec2(ImGui::GetCursorScreenPos().x, pos.y + heroSz.y - capFont->LegacySize),
OnSurfaceMedium(), DRAGONX_TICKER);
DrawPendingChip(dl, ImVec2(ImGui::GetCursorScreenPos().x
+ capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, DRAGONX_TICKER).x
+ Layout::spacingMd(), pos.y + heroSz.y - capFont->LegacySize),
ImGui::GetCursorScreenPos().x + ImGui::GetContentRegionAvail().x,
capFont, capFont->LegacySize);
ImGui::NewLine();
double usd_value = state.getBalanceUSD();
+13 -1
View File
@@ -1022,9 +1022,21 @@ void RenderChatTab(App* app)
dl->AddText(nameFont, nameSz, ImVec2(textX, p.y + pad),
c.hidden ? material::OnSurfaceMedium() : material::OnSurface(), c.peerName.c_str());
dl->PopClipRect();
// Pending-request pill (bottom-right) for an unsolicited stranger; the preview is clipped to its left.
float previewRight = mx.x - pad;
if (c.pendingRequest) {
const char* rl = TR("chat_request_pill");
const ImVec2 rs = metaFont->CalcTextSizeA(metaSz, FLT_MAX, 0.0f, rl);
const float px = 6.0f * dp, py = 1.0f * dp;
const ImVec2 pmax(mx.x - pad, p.y + rowH - pad + py);
const ImVec2 pmin(pmax.x - rs.x - 2.0f * px, pmax.y - rs.y - 2.0f * py);
dl->AddRectFilled(pmin, pmax, material::WithAlpha(material::Warning(), 60), (rs.y + 2.0f * py) * 0.5f);
dl->AddText(metaFont, metaSz, ImVec2(pmin.x + px, pmin.y + py), material::Warning(), rl);
previewRight = pmin.x - pad;
}
// Preview (bottom, clipped to the text column, muted).
const std::string preview = previewOf(c.lastBody);
dl->PushClipRect(ImVec2(textX, p.y), ImVec2(mx.x - pad, mx.y), true);
dl->PushClipRect(ImVec2(textX, p.y), ImVec2(std::max(textX, previewRight), mx.y), true);
dl->AddText(metaFont, metaSz, ImVec2(textX, p.y + rowH - pad - metaSz),
material::OnSurfaceMedium(), preview.c_str());
dl->PopClipRect();
+1
View File
@@ -232,6 +232,7 @@ void I18n::loadBuiltinEnglish()
strings_["chat_empty_hint"] = "No conversations yet. Messages you receive will appear here.";
strings_["chat_you"] = "You";
strings_["chat_contact_request"] = "contact request";
strings_["chat_request_pill"] = "Request";
strings_["chat_request_title"] = "New contact request";
strings_["chat_request_body"] = "This person isn't in your contacts. Accept to add them, or block to delete this conversation and ignore them.";
strings_["chat_request_accept"] = "Accept";