From 2dd9c82a42de90765ff619d04be907ef6f869070 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Wed, 28 Oct 2026 21:20:51 -0700 Subject: [PATCH] fix(github): ignore overlapping resolve on an in-flight review thread (#35655) Track in-flight resolve/unresolve requests per comments cacheKey + threadId in the store. A second request on the same thread no-ops until the first settles, and the Checks panel ResolveButton shows pending from the store so the remounted button after the optimistic section move cannot fire the opposite request. Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../checks-panel/comment-controls.tsx | 6 +- .../use-checks-panel-comment-resolution.tsx | 5 + .../src/store/github/review-thread-actions.ts | 143 ++++++++++-------- .../review-thread-resolution-in-flight.ts | 26 ++++ src/renderer/src/store/github/slice-types.ts | 2 + .../github-review-thread-rollback.test.ts | 31 ++++ src/renderer/src/store/slices/github.ts | 1 + 7 files changed, 149 insertions(+), 65 deletions(-) create mode 100644 src/renderer/src/store/github/review-thread-resolution-in-flight.ts diff --git a/src/renderer/src/components/right-sidebar/checks-panel/comment-controls.tsx b/src/renderer/src/components/right-sidebar/checks-panel/comment-controls.tsx index db49fca4ee0..89c3e54d2c3 100644 --- a/src/renderer/src/components/right-sidebar/checks-panel/comment-controls.tsx +++ b/src/renderer/src/components/right-sidebar/checks-panel/comment-controls.tsx @@ -18,6 +18,8 @@ import { DropdownMenuTrigger } from '@/components/ui/dropdown-menu' import { cn } from '@/lib/utils' +import { useAppStore } from '@/store' +import { isReviewThreadResolutionInFlight } from '@/store/github/review-thread-resolution-in-flight' import { basename } from '@/lib/path' import { isBotPRComment } from '../../../../../shared/pr-comment-audience' import { setPRBotAuthorOverride } from '@/lib/pr-bot-author-overrides' @@ -99,6 +101,8 @@ export function ResolveButton({ onResolve: (threadId: string, resolve: boolean) => boolean | Promise }): React.JSX.Element { const [loading, setLoading] = useState(false) + // Why: the optimistic move remounts this button in the other section, losing local loading state. + const inFlight = useAppStore((s) => isReviewThreadResolutionInFlight(s, threadId)) const loadingResetTimerRef = useRef(null) const clearLoadingResetTimer = useCallback((): void => { @@ -129,7 +133,7 @@ export function ResolveButton({ return ( - {loading ? ( + {loading || inFlight ? ( ) : (