feat(events): an event action's progress() on a live run's public page (MODULE_API 1.12.0)
An event action may declare an optional `progress(envelope)`: how its
step stands while the run is live. Core accepts two shapes and
interprets neither:
- a count: { label, left, of }, rendered "Bandits: 3 of 8 left";
- a percentage: { label, percent }, rendered "The Juggernaut: 62%".
How core asks:
- `events/progress.js` asks each step of a live run whose action has
one. It passes the step's own held ledger rows and the reader's
account id, so the module applies its own visibility.
- Each call has a 2 s deadline. A throw, a late answer or any other
shape is a line not shown, never an error on the page.
- One answer per run and reader is cached for 5 s.
- The public event's live occurrence carries the lines as `progress`.
The route takes an optional session for the reader.
- The page renders them under the phase label and re-reads them every
15 s while the event is live.
Core stays game-agnostic: no game's words are in it. Additive, so a
minor bump. Module-uo declares ^1.10.0, registers no action with
progress(), and its actions and pages are unchanged.
From RunicNPC stage 8 (D304, D305).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -36,6 +36,7 @@ const versionsDb = require('./eventVersions.db')
|
||||
const participantsDb = require('./eventRunParticipants.db')
|
||||
const calendarModel = require('./eventCalendar.model')
|
||||
const recurrence = require('../../events/recurrence')
|
||||
const progress = require('../../events/progress')
|
||||
|
||||
// The public calendar's window when a caller names neither end: a few days BACK
|
||||
// through a month out. A visitor arriving at /site/events wants "what is on", and
|
||||
@@ -289,7 +290,7 @@ const publicOccurrence = (run, spec) => ({
|
||||
* turn a stale link in a months-old mail into a dead end rather than a page
|
||||
* about the thing the mail was about.
|
||||
*/
|
||||
async function event(slug, { runId = null } = {}) {
|
||||
async function event(slug, { runId = null, userId = null } = {}) {
|
||||
const definition = await definitionsDb.getPublicBySlug(String(slug || ''))
|
||||
if (!definition) return { ok: false, status: 404, errors: ['Not found'] }
|
||||
|
||||
@@ -339,6 +340,13 @@ async function event(slug, { runId = null } = {}) {
|
||||
const specRun = named || live[0] || null
|
||||
const spec = specRun ? (await versionsDb.getById(specRun.version_id))?.spec || null : null
|
||||
|
||||
// How the live occurrence stands, in the words its steps' modules chose
|
||||
// (MODULE_API 1.12.0). Only for a live run: a finished one has results, and a
|
||||
// scheduled one has nothing to count yet. `progress.forRun` never rejects and
|
||||
// answers `[]` when nobody has anything to say.
|
||||
const current = live[0] ? publicOccurrence(live[0], spec) : null
|
||||
if (current) current.progress = await progress.forRun(live[0], { viewer: { userId } })
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
@@ -351,7 +359,7 @@ async function event(slug, { runId = null } = {}) {
|
||||
timezone: definition.timezone,
|
||||
series: series ? { name: series.name, slug: series.slug } : null,
|
||||
live: live.length > 0,
|
||||
current: live[0] ? publicOccurrence(live[0], spec) : null,
|
||||
current,
|
||||
next: next ? publicOccurrence(next, spec) : null,
|
||||
upcoming: upcoming.map((r) => publicOccurrence(r, spec)),
|
||||
past: past.map((r) => publicOccurrence(r, spec)),
|
||||
|
||||
Reference in New Issue
Block a user