feat(settings): switch built-in weekly tasks per game, and make one your own
The switch sits beside the daily chores and answers the strip's question too. Built-ins are listed with a link to their dated source. Make it mine copies one into the reader's tasks, numbers and ticks included, and hides the original while the copy exists. It's additive, so deleting the copy brings the original back as it was. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
228ce9132d
commit
624c725cbf
8 files changed
+161
-3
No files matched your search
@@ -7,6 +7,7 @@ import { moveGame } from "../state/gameOrder.ts";
|
||||
import type { ThemeChoice } from "../state/theme.ts";
|
||||
import type { Prefs } from "../state/usePrefs.ts";
|
||||
import { YourOwn } from "./YourOwn.tsx";
|
||||
import { asOfLabel, routinesFor } from "../../shared/routines.ts";
|
||||
|
||||
const REGIONS: Array<{ id: Region; label: string }> = (
|
||||
["america", "europe", "asia"] as const
|
||||
@@ -91,6 +92,7 @@ export function Controls({
|
||||
/** Everything the reader entered themselves, and the ways to change it. */
|
||||
own: React.ComponentProps<typeof YourOwn>;
|
||||
}) {
|
||||
const gameMeta = useGameMeta();
|
||||
const shown = games.filter((g) => !prefs.hiddenGames.includes(g)).length;
|
||||
const ownGames = Object.keys(own.games).length;
|
||||
const ownEvents = Object.keys(own.events).length;
|
||||
@@ -246,6 +248,36 @@ export function Controls({
|
||||
hint="Commissions, resin, daily training and the rest. Nobody publishes these, so the app keeps a fixed list per game. Off, the strip shows only events with a checklist. Your ticks and streaks are kept either way."
|
||||
/>
|
||||
|
||||
{/* Beside the chores rather than on each game's row, which already
|
||||
carries a handle, two arrows and a switch. Opt-in per game: these
|
||||
are someone else's figures, each dated by its source. */}
|
||||
{games
|
||||
.filter((game) => routinesFor(game).length > 0)
|
||||
.map((game) => {
|
||||
const on = prefs.routineGames?.includes(game) ?? false;
|
||||
const list = routinesFor(game);
|
||||
return (
|
||||
<Check
|
||||
key={game}
|
||||
checked={on}
|
||||
onChange={(checked) =>
|
||||
onUpdate({
|
||||
routineGames: checked
|
||||
? [...(prefs.routineGames ?? []), game]
|
||||
: (prefs.routineGames ?? []).filter((g) => g !== game),
|
||||
// Answering here answers the strip's question too.
|
||||
routinesAsked: [
|
||||
...(prefs.routinesAsked ?? []).filter((g) => g !== game),
|
||||
game,
|
||||
],
|
||||
})
|
||||
}
|
||||
label={`Built-in weekly tasks for ${gameMeta(game).name}`}
|
||||
hint={`${list.map((r) => r.title).join(", ")} — from a source as of ${asOfLabel(list[0]!.asOf)}. Off hides them; your numbers are kept.`}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
{ignoredCount > 0 && (
|
||||
<Check
|
||||
checked={prefs.showIgnored}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { cadenceLabel, EventForm, GameForm } from "./CustomForms.tsx";
|
||||
import { taskCaption, type Task } from "../../shared/task.ts";
|
||||
import type { TaskDraft } from "../state/useTasks.ts";
|
||||
import { TaskForm } from "./TaskForm.tsx";
|
||||
import { asOfLabel, type Routine } from "../../shared/routines.ts";
|
||||
|
||||
/**
|
||||
* The reader's own games and events — a group of the settings panel (PRD F13).
|
||||
@@ -68,6 +69,8 @@ export function YourOwn({
|
||||
onAddTask,
|
||||
onEditTask,
|
||||
onRemoveTask,
|
||||
routines = [],
|
||||
onMakeItMine,
|
||||
}: {
|
||||
games: CustomGames;
|
||||
events: CustomEvents;
|
||||
@@ -89,6 +92,9 @@ export function YourOwn({
|
||||
onAddTask?: ((draft: TaskDraft) => void) | undefined;
|
||||
onEditTask?: ((id: string, draft: TaskDraft) => void) | undefined;
|
||||
onRemoveTask?: ((id: string) => void) | undefined;
|
||||
/** Built-in weekly tasks the reader has on, and how to copy one into their own. */
|
||||
routines?: readonly Routine[] | undefined;
|
||||
onMakeItMine?: ((routine: Routine) => void) | undefined;
|
||||
}) {
|
||||
const gameMeta = useGameMeta();
|
||||
const [adding, setAdding] = useState<"game" | "event" | "task" | null>(null);
|
||||
@@ -313,6 +319,44 @@ export function YourOwn({
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Built-ins are not editable in place — they are someone else's figures,
|
||||
dated by their source — so the way to change one is to make it yours:
|
||||
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>
|
||||
<ul className="mt-1.5 flex flex-col gap-1">
|
||||
{routines.map((routine) => (
|
||||
<li key={routine.id}>
|
||||
<div className="flex items-baseline gap-2">
|
||||
<span className="shrink-0 text-xs text-faint">{gameMeta(routine.game).short}</span>
|
||||
<span className="min-w-0 flex-1 truncate text-sm text-muted">{routine.title}</span>
|
||||
<a
|
||||
href={routine.source}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
className="shrink-0 text-xs text-faint underline decoration-hairline underline-offset-2 transition-colors hover:text-muted"
|
||||
>
|
||||
as of {asOfLabel(routine.asOf)}
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onMakeItMine(routine)}
|
||||
className="shrink-0 text-xs text-faint transition-colors hover:text-ink"
|
||||
>
|
||||
Make it mine
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-xs text-faint">
|
||||
{taskCaption(routine)}
|
||||
{routine.note !== undefined && ` · ${routine.note}`}
|
||||
</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
|
||||
{adding === "task" && onAddTask !== undefined && (
|
||||
<TaskForm
|
||||
lanes={lanes}
|
||||
|
||||
Reference in new issue
Block a user