Skip to content

feat(settings): show a loading state and lock other options during passkey sign-in - #20933

Draft
vpomerleau wants to merge 1 commit into
mainfrom
FXA-14267
Draft

feat(settings): show a loading state and lock other options during passkey sign-in#20933
vpomerleau wants to merge 1 commit into
mainfrom
FXA-14267

Conversation

@vpomerleau

Copy link
Copy Markdown
Contributor

Because

  • After a passkey verification succeeded, the sign-in surface stayed on screen with no success indication while navigation was pending, and the passkey button reverted to a clickable state — which was confusing and invited unintended retries (first noticed on the email-first flow).
  • While one sign-in method was in flight, the other options on the same surface stayed interactive, so a user could start a competing attempt and race the one already running.

This pull request

  • Adds an isNavigating state to usePasskeySignIn in packages/fxa-settings/src/lib/passkeys/signin-flow.ts, raised only on committed-success paths (never on an error), and passes loading={passkey.isNavigating} to AppLayout on all four passkey sign-in surfaces (Index, Signin, SigninAlternativeAuthOptions, SigninPasswordlessCode) so the page shows a spinner through navigation / the WebChannel handoff instead of a reverted button.
  • Adds a bidirectional form-lock so each surface disables its other sign-in methods (password/OTP submit, third-party auth, passkey) while any one attempt is in flight, via new optional disabled props on ButtonPasskeySignin, ThirdPartyAuth, AlternativeAuthOptions, and FormVerifyCode.
  • On the Signin page, tracks the password attempt separately (signinAttemptInFlight) so a failed password releases the passkey/third-party lock immediately, while the submit button keeps its existing disabled-until-edited behaviour.
  • Adds unit tests for the new hook state, the disabled props on the shared components, and the Signin/OTP form-lock behaviour.

Issue that this pull request solves

Closes: FXA-14267

Checklist

Put an x in the boxes that apply

  • My commit is GPG signed.
  • If applicable, I have modified or added tests which pass locally.
  • I have added necessary documentation (if appropriate).
  • I have verified that my changes render correctly in RTL (if appropriate).
  • I have manually reviewed all AI generated code.

How to review (Optional)

  • Key files/areas to focus on: packages/fxa-settings/src/lib/passkeys/signin-flow.ts — the isNavigating state machine and its error-path resets.
  • Suggested review order: hook (signin-flow.ts) → shared components (ButtonPasskeySignin, ThirdPartyAuth, AlternativeAuthOptions, FormVerifyCode) → surfaces (Index, Signin, SigninPasswordlessCode, SigninAlternativeAuthOptions).
  • Risky or complex parts: the hook's success-vs-error branching — isNavigating is set only after navigation is committed, and every cancel/error path routes through finish() to reset the loading/disabled state.

Screenshots (Optional)

Please attach the screenshots of the changes made in case of change in user interface.

Other information (Optional)

Any other information that is important to this pull request.

…sskey sign-in

Because:
- After passkey verification succeeded, the surface stayed rendered with no
  success indication while navigation was pending; the passkey button reverted
  to a clickable state, which was confusing and invited unintended retries.
- Other sign-in options stayed interactive while a sign-in attempt was in
  flight, so a competing method could race the one already running.

This commit:
- Adds an isNavigating state to usePasskeySignIn, raised only on committed
  success (never on an error), and renders AppLayout's loading spinner on all
  four passkey sign-in surfaces while navigation or the WebChannel handoff is
  pending.
- Adds a form-lock that disables the other sign-in options on a surface while a
  submit or passkey ceremony is in flight, via new optional disabled props on
  ButtonPasskeySignin, ThirdPartyAuth, AlternativeAuthOptions, and
  FormVerifyCode.

Closes #FXA-14267

Copilot AI 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.

Pull request overview

Adds a clearer “in progress” UX for passkey sign-in in fxa-settings by introducing a page-level loading state during the post-verification navigation/handoff, and by locking out competing sign-in methods on the same surface while any one attempt is in flight.

Changes:

  • Added isNavigating to usePasskeySignIn and wired it into sign-in surfaces via AppLayout loading=... so success doesn’t “snap back” to an interactive state while navigation/handoff is pending.
  • Implemented a bidirectional form-lock by threading new optional disabled props through passkey / third-party / OTP verification components and using them from the sign-in pages.
  • Added/updated unit tests to cover the new hook state and the new lock/disabled behaviors.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/fxa-settings/src/pages/Signin/SigninPasswordlessCode/index.tsx Mirrors OTP submit state to lock passkey/resend actions during OTP verification; shows page-level loading during passkey navigation.
packages/fxa-settings/src/pages/Signin/SigninPasswordlessCode/index.test.tsx Adds tests ensuring passkey/resend are disabled during OTP verification and re-enabled on failure.
packages/fxa-settings/src/pages/Signin/index.tsx Separates “attempt in flight” from lingering submit-disable state; locks alternative auth options while attempts run; shows page-level loading for passkey navigation.
packages/fxa-settings/src/pages/Signin/index.test.tsx Adds test verifying alternatives remain enabled after a failed password attempt.
packages/fxa-settings/src/pages/Signin/components/SigninAlternativeAuthOptions/index.tsx Locks options while a passkey ceremony is running and enables page-level loading during navigation.
packages/fxa-settings/src/pages/Index/index.tsx Locks email-first submit and alternative options while passkey or email submission is in flight; enables page-level loading during passkey navigation.
packages/fxa-settings/src/lib/passkeys/signin-flow.ts Introduces isNavigating state and sets it only on committed-success navigation/handoff paths.
packages/fxa-settings/src/lib/passkeys/signin-flow.test.tsx Adds coverage for isNavigating behavior across success, navigation error, required-password routing, cancellation, and WebChannel cases.
packages/fxa-settings/src/components/ThirdPartyAuth/index.tsx Adds disabled prop to prevent starting provider flows while another method is in flight.
packages/fxa-settings/src/components/ThirdPartyAuth/index.test.tsx Tests that disabled third-party buttons don’t trigger provider flow or navigation.
packages/fxa-settings/src/components/ThirdPartyAuth/snapshots/index.test.tsx.snap Updates snapshots for new disabled styling classes on icon buttons.
packages/fxa-settings/src/components/FormVerifyCode/mocks.tsx Extends mock Subject to accept and pass through isThrottled and disabled.
packages/fxa-settings/src/components/FormVerifyCode/index.tsx Adds external disabled prop and guards submit entry points (button + paste auto-submit) against external locks.
packages/fxa-settings/src/components/FormVerifyCode/index.test.tsx Adds test ensuring disabled keeps submit disabled even with a valid code.
packages/fxa-settings/src/components/ButtonPasskeySignin/index.tsx Adds disabled prop to lock the passkey button without showing the loading spinner.
packages/fxa-settings/src/components/ButtonPasskeySignin/index.test.tsx Adds tests for disabled-without-spinner behavior and click suppression when disabled.
packages/fxa-settings/src/components/AlternativeAuthOptions/index.tsx Adds disabled prop and wires it into passkey + third-party subcomponents for surface-level locking.
packages/fxa-settings/src/components/AlternativeAuthOptions/index.test.tsx Adds tests verifying disabled locks passkey and third-party options, and leaves them enabled otherwise.

Comment on lines 161 to 163
setSigninLoading(true);
setSigninAttemptInFlight(true);
const { data, error } = await beginSigninHandler(email, password);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants