From d3d467c1f8c51efc77de90a02c94452f02418222 Mon Sep 17 00:00:00 2001 From: smartcalsystems-sketch Date: Wed, 14 Oct 2026 00:12:01 +0530 Subject: [PATCH] fix: show the claimant their own claim, not the loser's message (closes #699) When status === 'claimed', ClaimButton showed "This bounty has already been claimed" / "Claimed by: ..." to everyone, including the person who claimed it -- once the 5s success notice expires, or on any later visit, the contributor doing the work sees wording written for the people who lost the race, with no guidance on what to do next. Compare bounty.claimedById against user.id: the claimant now sees a distinct "You're working on this" panel with the Fixes #N PR instruction, while everyone else (a different signed-in user, or signed out) still sees the existing generic already-claimed view. --- src/components/bounty/ClaimButton.test.tsx | 45 ++++++++++++++++++++++ src/components/bounty/ClaimButton.tsx | 24 ++++++++++++ 2 files changed, 69 insertions(+) diff --git a/src/components/bounty/ClaimButton.test.tsx b/src/components/bounty/ClaimButton.test.tsx index 9c49b61..835cb75 100644 --- a/src/components/bounty/ClaimButton.test.tsx +++ b/src/components/bounty/ClaimButton.test.tsx @@ -108,6 +108,51 @@ describe("ClaimButton", () => { expect(screen.queryByText(/Claimed by:/)).not.toBeInTheDocument(); }); + describe("the claimant's own view of their claim (#699)", () => { + it("shows 'You're working on this' with PR instructions instead of the loser's message", () => { + mockAuth({ id: "user-1", stellarAddress: "GSTELLAR" }); + mockHooks( + {}, + { + status: "claimed", + bounty: { id: "b1", status: "claimed", claimedById: "user-1", issueNumber: 42 }, + }, + ); + render(); + + expect(screen.getByText("You're working on this")).toBeInTheDocument(); + expect(screen.getByText("Fixes #42")).toBeInTheDocument(); + expect(screen.queryByText(/This bounty has already been/)).not.toBeInTheDocument(); + }); + + it("still shows the generic already-claimed view to a different signed-in user", () => { + mockAuth({ id: "user-2", stellarAddress: "GOTHER" }); + mockHooks( + {}, + { + status: "claimed", + bounty: { id: "b1", status: "claimed", claimedById: "user-1", claimedBy: "alice" }, + }, + ); + render(); + + expect(screen.getByText(/This bounty has already been/)).toBeInTheDocument(); + expect(screen.queryByText("You're working on this")).not.toBeInTheDocument(); + }); + + it("shows the generic already-claimed view when signed out", () => { + mockAuth(null); + mockHooks( + {}, + { status: "claimed", bounty: { id: "b1", status: "claimed", claimedById: "user-1" } }, + ); + render(); + + expect(screen.getByText(/This bounty has already been/)).toBeInTheDocument(); + expect(screen.queryByText("You're working on this")).not.toBeInTheDocument(); + }); + }); + it("shows the auto-refresh hint only while polling", () => { mockHooks({}, { isPolling: true }); render(); diff --git a/src/components/bounty/ClaimButton.tsx b/src/components/bounty/ClaimButton.tsx index 3e11d4c..f867801 100644 --- a/src/components/bounty/ClaimButton.tsx +++ b/src/components/bounty/ClaimButton.tsx @@ -186,6 +186,30 @@ export function ClaimButton({ const needsPayoutWallet = !!user && !user.stellarAddress; if (status === 'claimed') { + // The claimant sees this same view on every later visit too, not just + // right after the 5s success notice expires -- "already claimed" / + // "Claimed by: ..." reads like they lost a race they actually won, with + // no indication of what to do next (#699). + const isOwnClaim = !!user && !!bounty?.claimedById && bounty.claimedById === user.id; + + if (isOwnClaim) { + return ( +
+

You're working on this

+

+ Open a pull request that says{' '} + + Fixes #{bounty?.issueNumber} + {' '} + in its description. The reward releases automatically once it's merged. +

+
+ ); + } + return (