diff --git a/web/src/lib/nostr-build.ts b/web/src/lib/nostr-build.ts index b7d080b..3f0b1ee 100644 --- a/web/src/lib/nostr-build.ts +++ b/web/src/lib/nostr-build.ts @@ -55,19 +55,6 @@ export interface BuildReview { * characters or matching a common test string do not qualify. They still appear in * full on the mint page, nothing is hidden, this strip is just a shop window. */ -/** How many candidates to gather per card shown, so the ordering has a real choice. */ -const POOL_FACTOR = 12; - -/** - * How recent a review must still be to be promoted for having a named author. - * - * The ordering below prefers reviews whose author has a kind 0, and this is the leash - * on that preference. Without it the strip would headline a named review from two - * years ago under a heading that says "Latest reviews"; with it, a named review from - * within the season can lead and anything older cannot. - */ -const PROMOTE_MAX_AGE_S = 90 * 24 * 60 * 60; - const TEST_STRINGS = new Set([ 'test', 'testing', 'test test', 'hello', 'hi', 'ok', 'okay', 'good', 'nice', 'great', 'cool', 'gm', 'a', '.', '..', '...', 'asdf', 'qwerty', '123', @@ -227,29 +214,30 @@ export async function fetchLatestReviews( profile: null, }); - if (candidates.length >= limit * POOL_FACTOR) break; + // The scan runs newest first, so the first `limit` survivors are the latest + // `limit` reviews. Nothing further down the list can outrank them. + if (candidates.length >= limit) break; } const profiles = await fetchProfiles(candidates.map((c) => c.pubkey)); for (const review of candidates) review.profile = profiles.get(review.pubkey) ?? null; /* - * Recent reviews with a named author come first; everything else stays newest - * first behind them. + * Strict recency, newest first, as the last thing that happens to this list. * - * Most review keys have published exactly one event in their life: the review - * itself. Nothing can be fetched for them, so a strict recency order fills this - * strip with anonymous npubs while a named review sits a few rows below the - * cut. This is the home page shop window, which already drops junk bodies, so - * it prefers a review a reader can attach a person to. Nothing is hidden: - * every review is on its mint page, and PROMOTE_MAX_AGE_S keeps "latest" - * meaning latest. + * The heading says "Latest reviews", so the order under it is the event + * `created_at` and nothing else. This used to float reviews whose author had a + * kind 0 to the front, which read as broken on the page: a named review from two + * months ago sat between two reviews from this week, and the card feet said so, + * because the "2mo ago" label is formatted from the very same `created_at` this + * sorts on. Author names are still resolved and still shown — they just no longer + * decide the order. + * + * Sorted here rather than left to the scan above, so that the invariant holds + * whatever the gathering loop does later, and `id` breaks ties between two events + * that share a second so two builds of the same events agree. */ - const cutoff = Math.floor(Date.now() / 1000) - PROMOTE_MAX_AGE_S; - const promoted = (review: BuildReview): number => - review.profile?.found && review.created_at >= cutoff ? 1 : 0; - - candidates.sort((a, b) => promoted(b) - promoted(a) || b.created_at - a.created_at); + candidates.sort((a, b) => b.created_at - a.created_at || a.id.localeCompare(b.id)); return candidates.slice(0, limit); } catch {