From 3f95765dcc240caf4e7ef30cfcc4fc18b4ed9564 Mon Sep 17 00:00:00 2001 From: DanS Date: Sun, 12 Jul 2026 15:11:35 -0500 Subject: [PATCH] fix(balance): unique IDs + scope filter for the Add-to-portfolio menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The address context menu's "Add to portfolio" submenu built each item with MenuItem(label), whose ImGui ID is derived from the label — so two groups with the same label (or two empty labels) collided, routing a click to the wrong group, and a blank label rendered as an invisible row. - PushID(pi) per item gives a stable unique ID regardless of label, and an empty label now shows the "new entry" placeholder. - Filter the list to the active wallet's groups (or legacy/unscoped), matching the Market summary and editor, so an address can't be added to a different wallet's group. The "no entries" placeholder now also covers the case where every group is filtered out. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/ui/windows/balance_components.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/ui/windows/balance_components.cpp b/src/ui/windows/balance_components.cpp index a5da515..dd97827 100644 --- a/src/ui/windows/balance_components.cpp +++ b/src/ui/windows/balance_components.cpp @@ -680,19 +680,31 @@ void RenderSharedAddressList(App* app, float listH, float availW, ImGui::Separator(); if (ImGui::BeginMenu(TR("portfolio_add_to"))) { const auto& pf = app->settings()->getPortfolioEntries(); - if (pf.empty()) { - ImGui::MenuItem(TR("portfolio_no_entries"), nullptr, false, false); - } + const std::string activeHash = app->activeWalletIdentityHash(); + int shown = 0; for (int pi = 0; pi < (int)pf.size(); pi++) { + // Only this wallet's groups (or legacy/unscoped), matching the Market summary — + // don't offer to add this address to another wallet's group. + if (!(pf[pi].scope.empty() || activeHash.empty() || pf[pi].scope == activeHash)) + continue; + // PushID keeps the item ID unique even when two groups share a label (a + // label-derived MenuItem ID would collide); a blank label shows a placeholder + // instead of rendering as an invisible row. + ImGui::PushID(pi); bool inSet = dragonx::data::PortfolioEntryContains(pf[pi].addresses, addr.address); - if (ImGui::MenuItem(pf[pi].label.c_str(), nullptr, inSet)) { + const char* lbl = pf[pi].label.empty() ? TR("portfolio_new_entry") : pf[pi].label.c_str(); + if (ImGui::MenuItem(lbl, nullptr, inSet)) { auto entries = pf; // copy, mutate, persist if (inSet) dragonx::data::PortfolioEntryRemove(entries[pi].addresses, addr.address); else dragonx::data::PortfolioEntryAdd(entries[pi].addresses, addr.address); app->settings()->setPortfolioEntries(entries); app->settings()->save(); } + ImGui::PopID(); + shown++; } + if (shown == 0) + ImGui::MenuItem(TR("portfolio_no_entries"), nullptr, false, false); ImGui::EndMenu(); } effects::ImGuiAcrylic::EndAcrylicPopup();