Skip to content

fix(web): guard invite-settings activate button against double-click#38337

Merged
crazywoola merged 1 commit into
langgenius:mainfrom
SquabbyZ:fix/invite-settings-button-double-click
Jul 8, 2026
Merged

fix(web): guard invite-settings activate button against double-click#38337
crazywoola merged 1 commit into
langgenius:mainfrom
SquabbyZ:fix/invite-settings-button-double-click

Conversation

@SquabbyZ

@SquabbyZ SquabbyZ commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Fixes #38410

Summary

The /signin/invite-settings activate button calls handleActivate() which awaits activateMember(...). Until the call resolves the button stays enabled, so a fast user (or an accidental Enter keypress in the Name input ??the input already wires Enter to handleActivate via onKeyDown) can submit the request multiple times. The backend may issue duplicate workspace-membership rows or, worse, race the setLocaleOnClient + router.replace side effects.

This change adds an isActivating state, sets it to true at the top of handleActivate(), resets it in the catch block (alongside the existing recheck()), and binds it to the activate button via loading={isActivating} disabled={isActivating} so the button visually deactivates and ignores further clicks while the request is in flight.

Changes

  • web/app/signin/invite-settings/page.tsx:
    • Add const [isActivating, setIsActivating] = useState(false).
    • In handleActivate(): setIsActivating(true) before the await activateMember(...) call; setIsActivating(false) in the catch block alongside recheck().
    • On the activate <Button>: add loading={isActivating} disabled={isActivating}.
    • Update useCallback deps to include isActivating for react-hooks/exhaustive-deps.

The pre-existing if (!checkRes) return <Loading /> guard is preserved verbatim (it still works because useInvitationCheck returns { data: undefined, isPending: true } until the first response arrives ??i.e. checkRes is undefined, not null).

Diff stat

web/app/signin/invite-settings/page.tsx | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

Test plan

  • Manual: open /signin/invite-settings?invite_token=... with a valid token ??fill name ??click activate twice quickly ??confirm only one POST /activate request fires (DevTools network panel).
  • Manual: click activate ??before it resolves, press Enter in the Name input ??confirm button is disabled, no second request.
  • Manual: simulate a backend 500 ??confirm isActivating resets and the button re-enables for retry.
  • pnpm lint and pnpm type-check clean.

Risk / behavior preservation

  • The happy path (res.result === 'success' ??router.replace) is unchanged.
  • The catch block still calls recheck() to refresh the invitation validity check; we just additionally reset isActivating so the user can retry.
  • No backend, schema, i18n, or migration changes.

Related

Automation

Generated via peaks-loop (full-auto mode). Red-line scope was limited to web/app/signin/invite-settings/page.tsx. Commit message includes the peaks-loop provenance line for traceability.

?? Generated with Claude Code via peaks-loop

The /signin/invite-settings activate button calls handleActivate()
which awaits activateMember(...). Until the call resolves the button
stays enabled, so a fast user (or accidental Enter keypress in the
Name input, which the input already wires to handleActivate via
onKeyDown) can submit the request multiple times. The backend will
issue duplicate workspace-membership rows or, worse, race the
setLocaleOnClient + router.replace side effects.

This change adds an isActivating state, sets it to true at the top
of handleActivate(), resets it in the catch block (alongside the
existing recheck()), and binds it to the activate button via
loading={isActivating} disabled={isActivating} so the button
visually deactivates and ignores further clicks while the request
is in flight.

useCallback's dependency array is updated to include isActivating
to satisfy react-hooks/exhaustive-deps.

The pre-existing 'if (!checkRes) return <Loading />' guard is
preserved verbatim (it still works because useInvitationCheck
returns { data: undefined, isPending: true } until the first
response arrives — i.e. checkRes is undefined, not null).

diff --stat:
  web/app/signin/invite-settings/page.tsx | 5 +++++
  1 file changed, 5 insertions(+)

Generated via peaks-loop (session 2026-07-02-session-95565c, request
2026-07-02-fix-invite-settings-double-click). Red-line scope was
limited to invite-settings/page.tsx.
@SquabbyZ SquabbyZ requested review from douxc and iamjoel as code owners July 2, 2026 15:51
@dosubot dosubot Bot added the size:XS This PR changes 0-9 lines, ignoring generated files. label Jul 2, 2026
@github-actions github-actions Bot added the web This relates to changes on the web. label Jul 2, 2026

Copy link
Copy Markdown
Member

Thanks for the contribution. Before we review this PR, please open an issue first to describe the problem, expected behavior, and proposed approach, then link that issue here. This helps us confirm the scope and align on the fix before moving forward.

@crazywoola crazywoola self-assigned this Jul 3, 2026
@SquabbyZ

SquabbyZ commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

@crazywoola Thanks for the guidance. Opened issue #38410 describing the invite-settings "Activate" button double-click / Enter-key bug, the proposed isActivating state guard, and the risk analysis (happy path unchanged). PR description now links the issue via Fixes #38410. Ready for re-review.

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Jul 8, 2026
@crazywoola crazywoola enabled auto-merge July 8, 2026 06:23
@crazywoola crazywoola added this pull request to the merge queue Jul 8, 2026
Merged via the queue into langgenius:main with commit 3523da5 Jul 8, 2026
34 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm This PR has been approved by a maintainer size:XS This PR changes 0-9 lines, ignoring generated files. web This relates to changes on the web.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

invite-settings "Activate" button fires multiple parallel POST /activate requests on Enter + quick clicks

2 participants