Skip to content

Fix/eid wallet binding data loading#1088

Open
Bekiboo wants to merge 4 commits into
mainfrom
fix/eid-wallet-binding-data-loading
Open

Fix/eid wallet binding data loading#1088
Bekiboo wants to merge 4 commits into
mainfrom
fix/eid-wallet-binding-data-loading

Conversation

@Bekiboo

@Bekiboo Bekiboo commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Description of change

Two fixes to how the eID wallet loads binding documents. The Social Bindings full
list downloaded every counterparty's photo blobs just to render their name (~2 MB
for 8 contacts — the reported 3–4s spinner). Separately, a failed refresh silently
wiped the Legal ID and Personal cards back to their empty state.
Also clears the pages' render caches on logout — they survived it, so a
different eName onboarding on the same device was briefly shown the previous
user's name, legal ID and contacts.

No env, config or infra changes — nothing to do on staging or prod.

Issue Number

Closes #1080
Closes #1086

Type of change

  • Fix (a change which fixes an issue)
  • Chore (refactoring, build scripts or anything else that isn't user-facing) ✅ — pnpm test was broken

How the change has been tested

pnpm test: 14 passing (up from 4). svelte-check: 0 errors. Biome clean.
The 4 new #1086 regression tests were confirmed to fail against the pre-fix code.

#1080 — measured, not estimated. Bytes are network-independent, so they hold
for the reporter as well as locally (the bug is invisible on localhost, where a
round trip costs ~1 ms): 257,467 → 442 bytes per contact, i.e. ~2 MB → 3.4 KB
for 8. Registry round trips for the list drop from 16 to 8.

#1086 — reproduced live, then verified. Forced a GraphQL error (HTTP 200 +
{errors, data:null}) on the id_document query in the running app, same
starting state, only the fix differing:

with fix without fix
console Failed to load binding documents nothing
Legal ID / badge holds / VERIFIED reverts to ADD / UNVERIFIED

The "without fix" screen matches the ticket's screenshot. The silence is the
point — it's why the ticket had no error to report.

Change checklist

  • I have ensured that the CI Checks pass locally
  • I have removed any unnecessary logic
  • My code is well documented
  • I have signed my commits
  • My code follows the pattern of the application
  • I have self reviewed my code

Design decisions

nameOnly trades one extra request for 250 KB, deliberately. It issues two
typed queries instead of one broad one, so the raw request count goes up — but
they run in Promise.all (no extra round trip) and the photo blobs stop crossing
the wire. Round trips and bytes are the honest metric here; "fewer requests"
would have rejected the best fix.

resolveVaultUri is memoised inside the function, so all 7 callers benefit.
5-minute TTL (a vault URI only changes on re-provisioning), concurrent callers
share one in-flight promise, failures are never cached.

#1086 was fixed by deleting a duplicate, not guarding it.
loadBindingDocuments hand-rolled a fetch that already existed as
vaultGqlRequest — and the copy was the buggy one. Reusing the helper makes the
bug impossible by construction, and the fix is a net negative diff.

Promise.all over allSettled in the count-only path. Substituting an empty
result for a failed query isn't graceful degradation when the caller feeds it into
a full-store replace — it erases marks the user still has. Also realigns the
function with its sibling, which already used Promise.all.

Detailed breakdown

#1080. The full list called fetchNameFromVault without { nameOnly: true },
taking the branch that returns every doc type from the counterparty's vault to
read one name field. The home screen passes the flag precisely to avoid this;
the list never got it. Separately, three layers resolved the same counterparty
independently — server logs showed 16 /resolve for 8 contacts on one load. The
page now also caches at module level like the home screen, so revisits paint the
known list instead of a full-screen spinner; that's perceived latency only, it
removes no requests.

#1086. Two paths turned a failure into "the user has no data", then wrote it
over good state: loadBindingDocuments checked neither res.ok nor json.errors,
so edges fell back to [] and legalId was nulled (verified derives from
isFake === false || legalId !== null, so the badge followed); and
_loadPersonalBindingsCountOnly swallowed rejections into an empty result that
replaceAllPersonal wrote over the store wholesale. Network-level throws were
already handled correctly — only response-level failures leaked, which is why it
looked intermittent and logged nothing. Both are reachable from the 30s interval,
visibilitychange and pull-to-refresh, so one blip visibly resets the card.

Out of scope (deliberate). SOCIAL_BINDING_DOCS_QUERY hardcodes first: 50
with no pageInfo, so the list is silently truncated at 50 docs — real bug, worth
its own ticket. The per-binding reconcile still costs a resolve + paginated read
each; batching needs server support. No in-flight guard was added to
loadBindingDocuments: once failures throw instead of producing empty data, two
concurrent successful runs write the same values.

Cache lifetime. The home and Social Bindings pages hold their last-known
values at module scope for instant repaints. That state outlives logout:
performLogout leaves with goto("/"), a client-side nav, and SvelteKit never
re-evaluates a module on client-side nav — so the next user onboarding on the
device got the previous user's data on first paint, with no spinner, since
hasEverLoaded was cached too. Pages now register a reset from
<script module> and performLogout calls clearAllPageCaches(), next to the
notification and photo clears already there. Scoping the cache by eName was the
obvious alternative but can't work: the eName only arrives after an async vault
read, by which point the cache has already painted.

Summary by CodeRabbit

  • New Features

    • Added caching for social-binding vault lookups, including request sharing and retry handling.
    • Social-binding contacts now remain visible while refreshed after returning to the page.
    • Added an option to load only name-related documents when retrieving contact details.
  • Bug Fixes

    • Binding document requests now correctly surface network and GraphQL errors.
    • Personal-binding loads fail clearly when required requests fail, while genuinely missing data remains supported.
  • Tests

    • Added coverage for caching, concurrent lookups, error handling, and missing binding data.

@Bekiboo Bekiboo self-assigned this Jul 15, 2026
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The eID wallet now propagates personal-binding query failures, memoizes vault URI resolution, uses shared typed GraphQL requests for binding documents, and retains social-binding rows across navigation. Vitest coverage and test environment placeholders were updated accordingly.

Changes

eID wallet loading and caching

Layer / File(s) Summary
Binding loading and test coverage
infrastructure/eid-wallet/src/lib/utils/personalBinding.ts, infrastructure/eid-wallet/src/lib/utils/personalBinding.spec.ts, infrastructure/eid-wallet/src/test/env-static-public.ts, infrastructure/eid-wallet/package.json
Personal-binding count loading now rejects on HTTP or GraphQL failures instead of substituting empty results; tests and Vitest configuration cover these outcomes.
Vault URI memoization
infrastructure/eid-wallet/src/lib/utils/socialBinding.ts, infrastructure/eid-wallet/src/lib/utils/socialBinding.spec.ts
Vault URI lookups use TTL caching, in-flight request coalescing, normalized keys, retryable failures, and an explicit cache-clearing utility.
Shared binding-document requests
infrastructure/eid-wallet/src/routes/(app)/main/+page.svelte
Binding-document loading uses typed parallel vaultGqlRequest calls for id_document and self, relying on shared error handling.
Social-binding page state
infrastructure/eid-wallet/src/routes/(app)/social-bindings/+page.svelte
Previously loaded contact rows persist across navigation, refreshes update the module cache, and name resolution requests only name-related documents.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Suggested reviewers: coodos

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The code changes address both #1080 and #1086 by reducing social-binding payloads and preserving binding state on failures.
Out of Scope Changes check ✅ Passed No clearly unrelated code paths were introduced; the test and helper updates support the binding-loading fixes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title is concise and directly matches the main change: fixing eid wallet binding data loading.
Description check ✅ Passed The description follows the template and includes the required sections, issue numbers, testing, and checklist.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/eid-wallet-binding-data-loading

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Bekiboo
Bekiboo marked this pull request as ready for review July 15, 2026 21:38
@Bekiboo
Bekiboo requested a review from coodos as a code owner July 15, 2026 21:38

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@infrastructure/eid-wallet/src/routes/`(app)/social-bindings/+page.svelte:
- Around line 1-8: Update the module-level cache used by the social bindings
page, including cachedContacts and hasEverLoaded, so it is scoped to the
authenticated user via eName or cleared during sign-out. Ensure a new login
cannot render the previous user’s contacts before the current vault finishes
loading, while preserving the existing cache behavior for the same user.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 65838db3-0c72-4482-91b5-6ffec886f1e4

📥 Commits

Reviewing files that changed from the base of the PR and between 91bf719 and e16692d.

📒 Files selected for processing (8)
  • infrastructure/eid-wallet/package.json
  • infrastructure/eid-wallet/src/lib/utils/personalBinding.spec.ts
  • infrastructure/eid-wallet/src/lib/utils/personalBinding.ts
  • infrastructure/eid-wallet/src/lib/utils/socialBinding.spec.ts
  • infrastructure/eid-wallet/src/lib/utils/socialBinding.ts
  • infrastructure/eid-wallet/src/routes/(app)/main/+page.svelte
  • infrastructure/eid-wallet/src/routes/(app)/social-bindings/+page.svelte
  • infrastructure/eid-wallet/src/test/env-static-public.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant