feat: let readers ignore events they don't care about

Ignoring is not completing. "Done" keeps an event visible and counted;
"not interested" removes it from both views, which is the entire point.

Completions and ignores are the same shape and want the same guarantees, so
they now share one mark-set implementation — union merge on import, never a
removal, since nothing else holds a copy. Both ride in the export file.

Ignored events stay recoverable: the count and a reveal toggle appear in
settings once there is something to reveal.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Lucas Winther
2026-08-15 01:18:11 +02:00
co-authored by Claude Opus 5
parent dd596b5a43
commit 71bff72ee9
6 changed files with 84 additions and 75 deletions
+26 -9
View File
@@ -13,6 +13,7 @@ export function Controls({
prefs,
onToggleGame,
onUpdate,
ignoredCount,
onExport,
onImport,
}: {
@@ -20,6 +21,7 @@ export function Controls({
prefs: Prefs;
onToggleGame: (g: GameId) => void;
onUpdate: (p: Partial<Prefs>) => void;
ignoredCount: number;
onExport: () => void;
onImport: (file: File) => void;
}) {
@@ -73,15 +75,30 @@ export function Controls({
</div>
</div>
<label className="flex cursor-pointer select-none items-center gap-2 text-xs text-muted">
<input
type="checkbox"
checked={prefs.showCompleted}
onChange={(e) => onUpdate({ showCompleted: e.target.checked })}
className="size-4 accent-[var(--color-near)]"
/>
Show events I've finished
</label>
<div className="flex flex-col gap-2">
<label className="flex cursor-pointer select-none items-center gap-2 text-xs text-muted">
<input
type="checkbox"
checked={prefs.showCompleted}
onChange={(e) => onUpdate({ showCompleted: e.target.checked })}
className="size-4 accent-[var(--color-near)]"
/>
Show events I've finished
</label>
{ignoredCount > 0 && (
<label className="flex cursor-pointer select-none items-center gap-2 text-xs text-muted">
<input
type="checkbox"
checked={prefs.showIgnored}
onChange={(e) => onUpdate({ showIgnored: e.target.checked })}
className="size-4 accent-[var(--color-near)]"
/>
Show the {ignoredCount} event{ignoredCount > 1 ? "s" : ""} I'm
ignoring
</label>
)}
</div>
</div>
<div className="mt-6 border-t border-hairline pt-4">
+19
View File
@@ -7,12 +7,16 @@ import { Meter, URGENCY_COLOR } from "./Meter.tsx";
export function EventDetail({
row,
completed,
ignored,
onToggle,
onIgnore,
onClose,
}: {
row: RowEvent;
completed: boolean;
ignored: boolean;
onToggle: (id: string) => void;
onIgnore: (id: string) => void;
onClose: () => void;
}) {
const { event, clock } = row;
@@ -109,6 +113,21 @@ export function EventDetail({
Source
</a>
</div>
{/* Ignoring is not completing. "Done" keeps an event visible and
counted; "not interested" removes it from both views entirely. */}
<button
type="button"
onClick={() => {
onIgnore(event.id);
if (!ignored) onClose();
}}
className="mt-3 w-full rounded-lg px-4 py-2 text-xs text-faint transition-colors hover:text-muted"
>
{ignored
? "Stop ignoring this event"
: "Not interested — hide this event"}
</button>
</div>
</div>
);