fix(balance): unique IDs + scope filter for the Add-to-portfolio menu

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) <noreply@anthropic.com>
This commit is contained in:
2026-07-12 15:11:35 -05:00
parent 62c92cc862
commit 3f95765dcc

View File

@@ -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();