feat(routines): let a reader stop tracking one built-in
A new pref, routinesHidden, lists built-ins stopped one at a time by id. It narrows the game's answer and never widens it, so stopping one changes nothing else, and a built-in shipped later still arrives. Tracking one of a game that is off turns the game on with that item alone (trackRoutinePatch). Absent means none; ticks and numbers stay under the id. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2c6c6859f0
commit
612e0a23e4
3 files changed
+145
-19
No files matched your search
@@ -8,7 +8,8 @@ import type { TimelineGroup } from "./lanes.ts";
|
||||
import { DEFAULT_THEME_CHOICE, type ThemeChoice } from "./theme.ts";
|
||||
import { DEFAULT_DAY_WIDTH, snapDayWidth } from "./zoom.ts";
|
||||
import { ENTRIES, newestId } from "../../shared/changelog.ts";
|
||||
import type { PerGame } from "../../shared/perGame.ts";
|
||||
import { setFor, shownFor, type PerGame } from "../../shared/perGame.ts";
|
||||
import { hiddenAfter, routinesFor, type Routine } from "../../shared/routines.ts";
|
||||
|
||||
/**
|
||||
* Which of the two views the reader is looking at.
|
||||
@@ -222,6 +223,13 @@ export interface Prefs {
|
||||
routinesOff?: LaneId[] | undefined;
|
||||
/** Games already asked "track its weekly tasks?", so the question is asked once. */
|
||||
routinesAsked?: LaneId[] | undefined;
|
||||
/**
|
||||
* Built-ins the reader stopped tracking one at a time, by `routine:` id —
|
||||
* Endfield's weekly items but not its Stock Redistribution. Narrows the game
|
||||
* rule above and never widens it; absent means none, and their ticks and
|
||||
* numbers stay stored under the id.
|
||||
*/
|
||||
routinesHidden?: string[] | undefined;
|
||||
/**
|
||||
* The newest changelog entry the reader dismissed (PRD F18). Absent means
|
||||
* unrecorded, not "seen nothing": first-run setup sets it, so an absent value
|
||||
@@ -262,6 +270,32 @@ export function routineRule(p: Prefs): PerGame {
|
||||
return { all: p.showRoutines ?? false, on: p.routineGames, off: p.routinesOff };
|
||||
}
|
||||
|
||||
/**
|
||||
* What tracking one built-in, or stopping, writes (the strip's Edit panel).
|
||||
*
|
||||
* Stopping hides that one and nothing else. Tracking one whose game is on
|
||||
* unhides it. Tracking one whose game is off turns the game on — the same
|
||||
* answer the strip's question records — with its siblings hidden, because the
|
||||
* reader asked for one item and not the game's whole list.
|
||||
*/
|
||||
export function trackRoutinePatch(p: Prefs, routine: Routine, track: boolean): Partial<Prefs> {
|
||||
const rule = routineRule(p);
|
||||
if (!track || shownFor(rule, routine.game)) {
|
||||
return { routinesHidden: hiddenAfter(p.routinesHidden, routine.id, track) };
|
||||
}
|
||||
const { on, off } = setFor(rule, routine.game, true);
|
||||
const siblings = routinesFor(routine.game)
|
||||
.map((r) => r.id)
|
||||
.filter((id) => id !== routine.id);
|
||||
const hidden = [...(p.routinesHidden ?? []).filter((id) => id !== routine.id && !siblings.includes(id)), ...siblings];
|
||||
return {
|
||||
routineGames: on,
|
||||
routinesOff: off,
|
||||
routinesAsked: [...(p.routinesAsked ?? []).filter((g) => g !== routine.game), routine.game],
|
||||
routinesHidden: hidden.length > 0 ? hidden : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export function defaults(): Prefs {
|
||||
return {
|
||||
region: guessRegion(),
|
||||
@@ -396,7 +430,7 @@ export function restorePrefsValue(incoming: unknown): Prefs {
|
||||
}
|
||||
|
||||
// Per-game exceptions and built-in tasks: string arrays, or absent.
|
||||
for (const key of ["routineGames", "routinesAsked", "routinesOff", "choresOn", "choresOff"] as const) {
|
||||
for (const key of ["routineGames", "routinesAsked", "routinesOff", "routinesHidden", "choresOn", "choresOff"] as const) {
|
||||
const value: unknown = result[key];
|
||||
if (value === undefined) continue;
|
||||
result[key] = Array.isArray(value)
|
||||
|
||||
Reference in new issue
Block a user