mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-08 14:41:28 -07:00
adjust "top" window
This commit is contained in:
@@ -352,23 +352,29 @@ async function publicInventions(db: D1Database, featuredOnly = false): Promise<S
|
||||
return results.map((r) => JSON.parse(r.data) as SavedInvention)
|
||||
}
|
||||
|
||||
/** Midnight UTC today, as the ISO timestamp `acquired_at` is compared against. */
|
||||
function startOfUtcDay(): string {
|
||||
return `${new Date().toISOString().slice(0, 10)}T00:00:00.000Z`
|
||||
/** Length of the "today" window — a trailing day, not the calendar one. */
|
||||
const TOP_TODAY_WINDOW_MS = 24 * 60 * 60 * 1000
|
||||
|
||||
/** 24 hours ago, as the ISO timestamp `acquired_at` is compared against. */
|
||||
function startOfWindow(): string {
|
||||
return new Date(Date.now() - TOP_TODAY_WINDOW_MS).toISOString()
|
||||
}
|
||||
|
||||
/**
|
||||
* The "top today" feed — the inventions other players picked up TODAY, most first.
|
||||
* The "top today" feed — the inventions other players picked up in the last 24 hours,
|
||||
* most first.
|
||||
*
|
||||
* Ranked from the acquisitions the `econ` worker records in `inventory_invention` at
|
||||
* purchase time, grouped by invention, rather than from the lifetime counters on the
|
||||
* invention itself: those never reset, so "top today" used to mean "top ever" and the
|
||||
* shelf only changed when something overtook a total built up over months.
|
||||
*
|
||||
* "Today" is the UTC day, matching the timestamps econ writes. The day therefore rolls
|
||||
* over at 00:00 UTC wherever the player is, and the feed IS EMPTY until the first
|
||||
* acquisition of that day — nothing stands in for it, the same way the featured feed
|
||||
* serves nothing while nothing is curated.
|
||||
* "Today" is a TRAILING 24 hours, not the calendar UTC day, so the feed doesn't empty
|
||||
* itself at midnight UTC and slowly refill through the small hours — it always covers a
|
||||
* full day's worth of activity. It is still genuinely a window: an invention nobody has
|
||||
* picked up since yesterday falls off, and the feed IS EMPTY when nothing at all was
|
||||
* acquired in a day. Nothing stands in for it, the same way the featured feed serves
|
||||
* nothing while nothing is curated.
|
||||
*
|
||||
* An acquired invention that has since been unpublished or hidden drops out: this is a
|
||||
* public feed, so it is filtered like every other one. Paginated via skip/take AFTER
|
||||
@@ -379,7 +385,7 @@ export async function getTopInventions(
|
||||
skip: number,
|
||||
take: number
|
||||
): Promise<SavedInvention[]> {
|
||||
const counts = await getInventionAcquisitionCounts(db, startOfUtcDay())
|
||||
const counts = await getInventionAcquisitionCounts(db, startOfWindow())
|
||||
if (counts.length === 0) return []
|
||||
|
||||
// getInventionsByIds answers in the order it is asked, so the ranking survives the
|
||||
|
||||
@@ -622,7 +622,7 @@ export const avatarRoutes = new Hono<App>({ strict: false })
|
||||
}
|
||||
)
|
||||
|
||||
// The "top today" invention feed — the inventions most acquired since 00:00 UTC,
|
||||
// The "top today" invention feed — the inventions most acquired in the last 24 hours,
|
||||
// counted from the purchase rows the `econ` worker writes. A real day window, so an
|
||||
// empty list is a quiet day rather than a bug. Paginated via skip/take (take defaults
|
||||
// to 50, as the client asks for). Bare array.
|
||||
@@ -632,10 +632,11 @@ export const avatarRoutes = new Hono<App>({ strict: false })
|
||||
tags: ['Inventions'],
|
||||
summary: 'The “top today” feed',
|
||||
description:
|
||||
'Published inventions ranked by how many players acquired them TODAY (since ' +
|
||||
'00:00 UTC), counted from the purchase records — free grants included, one per ' +
|
||||
'player per invention. Genuinely a day window: empty until the day’s first ' +
|
||||
'acquisition, and it resets at midnight UTC.',
|
||||
'Published inventions ranked by how many players acquired them in the last 24 ' +
|
||||
'hours, counted from the purchase records — free grants included, one per ' +
|
||||
'player per invention. Genuinely a window: an invention nobody has picked up ' +
|
||||
'since yesterday falls off, and a day with no acquisitions at all serves an ' +
|
||||
'empty list. It trails the clock rather than resetting at midnight.',
|
||||
parameters: pageParams(50),
|
||||
responses: { 200: json(InventionDto.array(), 'The top inventions') },
|
||||
}),
|
||||
|
||||
@@ -1184,22 +1184,23 @@ describe('public endpoints', () => {
|
||||
.run()
|
||||
}
|
||||
|
||||
// Today's acquisitions, which is what "top today" now counts: 201 picked up by three
|
||||
// Recent acquisitions, which is what "top today" now counts: 201 picked up by three
|
||||
// players, 203 by one. 204/205 are acquired too — an unpublished and a hidden
|
||||
// invention can still be owned — and must not surface in a public feed.
|
||||
for (const accountId of [7001, 7002, 7003]) await grantInvention(env.DB, accountId, 201)
|
||||
await grantInvention(env.DB, 7001, 203)
|
||||
await grantInvention(env.DB, 7001, 204)
|
||||
await grantInvention(env.DB, 7002, 205)
|
||||
// 202 was acquired, but not today — the window is the current UTC day, so it is out.
|
||||
// 202 was acquired 25 hours ago, just past the trailing 24-hour window, so it is out —
|
||||
// the feed really does forget, rather than accumulating every acquisition ever.
|
||||
await env.DB.prepare(
|
||||
'INSERT INTO inventory_invention (account_id, invention_id, acquired_at) VALUES (?1, ?2, ?3)'
|
||||
)
|
||||
.bind(7004, 202, '2020-01-01T00:00:00.000Z')
|
||||
.bind(7004, 202, new Date(Date.now() - 25 * 60 * 60 * 1000).toISOString())
|
||||
.run()
|
||||
|
||||
// Top: most acquisitions today first. Download counts no longer rank anything — 202
|
||||
// has the biggest of them and is absent entirely.
|
||||
// Top: most acquisitions in the window first. Download counts no longer rank anything —
|
||||
// 202 has the biggest of them and is absent entirely.
|
||||
const top = await ids(await exports.default.fetch(`${ORIGIN}/api/inventions/v1/toptoday`))
|
||||
expect(top).toEqual([201, 203])
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ export async function ownsInvention(
|
||||
/**
|
||||
* How many times each invention was acquired at or after `since`, most-acquired first
|
||||
* (ties broken by newest invention, so paging is stable). Backs the `api` worker's "top
|
||||
* today" feed, which passes the start of the current UTC day.
|
||||
* today" feed, which passes 24 hours ago.
|
||||
*
|
||||
* `acquired_at` holds `toISOString()` output, which is fixed-width UTC, so a lexical
|
||||
* `>=` on the string is a chronological comparison — no date parsing in SQL.
|
||||
|
||||
Reference in New Issue
Block a user