Order the latest-reviews strip by created_at alone. #9

Merged
Michilis merged 1 commits from dev into main 2026-08-25 15:45:41 +00:00
Showing only changes of commit 1eade490c8 - Show all commits
+16 -28
View File
@@ -55,19 +55,6 @@ export interface BuildReview {
* characters or matching a common test string do not qualify. They still appear in * 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. * 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([ const TEST_STRINGS = new Set([
'test', 'testing', 'test test', 'hello', 'hi', 'ok', 'okay', 'good', 'nice', 'test', 'testing', 'test test', 'hello', 'hi', 'ok', 'okay', 'good', 'nice',
'great', 'cool', 'gm', 'a', '.', '..', '...', 'asdf', 'qwerty', '123', 'great', 'cool', 'gm', 'a', '.', '..', '...', 'asdf', 'qwerty', '123',
@@ -227,29 +214,30 @@ export async function fetchLatestReviews(
profile: null, 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)); const profiles = await fetchProfiles(candidates.map((c) => c.pubkey));
for (const review of candidates) review.profile = profiles.get(review.pubkey) ?? null; for (const review of candidates) review.profile = profiles.get(review.pubkey) ?? null;
/* /*
* Recent reviews with a named author come first; everything else stays newest * Strict recency, newest first, as the last thing that happens to this list.
* first behind them.
* *
* Most review keys have published exactly one event in their life: the review * The heading says "Latest reviews", so the order under it is the event
* itself. Nothing can be fetched for them, so a strict recency order fills this * `created_at` and nothing else. This used to float reviews whose author had a
* strip with anonymous npubs while a named review sits a few rows below the * kind 0 to the front, which read as broken on the page: a named review from two
* cut. This is the home page shop window, which already drops junk bodies, so * months ago sat between two reviews from this week, and the card feet said so,
* it prefers a review a reader can attach a person to. Nothing is hidden: * because the "2mo ago" label is formatted from the very same `created_at` this
* every review is on its mint page, and PROMOTE_MAX_AGE_S keeps "latest" * sorts on. Author names are still resolved and still shown — they just no longer
* meaning latest. * 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; candidates.sort((a, b) => b.created_at - a.created_at || a.id.localeCompare(b.id));
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);
return candidates.slice(0, limit); return candidates.slice(0, limit);
} catch { } catch {