Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/lib/bounty-query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,30 @@ describe("applyBountyQuery", () => {
);
expect(result.items.map((b) => b.id)).toEqual(["z", "y", "x"]);
});

it("puts an already-expired deadline after every still-open one (#703)", () => {
const mixed = [
bounty({ id: "expired-recent", deadline: new Date(Date.now() - 3600000).toISOString(), reward: 1 }),
bounty({ id: "expired-long-ago", deadline: new Date(Date.now() - 86400000 * 30).toISOString(), reward: 2 }),
bounty({ id: "open-far", deadline: new Date(Date.now() + 86400000 * 7).toISOString(), reward: 3 }),
bounty({ id: "open-soon", deadline: new Date(Date.now() + 3600000).toISOString(), reward: 4 }),
bounty({ id: "no-deadline", deadline: null, reward: 5 }),
];
const result = applyBountyQuery(
mixed,
{ status: "all", difficulty: "all", asset: "all", sort: "deadline-asc", page: 1 },
10,
);
// Open bounties (soonest first), then expired ones, then no-deadline last —
// not a raw timestamp sort, which would put "expired-long-ago" first.
expect(result.items.map((b) => b.id)).toEqual([
"open-soon",
"open-far",
"expired-long-ago",
"expired-recent",
"no-deadline",
]);
});
});

describe("buildBountyQueryString", () => {
Expand Down
15 changes: 13 additions & 2 deletions src/lib/bounty-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,25 @@ function sortBounties(bounties: Bounty[], sort: SortOption): Bounty[] {
case "reward-asc":
sorted.sort((a, b) => a.reward - b.reward);
break;
case "deadline-asc":
case "deadline-asc": {
// Captured once so every comparison in this sort agrees on "now" (#703).
const now = Date.now();
sorted.sort((a, b) => {
if (!a.deadline && !b.deadline) return 0;
if (!a.deadline) return 1;
if (!b.deadline) return -1;
return new Date(a.deadline).getTime() - new Date(b.deadline).getTime();
const aTime = new Date(a.deadline).getTime();
const bTime = new Date(b.deadline).getTime();
const aExpired = aTime < now;
const bExpired = bTime < now;
// "Soonest" means soonest upcoming deadline. An already-expired
// deadline is never "soonest" — it ranks after every still-open one,
// regardless of how its raw timestamp compares.
if (aExpired !== bExpired) return aExpired ? 1 : -1;
return aTime - bTime;
});
break;
}
case "default":
break;
}
Expand Down