@@ -39,22 +39,6 @@ const IDLE: WifPreviewState = { status: "idle" };
3939const CHECKING : WifPreviewState = { status : "checking" } ;
4040const DEBOUNCE_MS = 400 ;
4141
42- interface Resolved {
43- wif : string ;
44- state : WifPreviewState ;
45- }
46-
47- // Module-scoped cache: a WIF that resolved to a given identity (or to an
48- // actionable error) yields the same answer on every future paste, so survive
49- // modal close/reopen. Idle outcomes (UnknownIdentity / network blips) are
50- // not cached — see the resolver below.
51- const previewCache = new Map < string , WifPreviewState > ( ) ;
52-
53- /** Test-only: reset the module-scoped preview cache between tests. */
54- export function _resetWifPreviewCacheForTests ( ) : void {
55- previewCache . clear ( ) ;
56- }
57-
5842/**
5943 * Eagerly resolves a pasted WIF to its owning identity (and DPNS name) so
6044 * the user gets pre-submit confirmation and "wrong key type" feedback before
@@ -66,8 +50,11 @@ export function _resetWifPreviewCacheForTests(): void {
6650 * committed to login yet, so we don't surface UnknownIdentityError or network
6751 * failures as UI errors. The actual login path will surface them on submit.
6852 *
69- * Results are cached per WIF for the lifetime of the hook so re-typing the
70- * same key doesn't re-query.
53+ * Resolution state is component-local: changing the WIF cancels any in-flight
54+ * resolver and clears the prior result. We do NOT cache resolved outcomes
55+ * across renders — keeping a Map of pasted secrets keyed by raw WIF would
56+ * extend secret retention beyond the form lifecycle for no real UX win
57+ * (re-pasting the exact same WIF is rare).
7158 */
7259export function useWifPreview (
7360 sdk : DashSdk | null ,
@@ -76,24 +63,27 @@ export function useWifPreview(
7663) : WifPreviewState {
7764 const trimmed = secret . trim ( ) ;
7865 const gateOk = enabled && Boolean ( sdk ) && looksLikeWif ( trimmed ) ;
79- const cached = gateOk ? previewCache . get ( trimmed ) : undefined ;
8066
81- // The resolver tags its result with the WIF that produced it; the render
82- // path ignores stale results from a previous WIF. This avoids any setState
83- // in the effect body or cleanup.
84- const [ resolved , setResolved ] = useState < Resolved | null > ( null ) ;
67+ // The resolver tags its result with the WIF that produced it; if `trimmed`
68+ // changes between scheduling and rendering, we ignore the stale result at
69+ // the bottom of this hook. This avoids any setState in the effect body.
70+ // Note: `wif` here is component-local state — it lives only as long as the
71+ // input does, matching the lifetime of `secret` in the parent component.
72+ const [ resolved , setResolved ] = useState < {
73+ wif : string ;
74+ state : WifPreviewState ;
75+ } | null > ( null ) ;
8576
8677 useEffect ( ( ) => {
87- if ( ! gateOk || cached ) {
88- return ;
89- }
78+ if ( ! gateOk ) return ;
9079 let cancelled = false ;
9180 const timer = window . setTimeout ( async ( ) => {
9281 if ( cancelled ) return ;
93- const mod = await loadLoginModule ( ) ;
94- if ( cancelled ) return ;
95- let next : WifPreviewState ;
82+ let next : WifPreviewState = IDLE ;
83+ let mod : LoginModule | null = null ;
9684 try {
85+ mod = await loadLoginModule ( ) ;
86+ if ( cancelled ) return ;
9787 const result = await mod . resolveIdentityFromWif ( sdk ! , trimmed ) ;
9888 let dpns : string | null = null ;
9989 try {
@@ -107,9 +97,13 @@ export function useWifPreview(
10797 dpnsName : dpns ,
10898 } ;
10999 } catch ( err ) {
100+ // mod is null only if loadLoginModule itself rejected (chunk fetch
101+ // failure / offline). Treat that as an idle outcome — same silent
102+ // policy we apply to UnknownIdentity and network blips.
110103 if (
111- err instanceof mod . WrongKeyPurposeError ||
112- err instanceof mod . KeyDisabledError
104+ mod &&
105+ ( err instanceof mod . WrongKeyPurposeError ||
106+ err instanceof mod . KeyDisabledError )
113107 ) {
114108 // We have an identity ID — resolve its DPNS name so the warning
115109 // can show the user-friendly handle instead of a truncated ID.
@@ -134,29 +128,23 @@ export function useWifPreview(
134128 dpnsName : dpns ,
135129 } ;
136130 }
137- } else if ( err instanceof mod . UnknownIdentityError ) {
138- next = IDLE ;
139131 } else {
132+ // UnknownIdentityError, network failure, import failure, or any
133+ // other unexpected error — stay silent until the user submits.
140134 next = IDLE ;
141135 }
142136 }
143137 if ( cancelled ) return ;
144- // Only cache stable outcomes — silent "idle" results from transient
145- // network errors should be retryable on the next keystroke.
146- if ( next . status !== "idle" ) {
147- previewCache . set ( trimmed , next ) ;
148- }
149138 setResolved ( { wif : trimmed , state : next } ) ;
150139 } , DEBOUNCE_MS ) ;
151140
152141 return ( ) => {
153142 cancelled = true ;
154143 window . clearTimeout ( timer ) ;
155144 } ;
156- } , [ sdk , trimmed , gateOk , cached ] ) ;
145+ } , [ sdk , trimmed , gateOk ] ) ;
157146
158147 if ( ! gateOk ) return IDLE ;
159- if ( cached ) return cached ;
160148 if ( resolved && resolved . wif === trimmed ) return resolved . state ;
161149 return CHECKING ;
162150}
0 commit comments