feat(routines): say "weekly and monthly" for built-ins

Built-ins now include monthly items, so the strip's question, Settings and Your
own say "weekly and monthly". The per-game tooltip quotes the oldest source
date when a game has several.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-10-16 03:00:21 +02:00
1 parent 3fd2b3c2ed
commit f322061c88
7 files changed
+22 -19

No files matched your search

+7 -4
View File
@@ -281,14 +281,17 @@ export function Controls({
<PerGameSwitch
checked={routines.all}
onChange={(showRoutines) => onUpdate({ showRoutines })}
label="Show built-in weekly tasks"
hint="Weekly tasks the app knows for some games, each from a cited page and dated by it. Off hides them; your ticks and numbers are kept."
label="Show built-in weekly and monthly tasks"
hint="Weekly and monthly tasks the app knows for some games, each from a cited page and dated by it. Off hides them; your ticks and numbers are kept."
rule={routines}
games={routineGames}
chipsLabel="Built-in weekly tasks per game"
chipsLabel="Built-in weekly and monthly tasks per game"
titleFor={(game) => {
const list = routinesFor(game);
return `${list.map((r) => r.title).join(", ")} — from a source as of ${asOfLabel(list[0]!.asOf)}`;
// Several pages per game now: quote the oldest, so the
// tooltip never makes a year-old figure look current.
const oldest = list.map((r) => r.asOf).sort()[0]!;
return `${list.map((r) => r.title).join(", ")} — ${list.length > 1 ? "from sources, the oldest" : "from a source"} as of ${asOfLabel(oldest)}`;
}}
onSet={(game, value) => {
const { on, off } = setFor(routines, game, value);
+4 -4
View File
@@ -334,7 +334,7 @@ export function Dailies({
tasks?: readonly StripTask[] | undefined;
taskStatusFor?: ((task: StripTask) => GoalStatus | undefined) | undefined;
/**
* A game whose built-in weekly tasks the reader has not been asked about yet,
* A game whose built-in weekly and monthly tasks the reader has not been asked about yet,
* and how to record the answer. Asked here because this is where the tasks
* would appear; one line, never a modal, never twice.
*/
@@ -427,13 +427,13 @@ export function Dailies({
ask === undefined ? null : (
<div className="relative flex flex-wrap items-baseline gap-x-3 gap-y-1 text-xs">
<p className="text-muted">
Track {gameMeta(ask.game).name}'s built-in weekly tasks?
Track {gameMeta(ask.game).name}'s built-in weekly and monthly tasks?
</p>
<div className="flex gap-3">
<button
type="button"
onClick={() => ask.onAnswer(true)}
aria-label={`Yes, track ${gameMeta(ask.game).name}'s built-in weekly tasks`}
aria-label={`Yes, track ${gameMeta(ask.game).name}'s built-in weekly and monthly tasks`}
className="font-medium text-near transition-colors hover:text-ink"
>
Yes
@@ -441,7 +441,7 @@ export function Dailies({
<button
type="button"
onClick={() => ask.onAnswer(false)}
aria-label={`No thanks to ${gameMeta(ask.game).name}'s built-in weekly tasks`}
aria-label={`No thanks to ${gameMeta(ask.game).name}'s built-in weekly and monthly tasks`}
className="text-faint transition-colors hover:text-muted"
>
No thanks
+1 -1
View File
@@ -332,7 +332,7 @@ export function YourOwn({
a copy that replaces it while it exists, numbers and ticks included. */}
{routines.length > 0 && onMakeItMine !== undefined && (
<>
<p className="mt-4 text-xs text-faint">Built-in weekly tasks</p>
<p className="mt-4 text-xs text-faint">Built-in weekly and monthly tasks</p>
<ul className="mt-1.5 flex flex-col gap-1">
{routines.map((routine) => {
const tracked = !(routinesHidden?.has(routine.id) ?? false);