Skip to content
This repository was archived by the owner on Sep 15, 2026. It is now read-only.
This repository was archived by the owner on Sep 15, 2026. It is now read-only.

Improve carpincho-wallet popup INP: debounce password scoring and trim recurring work #62

Description

@gabitoesmiapodo

Part of epic #40. Track: INP (interaction responsiveness). Paths are relative to carpincho-wallet/.

Priority

High

User story / Problem statement

Currently, password strength scoring blocks the main thread on every keystroke in the carpincho-wallet popup, and recurring background work (status polling, un-memoized handlers) competes with interactions while Home is mounted. This degrades the popup's INP on the common unlock/home path.

Expected outcome

  • Password strength scoring no longer blocks keystroke repaint and is computed once per render rather than redundantly.
  • Recurring main-thread work does not run while the popup is hidden and is cheaper while visible.
  • Home-view re-renders are not defeated by handler closures recreated every render.

Acceptance criteria

  • Password strength scoring is debounced (150–250 ms via useDeferredValue or a timer) so keystrokes paint immediately, and the score is computed once per render and passed down (no double scorePassword call).
  • useWalletServiceStatus polling pauses when the popup is hidden (document.visibilityState) and backs off its interval.
  • usePendingActions handler closures are memoized (useCallback / stable object) and PendingActionsSection is React.memo'd so spreading handlers no longer defeats memoization.

Technical notes

  1. [High] src/components/PasswordStrengthIndicator.tsx:23 — scorePassword(password) runs zxcvbn() synchronously inside render; the component re-renders on every keystroke because password is controlled state in NewPasswordFields (src/components/NewPasswordFields.tsx:59). zxcvbn is a known INP killer; every keypress blocks the main thread before the input repaints. The 1-entry memo (src/vault/passwordStrength.ts:34) only helps repeated identical strings. isNewPasswordPairValid (src/components/NewPasswordFields.tsx:39) calls scorePassword a second time per render, doubling the work. Fix: debounce scoring and compute once, pass down.
  2. [High / resolved-by-feedback] src/vault/crypto.ts:4 — KDF_ITERATIONS = 600_000 PBKDF2-HMAC-SHA256 via crypto.subtle.deriveKey (src/vault/crypto.ts:35), invoked by unlock() on the unlock click. crypto.subtle is async and typically off the main thread in Chrome, so it does not fully block the event loop; 600k is a deliberate security trade-off. UnlockView sets busy synchronously and shows "Unlocking…" (src/views/UnlockView.tsx:74). No code change needed beyond existing feedback. Flag only if profiling shows the click handler itself janks.
  3. [Medium] src/hooks/useWalletServiceStatus.ts:65 — a 5-second setInterval fires walletServiceRequest('status') continuously while Home is mounted, each tick re-rendering HomeView and below — recurring main-thread work competing with interactions. Fix: pause polling when the popup is hidden (document.visibilityState) and back off the interval.
  4. [Medium] src/views/HomeView.tsx:148 — HomeView holds ~7 useState slices; usePendingActions (src/views/home/usePendingActions.ts:57) returns six handler closures recreated every render (no useCallback), spread into PendingActionsSection via {...pendingActions} (src/views/HomeView.tsx:172), defeating memoization. Cost is wasted re-renders. Fix: memoize the handler object / wrap in useCallback and React.memo the section.
  5. [Low] src/vault/VaultContext.tsx:419 — idle auto-lock effect registers mousemove/keydown/click/scroll/touchstart; reset runs per event but is passive and storage writes are throttled to 5 s, so per-event cost is just clearTimeout/setTimeout. Noted for completeness; throttle is the right mitigation.

Additional context

Method: static (lab/heuristic) review — extension popups have no CrUX field data. Finding 7 is resolved by existing UX feedback and carries no code change.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestpriority: highMust be addressed in current sprint

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions