fix(web): guard invite-settings activate button against double-click#38337
Merged
crazywoola merged 1 commit intoJul 8, 2026
Merged
Conversation
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.
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. |
Contributor
Author
|
@crazywoola Thanks for the guidance. Opened issue #38410 describing the invite-settings "Activate" button double-click / Enter-key bug, the proposed |
crazywoola
approved these changes
Jul 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #38410
Summary
The
/signin/invite-settingsactivate button callshandleActivate()which awaitsactivateMember(...). 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 tohandleActivateviaonKeyDown) can submit the request multiple times. The backend may issue duplicate workspace-membership rows or, worse, race thesetLocaleOnClient+router.replaceside effects.This change adds an
isActivatingstate, sets it totrueat the top ofhandleActivate(), resets it in the catch block (alongside the existingrecheck()), and binds it to the activate button vialoading={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:const [isActivating, setIsActivating] = useState(false).handleActivate():setIsActivating(true)before theawait activateMember(...)call;setIsActivating(false)in thecatchblock alongsiderecheck().<Button>: addloading={isActivating} disabled={isActivating}.useCallbackdeps to includeisActivatingforreact-hooks/exhaustive-deps.The pre-existing
if (!checkRes) return <Loading />guard is preserved verbatim (it still works becauseuseInvitationCheckreturns{ data: undefined, isPending: true }until the first response arrives ??i.e.checkResis undefined, not null).Diff stat
Test plan
/signin/invite-settings?invite_token=...with a valid token ??fill name ??click activate twice quickly ??confirm only onePOST /activaterequest fires (DevTools network panel).isActivatingresets and the button re-enables for retry.pnpm lintandpnpm type-checkclean.Risk / behavior preservation
res.result === 'success'??router.replace) is unchanged.catchblock still callsrecheck()to refresh the invitation validity check; we just additionally resetisActivatingso the user can retry.Related
peaks-loopsession2026-07-02-session-95565cduring a project scan on 2026-07-02.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