History

Prevent duplicate sends while composer request is in flight

- Add `sendInFlightRef` guard in `ChatView` send handler - Set sending state before async work and always clear it in `finally` - Wrap early send flow in one `try/finally` to avoid double-submit races

Julius Marminge committed 8 months ago

Commit 69dbc5d1 parent 645aaa51 file · +15 −22Browse files

Unified · Split

apps/web/src/components/ChatView.tsx+15−22
@@ -1401,6 +1401,20 @@ export default function ChatView({ threadId }: ChatViewProps) { · 1400 unmodified lines
1401 isFirstMessage && envMode === "worktree" && !activeThread.worktreePath1401 isFirstMessage && envMode === "worktree" && !activeThread.worktreePath
1402 ? activeThread.branch1402 ? activeThread.branch
1403 : null;1403 : null;
1404
1405 // In worktree mode, require an explicit base branch so we don't silently
1406 // fall back to local execution when branch selection is missing.
1407 const shouldCreateWorktree =
1408 isFirstMessage && envMode === "worktree" && !activeThread.worktreePath;
1409 if (shouldCreateWorktree && !activeThread.branch) {
1410 dispatch({
1411 type: "SET_ERROR",
1412 threadId: threadIdForSend,
1413 error: "Select a base branch before sending in New worktree mode.",
1414 });
1415 return;
1416 }
1417
1404 const composerImagesSnapshot = [...composerImages];1418 const composerImagesSnapshot = [...composerImages];
1405 const messageIdForSend = newMessageId();1419 const messageIdForSend = newMessageId();
1406 const messageCreatedAt = new Date().toISOString();1420 const messageCreatedAt = new Date().toISOString();
@@ -1424,19 +1438,6 @@ export default function ChatView({ threadId }: ChatViewProps) { · 17 unmodified lines
1424 },1438 },
1425 ]);1439 ]);
1426 1440
1427 // In worktree mode, require an explicit base branch so we don't silently
1428 // fall back to local execution when branch selection is missing.
1429 const shouldCreateWorktree =
1430 activeThread.messages.length === 0 && envMode === "worktree" && !activeThread.worktreePath;
1431 if (shouldCreateWorktree && !activeThread.branch) {
1432 dispatch({
1433 type: "SET_ERROR",
1434 threadId: activeThread.id,
1435 error: "Select a base branch before sending in New worktree mode.",
1436 });
1437 return;
1438 }
1439
1440 setThreadError(threadIdForSend, null);1441 setThreadError(threadIdForSend, null);
1441 promptRef.current = "";1442 promptRef.current = "";
1442 setPrompt("");1443 setPrompt("");
@@ -1462,17 +1463,9 @@ export default function ChatView({ threadId }: ChatViewProps) { · 19 unmodified lines
1462 branch: result.worktree.branch,1463 branch: result.worktree.branch,
1463 worktreePath: result.worktree.path,1464 worktreePath: result.worktree.path,
1464 });1465 });
1465 // Keep local thread state in sync immediately so terminal drawer opens
1466 // with the worktree cwd/env instead of briefly using the project root.
1467 dispatch({
1468 type: "SET_THREAD_BRANCH",
1469 threadId: threadIdForSend,
1470 branch: result.worktree.branch,
1471 worktreePath: result.worktree.path,
1472 });
1473 const setupScript = setupProjectScript(activeProject.scripts);1466 const setupScript = setupProjectScript(activeProject.scripts);
1474 if (setupScript) {1467 if (setupScript) {
1475 await runProjectScript(setupScript, {1468 void runProjectScript(setupScript, {
1476 cwd: result.worktree.path,1469 cwd: result.worktree.path,
1477 worktreePath: result.worktree.path,1470 worktreePath: result.worktree.path,
1478 rememberAsLastInvoked: false,1471 rememberAsLastInvoked: false,