Demo data: three made-up repos and three people, recorded by the real Trail hooks.About TrailGitHub

On slow 3G the checkout button flickers and some people manage to place the order twice. Fix it. d02659b5

claudeLleo produced 1 commit · last active 7 days ago · main
Disable checkout button while the order is pending7 days agodc7bd64

Conversation behind dc7bd6474afa: Disable checkout button while the order is pending

User

On slow 3G the checkout button flickers and some people manage to place the order twice. Fix it.

Tool call: Read

{
 "file_path": "/home/dev/code/web-app/src/components/CheckoutButton.tsx"
}

Tool result

     1	export function CheckoutButton({ cart }: { cart: Cart }) {
     2	  const [loading, setLoading] = useState(false);
     3	
     4	  async function submit() {
     5	    setLoading(true);
     6	    await placeOrder(cart);
     7	    setLoading(false);
     8	  }
     9	
    10	  return <button onClick={submit}>{loading ? <Spinner /> : "Place order"}</button>;
    11	}

Assistant (thinking)

The button is never disabled, so a second tap during the request fires submit again. The flicker is the label swapping to a spinner, which changes the button width. Disable while pending, guard with a ref (state updates are async, two fast taps both see loading=false), and keep the width fixed.

Tool call: Edit

{
 "file_path": "/home/dev/code/web-app/src/components/CheckoutButton.tsx",
 "old_string": "  const [loading, setLoading] = useState(false);\n\n  async function submit() {\n    setLoading(true);\n    await placeOrder(cart);\n    setLoading(false);\n  }\n\n  return <button onClick={submit}>{loading ? <Spinner /> : \"Place order\"}</button>;\n",
 "new_string": "  const [loading, setLoading] = useState(false);\n  const inFlight = useRef(false); // two quick taps both see loading=false\n\n  async function submit() {\n    if (inFlight.current) return;\n    inFlight.current = true;\n    setLoading(true);\n    try {\n      await placeOrder(cart);\n    } finally {\n      inFlight.current = false;\n      setLoading(false);\n    }\n  }\n\n  return (\n    <button onClick={submit} disabled={loading} aria-busy={loading} style={{ minWidth: 140 }}>\n      {loading ? <Spinner /> : \"Place order\"}\n    </button>\n  );\n"
}

Tool result

The file src/components/CheckoutButton.tsx has been updated.

Tool call: Bash

{
 "command": "npx tsc --noEmit",
 "description": "Type check"
}

Tool result

Assistant

The button is disabled while the order is pending, a ref blocks a second tap that lands before the re-render, and a fixed min-width stops the flicker when the label swaps to the spinner.

Tool call: Bash

{
 "command": "git commit -am \"Disable checkout button while the order is pending\"",
 "description": "Commit"
}

Session

Harness
claude
ID
d02659b5-ec03-4d34-b363-1f0767df0d19
Branch
main
Stored in

Files

meta.jsontranscript.jsonltranscript.md

Each commit shows conversation.md: only the turns that led to it. Full transcript.