fix(custom): list your own games first when adding an event
Someone filling this form in by hand is usually doing it because the game isn't tracked, so making them scroll past nine that are gets the common case backwards. The default selection follows the top of the list rather than staying on whatever the feed happened to return first. The sort groups and does not reshuffle: tracked games keep their feed order behind the reader's own. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
adaa9eaada
commit
0a25cce9ce
@@ -86,7 +86,7 @@ src/client/ React app, service worker, manifest
|
|||||||
lens.ts — who sees which rows (focus, outstanding, next-to-expire); pure
|
lens.ts — who sees which rows (focus, outstanding, next-to-expire); pure
|
||||||
scripts/ build-feed.ts, parse-fixture.ts (offline), refresh-sources.ts (fetches)
|
scripts/ build-feed.ts, parse-fixture.ts (offline), refresh-sources.ts (fetches)
|
||||||
serve.ts static server + /api/health
|
serve.ts static server + /api/health
|
||||||
test/ 400 tests
|
test/ 401 tests
|
||||||
fixtures/<game>/ raw HTML + .expected.json per source — pinned, kept forever
|
fixtures/<game>/ raw HTML + .expected.json per source — pinned, kept forever
|
||||||
snapshots/ current page per source, rewritten by refresh — see its README
|
snapshots/ current page per source, rewritten by refresh — see its README
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -141,8 +141,16 @@ export function EventForm({
|
|||||||
const start = fields(initial?.startsAt ?? null);
|
const start = fields(initial?.startsAt ?? null);
|
||||||
const end = fields(initial?.endsAt ?? null);
|
const end = fields(initial?.endsAt ?? null);
|
||||||
|
|
||||||
|
// The reader's own games first, and so the default too. Someone filling this
|
||||||
|
// in by hand is usually doing it *because* the game isn't tracked; making
|
||||||
|
// them scroll past nine that are gets the common case backwards. Stable
|
||||||
|
// within each group, so the tracked ones keep their feed order.
|
||||||
|
const ordered = [...lanes].sort(
|
||||||
|
(a, b) => Number(isCustomGameId(b)) - Number(isCustomGameId(a)),
|
||||||
|
);
|
||||||
|
|
||||||
const [game, setGame] = useState<LaneId>(
|
const [game, setGame] = useState<LaneId>(
|
||||||
initial?.game ?? lanes[0] ?? Object.keys(customGames)[0] ?? "",
|
initial?.game ?? ordered[0] ?? Object.keys(customGames)[0] ?? "",
|
||||||
);
|
);
|
||||||
const [title, setTitle] = useState(initial?.title ?? "");
|
const [title, setTitle] = useState(initial?.title ?? "");
|
||||||
const [type, setType] = useState<EventType>(initial?.type ?? "other");
|
const [type, setType] = useState<EventType>(initial?.type ?? "other");
|
||||||
@@ -198,7 +206,7 @@ export function EventForm({
|
|||||||
onChange={(e) => setGame(e.target.value)}
|
onChange={(e) => setGame(e.target.value)}
|
||||||
className={inputClass()}
|
className={inputClass()}
|
||||||
>
|
>
|
||||||
{lanes.map((id) => (
|
{ordered.map((id) => (
|
||||||
<option key={id} value={id}>
|
<option key={id} value={id}>
|
||||||
{gameMeta(id).name}
|
{gameMeta(id).name}
|
||||||
{isCustomGameId(id) ? " (yours)" : ""}
|
{isCustomGameId(id) ? " (yours)" : ""}
|
||||||
|
|||||||
@@ -96,6 +96,44 @@ describe("EventForm", () => {
|
|||||||
expect(html).toContain("I don't know when it ends");
|
expect(html).toContain("I don't know when it ends");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("puts the reader's own games at the top of the picker, and defaults to one", () => {
|
||||||
|
// Someone entering an event by hand is usually doing it because the game
|
||||||
|
// isn't tracked. Two of theirs to prove the group order, not just a swap.
|
||||||
|
const twoOfMine: CustomGames = {
|
||||||
|
...GAMES,
|
||||||
|
"mygame:silver-palace": {
|
||||||
|
id: "mygame:silver-palace",
|
||||||
|
name: "Silver Palace",
|
||||||
|
hue: "#5C7CE0",
|
||||||
|
at: AT,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const html = renderToStaticMarkup(
|
||||||
|
<GameMetaProvider value={(id) => metaFor(id, twoOfMine)}>
|
||||||
|
<EventForm
|
||||||
|
lanes={["genshin", "hsr", "mygame:limbus-company", "mygame:silver-palace"]}
|
||||||
|
customGames={twoOfMine}
|
||||||
|
onSave={() => {}}
|
||||||
|
onCancel={() => {}}
|
||||||
|
/>
|
||||||
|
</GameMetaProvider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
// The game picker is the first select; the second is the event kind.
|
||||||
|
const picker = html.slice(0, html.indexOf("</select>"));
|
||||||
|
const order = [...picker.matchAll(/<option value="([^"]+)"/g)].map((m) => m[1]);
|
||||||
|
expect(order).toEqual([
|
||||||
|
"mygame:limbus-company",
|
||||||
|
"mygame:silver-palace",
|
||||||
|
// Tracked games keep their feed order behind them — the sort groups, it
|
||||||
|
// does not reshuffle.
|
||||||
|
"genshin",
|
||||||
|
"hsr",
|
||||||
|
]);
|
||||||
|
// The default follows the top of the list rather than staying on Genshin.
|
||||||
|
expect(html).toContain('value="mygame:limbus-company" selected');
|
||||||
|
});
|
||||||
|
|
||||||
test("lets an event be filed under a tracked game too", () => {
|
test("lets an event be filed under a tracked game too", () => {
|
||||||
// A source can miss an event in a game we do cover.
|
// A source can miss an event in a game we do cover.
|
||||||
const html = render(
|
const html = render(
|
||||||
|
|||||||
Reference in New Issue
Block a user