@erikey/react 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -177,6 +177,46 @@ function isAuthPath(href: string): boolean {
|
|
|
177
177
|
)
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
// Session storage key for persisting view state across remounts
|
|
181
|
+
const AUTH_FLOW_STATE_KEY = "erikey_auth_flow_state"
|
|
182
|
+
|
|
183
|
+
function getPersistedState(): {
|
|
184
|
+
view: AuthFlowView
|
|
185
|
+
email?: string
|
|
186
|
+
} | null {
|
|
187
|
+
if (typeof window === "undefined") return null
|
|
188
|
+
try {
|
|
189
|
+
const stored = sessionStorage.getItem(AUTH_FLOW_STATE_KEY)
|
|
190
|
+
if (stored) {
|
|
191
|
+
return JSON.parse(stored)
|
|
192
|
+
}
|
|
193
|
+
} catch {
|
|
194
|
+
// Ignore parse errors
|
|
195
|
+
}
|
|
196
|
+
return null
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function persistState(view: AuthFlowView, email?: string) {
|
|
200
|
+
if (typeof window === "undefined") return
|
|
201
|
+
try {
|
|
202
|
+
sessionStorage.setItem(
|
|
203
|
+
AUTH_FLOW_STATE_KEY,
|
|
204
|
+
JSON.stringify({ view, email })
|
|
205
|
+
)
|
|
206
|
+
} catch {
|
|
207
|
+
// Ignore storage errors
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function clearPersistedState() {
|
|
212
|
+
if (typeof window === "undefined") return
|
|
213
|
+
try {
|
|
214
|
+
sessionStorage.removeItem(AUTH_FLOW_STATE_KEY)
|
|
215
|
+
} catch {
|
|
216
|
+
// Ignore storage errors
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
180
220
|
export function AuthFlow({
|
|
181
221
|
mode = "internal",
|
|
182
222
|
onEvent,
|
|
@@ -196,8 +236,23 @@ export function AuthFlow({
|
|
|
196
236
|
...providerProps
|
|
197
237
|
}: AuthFlowProps) {
|
|
198
238
|
// Internal state for "internal" mode
|
|
199
|
-
|
|
200
|
-
const [
|
|
239
|
+
// Restore from sessionStorage if available (survives remounts caused by parent re-renders)
|
|
240
|
+
const [currentView, setCurrentView] = useState<AuthFlowView>(() => {
|
|
241
|
+
if (mode === "internal") {
|
|
242
|
+
const persisted = getPersistedState()
|
|
243
|
+
if (persisted) {
|
|
244
|
+
return persisted.view
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return initialView
|
|
248
|
+
})
|
|
249
|
+
const [verifyEmail, setVerifyEmail] = useState<string | undefined>(() => {
|
|
250
|
+
if (mode === "internal") {
|
|
251
|
+
const persisted = getPersistedState()
|
|
252
|
+
return persisted?.email
|
|
253
|
+
}
|
|
254
|
+
return undefined
|
|
255
|
+
})
|
|
201
256
|
|
|
202
257
|
// Handle navigation based on mode
|
|
203
258
|
const handleNavigate = useCallback(
|
|
@@ -304,38 +359,54 @@ export function AuthFlow({
|
|
|
304
359
|
case "SIGN_UP_REQUIRES_VERIFICATION":
|
|
305
360
|
setCurrentView("EMAIL_VERIFICATION")
|
|
306
361
|
setVerifyEmail(event.email)
|
|
362
|
+
persistState("EMAIL_VERIFICATION", event.email)
|
|
307
363
|
break
|
|
308
364
|
|
|
309
365
|
// Sign-in flow
|
|
310
366
|
case "SIGN_IN_REQUIRES_2FA":
|
|
311
367
|
setCurrentView("TWO_FACTOR")
|
|
368
|
+
persistState("TWO_FACTOR")
|
|
312
369
|
break
|
|
313
370
|
case "SIGN_IN_REQUIRES_VERIFICATION":
|
|
314
371
|
setCurrentView("EMAIL_VERIFICATION")
|
|
315
372
|
setVerifyEmail(event.email)
|
|
373
|
+
persistState("EMAIL_VERIFICATION", event.email)
|
|
316
374
|
break
|
|
317
375
|
|
|
318
376
|
// Verification success without token → sign in
|
|
319
377
|
case "VERIFICATION_SUCCESS":
|
|
320
378
|
if (!event.session?.token) {
|
|
321
379
|
setCurrentView("SIGN_IN")
|
|
380
|
+
clearPersistedState()
|
|
381
|
+
}
|
|
382
|
+
// If token present, clear state - auth succeeded
|
|
383
|
+
if (event.session?.token) {
|
|
384
|
+
clearPersistedState()
|
|
322
385
|
}
|
|
323
|
-
// If token present, let session state handle UI
|
|
324
386
|
break
|
|
325
387
|
|
|
326
388
|
// Password reset flow
|
|
327
389
|
case "PASSWORD_RESET_SUCCESS":
|
|
328
390
|
setCurrentView("SIGN_IN")
|
|
391
|
+
clearPersistedState()
|
|
329
392
|
break
|
|
330
393
|
|
|
331
394
|
// View toggles (footer links) - handled by InternalLink
|
|
332
395
|
case "VIEW_CHANGE":
|
|
333
396
|
setCurrentView(event.view)
|
|
334
397
|
if (event.email) setVerifyEmail(event.email)
|
|
398
|
+
// Only persist non-initial views
|
|
399
|
+
if (event.view !== "SIGN_IN") {
|
|
400
|
+
persistState(event.view, event.email)
|
|
401
|
+
} else {
|
|
402
|
+
clearPersistedState()
|
|
403
|
+
}
|
|
335
404
|
break
|
|
336
405
|
|
|
337
|
-
// AUTH_SUCCESS - let React re-render via useSession
|
|
338
|
-
|
|
406
|
+
// AUTH_SUCCESS - clear persisted state, let React re-render via useSession
|
|
407
|
+
case "AUTH_SUCCESS":
|
|
408
|
+
clearPersistedState()
|
|
409
|
+
break
|
|
339
410
|
}
|
|
340
411
|
}
|
|
341
412
|
},
|
|
@@ -77,6 +77,7 @@ export function AuthView({
|
|
|
77
77
|
}: AuthViewProps) {
|
|
78
78
|
const isHydrated = useIsHydrated()
|
|
79
79
|
const {
|
|
80
|
+
authFlowMode,
|
|
80
81
|
basePath,
|
|
81
82
|
credentials,
|
|
82
83
|
localization: contextLocalization,
|
|
@@ -421,6 +422,25 @@ export function AuthView({
|
|
|
421
422
|
: localization.SIGN_IN}
|
|
422
423
|
</Button>
|
|
423
424
|
</Link>
|
|
425
|
+
) : authFlowMode === "internal" ? (
|
|
426
|
+
<Link
|
|
427
|
+
className={cn(
|
|
428
|
+
"text-foreground underline",
|
|
429
|
+
classNames?.footerLink
|
|
430
|
+
)}
|
|
431
|
+
href={`${basePath}/${viewPaths.SIGN_IN}${isHydrated ? window.location.search : ""}`}
|
|
432
|
+
>
|
|
433
|
+
<Button
|
|
434
|
+
variant="link"
|
|
435
|
+
size="sm"
|
|
436
|
+
className={cn(
|
|
437
|
+
"px-0 text-foreground underline",
|
|
438
|
+
classNames?.footerLink
|
|
439
|
+
)}
|
|
440
|
+
>
|
|
441
|
+
{localization.GO_BACK}
|
|
442
|
+
</Button>
|
|
443
|
+
</Link>
|
|
424
444
|
) : (
|
|
425
445
|
<Button
|
|
426
446
|
variant="link"
|