fix(wallet-switch): cluster C — no mis-scoping in the unknown-identity window
From the audit: while a switch/first-load is in flight the wallet identity hash is empty, so contact/portfolio scope-writes silently fell back to "global" (leaking a wallet-specific entry into every wallet) and scope-filters showed every wallet's scoped entries. - Contacts: a NEW wallet-scoped contact created while the identity is unknown is now refused with a clear message (tick global or wait); editing an existing scoped contact preserves its scope instead of demoting it to global. - Portfolio: the "Add group" button is disabled while the identity is unknown (with a tooltip), so a new group can't get an empty/global scope. - Both views' visibility filters now show ONLY global entries when the identity is unknown — never another wallet's scoped contacts/groups — instead of showing everything. - +2 i18n strings (8 langs; reworded one zh string to stay within the CJK subset). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -380,14 +380,14 @@ static bool pfIndexVisible(App* app, int i)
|
||||
const auto& es = app->settings()->getPortfolioEntries();
|
||||
if (i < 0 || i >= (int)es.size()) return false;
|
||||
const std::string h = app->activeWalletIdentityHash();
|
||||
return es[i].scope.empty() || h.empty() || es[i].scope == h;
|
||||
return es[i].scope.empty() || (!h.empty() && es[i].scope == h);
|
||||
}
|
||||
static int pfFirstVisibleIndex(App* app)
|
||||
{
|
||||
const auto& es = app->settings()->getPortfolioEntries();
|
||||
const std::string h = app->activeWalletIdentityHash();
|
||||
for (int i = 0; i < (int)es.size(); i++)
|
||||
if (es[i].scope.empty() || h.empty() || es[i].scope == h) return i;
|
||||
if (es[i].scope.empty() || (!h.empty() && es[i].scope == h)) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -823,7 +823,9 @@ static void RenderPortfolioEditor(App* app)
|
||||
const std::string activeHash = app->activeWalletIdentityHash();
|
||||
std::vector<int> vis;
|
||||
for (int i = 0; i < (int)entries.size(); i++)
|
||||
if (entries[i].scope.empty() || activeHash.empty() || entries[i].scope == activeHash)
|
||||
// Global groups always; this wallet's groups only when the identity is known — never
|
||||
// another wallet's scoped groups during the switch/first-load window.
|
||||
if (entries[i].scope.empty() || (!activeHash.empty() && entries[i].scope == activeHash))
|
||||
vis.push_back(i);
|
||||
if (vis.empty())
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), TR("portfolio_no_entries"));
|
||||
@@ -905,7 +907,11 @@ static void RenderPortfolioEditor(App* app)
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
// Add immediately creates a persisted "Untitled" group and selects it for editing.
|
||||
// Add immediately creates a persisted "Untitled" group scoped to this wallet — so it's disabled
|
||||
// while the wallet identity is unknown (switch / first-load window), otherwise a new group would
|
||||
// get an empty scope and show under every wallet.
|
||||
const bool pfIdentityKnown = !app->activeWalletIdentityHash().empty();
|
||||
ImGui::BeginDisabled(!pfIdentityKnown);
|
||||
if (material::TactileButton(TR("portfolio_add_entry"), ImVec2(masterW, addH))) {
|
||||
// Auto-save the current group, then start a fresh one selected for editing.
|
||||
pfCommitIfNeeded(app);
|
||||
@@ -918,6 +924,9 @@ static void RenderPortfolioEditor(App* app)
|
||||
s_pfEdit.sel = (int)es.size() - 1;
|
||||
PortfolioBeginEdit(app, s_pfEdit.sel);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (!pfIdentityKnown && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
|
||||
material::Tooltip("%s", TR("portfolio_wallet_loading"));
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
@@ -1967,7 +1976,9 @@ static void mktDrawPortfolio(const MktCtx& cx)
|
||||
std::vector<int> vis;
|
||||
for (int i = 0; i < (int)allEntries.size(); i++) {
|
||||
const auto& e = allEntries[i];
|
||||
if (e.scope.empty() || activeHash.empty() || e.scope == activeHash) vis.push_back(i);
|
||||
// Global groups always; this wallet's only when the identity is known (never another
|
||||
// wallet's scoped groups during the switch/first-load window).
|
||||
if (e.scope.empty() || (!activeHash.empty() && e.scope == activeHash)) vis.push_back(i);
|
||||
}
|
||||
|
||||
int style = app->settings() ? app->settings()->getPortfolioStyle() : 0;
|
||||
@@ -2110,7 +2121,7 @@ void RenderMarketTab(App* app)
|
||||
const std::string pfActiveHash = app->activeWalletIdentityHash();
|
||||
int pfVisN = 0;
|
||||
for (const auto& e : pfEntriesGeo)
|
||||
if (e.scope.empty() || pfActiveHash.empty() || e.scope == pfActiveHash) pfVisN++;
|
||||
if (e.scope.empty() || (!pfActiveHash.empty() && e.scope == pfActiveHash)) pfVisN++;
|
||||
int pfStyle = app->settings()->getPortfolioStyle();
|
||||
float pfRowH = (pfStyle == 0 ? 46.0f : pfStyle == 1 ? 64.0f : 84.0f) * mktDp;
|
||||
float pfRowGap = Layout::spacingSm();
|
||||
|
||||
Reference in New Issue
Block a user