feat(routines): record which games' built-ins are on, and who was asked
Both prefs are absent unless the reader answers, so built-ins are opt-in for every install, old ones included. The question is asked only about a game on screen, and only once. A built-in the reader made their own stays hidden while the copy exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8aa820e2c0
commit
bfd79c295e
3 files changed
+87
-1
No files matched your search
@@ -184,6 +184,14 @@ export interface Prefs {
|
||||
regionConfirmed: boolean;
|
||||
/** False until the reader has picked their games on first run. */
|
||||
onboarded: boolean;
|
||||
/**
|
||||
* Games whose built-in weekly tasks the reader turned on (routines.ts, PRD
|
||||
* F17). Absent means none: built-ins are opt-in, so an install from before
|
||||
* they existed gets none rather than all.
|
||||
*/
|
||||
routineGames?: LaneId[] | undefined;
|
||||
/** Games already asked "track its weekly tasks?", so the question is asked once. */
|
||||
routinesAsked?: LaneId[] | undefined;
|
||||
}
|
||||
|
||||
export function defaults(): Prefs {
|
||||
@@ -313,6 +321,15 @@ export function restorePrefsValue(incoming: unknown): Prefs {
|
||||
}
|
||||
}
|
||||
|
||||
// Built-in tasks: string arrays, or absent.
|
||||
for (const key of ["routineGames", "routinesAsked"] as const) {
|
||||
const value: unknown = result[key];
|
||||
if (value === undefined) continue;
|
||||
result[key] = Array.isArray(value)
|
||||
? value.filter((g): g is LaneId => typeof g === "string")
|
||||
: undefined;
|
||||
}
|
||||
|
||||
// focusGame
|
||||
if (result.focusGame !== null && typeof result.focusGame !== "string") {
|
||||
result.focusGame = null;
|
||||
|
||||
@@ -63,3 +63,35 @@ export const ROUTINES: readonly Routine[] = [
|
||||
export function routinesFor(game: string): Routine[] {
|
||||
return ROUTINES.filter((r) => r.game === game);
|
||||
}
|
||||
|
||||
/**
|
||||
* The built-ins on screen: those of games the reader said yes to, less any
|
||||
* they made their own (`Task.from`) — the copy replaces the original while it
|
||||
* exists, and deleting the copy brings the original back.
|
||||
*/
|
||||
export function activeRoutines(
|
||||
routineGames: readonly string[] | undefined,
|
||||
copiedFrom: ReadonlySet<string>,
|
||||
): Routine[] {
|
||||
const on = new Set(routineGames ?? []);
|
||||
return ROUTINES.filter((r) => on.has(r.game) && !copiedFrom.has(r.id));
|
||||
}
|
||||
|
||||
/**
|
||||
* The one game to ask "track its weekly tasks?" about, or null.
|
||||
*
|
||||
* Only a game on screen — asking about a game the reader switched off or is not
|
||||
* focused on would be asking about something they are not looking at — and
|
||||
* only once: an answer either way is recorded in `asked`.
|
||||
*/
|
||||
export function routineToAsk(
|
||||
visible: readonly string[],
|
||||
routineGames: readonly string[] | undefined,
|
||||
asked: readonly string[] | undefined,
|
||||
): string | null {
|
||||
for (const game of visible) {
|
||||
if (routineGames?.includes(game) || asked?.includes(game)) continue;
|
||||
if (routinesFor(game).length > 0) return game;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Reference in new issue
Block a user