Give a reader their own events back
An event of theirs that had ended was on no surface at all. Every list and the board drop a row once its end has passed, and settings only counted the events a game held rather than naming them — so a one-off became unreachable the day it finished: impossible to edit, and impossible to delete out of a store nothing else can see. Repeating events escaped only because their occurrences roll forward. So settings names them now, under the game they were filed against, each row opening the same detail sheet a row on the front page does. Nothing about how they are managed changes; what was missing was the way back to them. Two things the index has to get right or it leaves the same hole it closes. The lists hold occurrences, never rules, so a repeating rule's own id opens nothing — nearestOccurrence bridges that, and answers for a finished series too by falling back to the first occurrence, since a rule whose `until` has passed would otherwise be exactly as stuck. And an event filed under a game we track has no row of theirs to nest under; the form allows that, so those get their own heading rather than trailing the list and reading as though they belonged to whichever game came last. Each row says why it is not on the front page — ended and when, or its cadence — because a list of bare titles leaves you guessing which of two entries is the dead one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
8671a309b0
commit
3a3b7d9c2f
8 files changed
+335
-12
No files matched your search
@@ -474,6 +474,38 @@ export function nextOccurrences(
|
||||
return occurrencesOf(event, nowMs, horizon, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* The occurrence to open when the reader asks for the rule itself.
|
||||
*
|
||||
* The settings index lists rules, but the detail sheet opens rows, and a
|
||||
* rule's own id is never a row — the lists hold its occurrences, keyed
|
||||
* `myevent:<token>#<date>`. This is the bridge between the two.
|
||||
*
|
||||
* Running or next where there is one. Where there is not — a series whose
|
||||
* `until` has passed — it falls back to the first occurrence rather than
|
||||
* giving up, and that fallback is the point rather than a nicety: a finished
|
||||
* rule with no future occurrence would otherwise be exactly as unreachable as
|
||||
* the ended one-off this index exists to rescue, and just as impossible to
|
||||
* delete. Which time round it lands on does not matter, because the reader
|
||||
* has come to edit or delete the rule, not to inspect an occurrence.
|
||||
*
|
||||
* Null only when nothing repeats, and the caller has a row already: a
|
||||
* non-repeating event's own id is in the lists unchanged.
|
||||
*/
|
||||
export function nearestOccurrence(
|
||||
event: RepeatingEvent,
|
||||
nowMs: number,
|
||||
): Occurrence | null {
|
||||
if (event.repeat === null) return null;
|
||||
|
||||
const upcoming = nextOccurrences(event, nowMs, 1);
|
||||
if (upcoming.length > 0) return upcoming[0]!;
|
||||
|
||||
const anchor = Date.parse(event.startsAt);
|
||||
if (Number.isNaN(anchor)) return null;
|
||||
return occurrencesOf(event, anchor, anchor, 1)[0] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* How many of a rule's ids the reader has actually recorded something
|
||||
* against — what a schedule edit that re-keys ids would strand.
|
||||
|
||||
Reference in new issue
Block a user