Add a National reset; replace native confirm()/alert() with the app dialog

- selection: clearAllGames() wipes seen/caught for every game but keeps
  the global favorites/notes record.
- Progress view: a "Reset caught / seen…" action (custom dialog) that
  calls clearAllGames — the soft, favorites-preserving reset. Settings →
  "Clear tracking data" stays as the full wipe (now spells out that it
  also drops favorites and notes, and points here for the softer one).
- Swept the remaining window.confirm / window.alert calls
  (Settings clear-tracking, clear-cache, backup import result;
  ShinyView delete-hunt) onto chooseDialog for a consistent look.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Ve7HLspzeG2xDPtJQ8vmu
This commit is contained in:
chris 2026-09-10 11:58:36 -04:00
parent edef6bf879
commit 3f887b2f8a
6 changed files with 111 additions and 22 deletions

View File

@ -126,7 +126,9 @@ Every game's seen/caught tally against its own dex, from its own per-game
bucket. Leads with the games you've played (selected at least once, or
imported a save for); the rest sit behind a toggle. The National Dex row is
the union across all games. **Edit** the "Your games" list to reset a
game's caught/seen or drop it from the list.
game's caught/seen or drop it from the list; **Reset caught / seen…** wipes
every game at once (favorites and notes are kept — only Settings →
_Clear tracking data_ removes those).
### Shiny hunt tracker (`#/shiny`)

View File

@ -149,6 +149,15 @@ export function clearGame(vgKey) {
});
}
/**
* Wipe seen/caught for every game (a "National Dex reset"). Favorites and
* notes the global `pokemon` record are kept. Use `selection.replace`
* for the full wipe that also drops those.
*/
export function clearAllGames() {
selection.set((s) => ({ ...s, games: {} }));
}
/** Aggregate seen/caught over a species list for one game (union when 'all'). */
export function stats(speciesIds, vgKey = curGame()) {
const get = vgKey === 'all' ? unionEntry : (id) => selection.get().games[vgKey]?.[id] || GBLANK;

View File

@ -3382,6 +3382,13 @@
color: var(--text-dim);
text-align: right;
}
.prog__bar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
flex-wrap: wrap;
}
.prog__legend {
display: flex;
align-items: center;
@ -3389,6 +3396,19 @@
font-size: 0.75rem;
color: var(--text-dim);
}
.prog__resetall {
border: none;
background: none;
font: inherit;
font-size: 0.78rem;
font-weight: 600;
color: var(--danger);
cursor: pointer;
padding: 4px 0;
}
.prog__resetall:hover {
text-decoration: underline;
}
.prog__key {
display: inline-block;
width: 1.1rem;

View File

@ -1,7 +1,7 @@
import { el, clear, onTeardown } from '../lib/dom.js';
import { loadSnapshot } from '../data/snapshot.js';
import { settings } from '../store/settings.js';
import { selection, stats, statsNational, clearGame } from '../store/selection.js';
import { selection, stats, statsNational, clearGame, clearAllGames } from '../store/selection.js';
import { ui } from '../store/ui.js';
import { playedGames, isPlayed, unmarkPlayed } from '../store/playedGames.js';
import { dexesForVersionGroup, versionGroupsByGeneration } from '../data/pokedex-resolver.js';
@ -172,6 +172,23 @@ export async function ProgressView() {
}
render();
async function resetEverything() {
const nat = statsNational(snap.species.map((s) => s.id));
if (!nat.seen && !nat.caught) return;
const pick = await chooseDialog({
title: 'Reset caught / seen',
body: `Clear all ${nat.caught} caught and ${nat.seen} seen, across every game? Favorites and notes are kept.`,
choices: [
{ key: 'reset', label: 'Reset everything', class: 'button--danger' },
{ key: null, label: 'Cancel', class: 'button--ghost' },
],
});
if (pick === 'reset') {
clearAllGames();
render();
}
}
clear(view).append(
el(
'nav',
@ -185,12 +202,21 @@ export async function ProgressView() {
el('p', {}, 'What youve caught in each game. Tap one to switch to it.'),
),
el(
'p',
{ class: 'prog__legend' },
el('span', { class: 'prog__key prog__key--caught' }),
' caught ',
el('span', { class: 'prog__key prog__key--seen' }),
' seen',
'div',
{ class: 'prog__bar' },
el(
'p',
{ class: 'prog__legend' },
el('span', { class: 'prog__key prog__key--caught' }),
' caught ',
el('span', { class: 'prog__key prog__key--seen' }),
' seen',
),
el(
'button',
{ type: 'button', class: 'prog__resetall', onclick: resetEverything },
'Reset caught / seen…',
),
),
list,
);

View File

@ -223,9 +223,17 @@ export async function SettingsView() {
if (data.formTracking) formTracking.replace(data.formTracking);
if (data.shinyHunts) shinyHunts.replace(data.shinyHunts);
if (data.playedGames) playedGames.replace(data.playedGames);
alert('Import complete.');
await chooseDialog({
title: 'Import complete',
body: 'Your backup was restored.',
choices: [{ key: null, label: 'OK' }],
});
} catch {
alert('That file could not be read as a Pokédex backup.');
await chooseDialog({
title: 'Couldnt read that file',
body: 'It doesnt look like a Pocketdex backup.',
choices: [{ key: null, label: 'OK' }],
});
}
},
});
@ -322,10 +330,16 @@ export async function SettingsView() {
{
class: 'button button--danger',
type: 'button',
onclick: () => {
if (confirm('Clear all seen/caught/favorite tracking? This cannot be undone.')) {
selection.replace({ version: 3, pokemon: {}, games: {} });
}
onclick: async () => {
const pick = await chooseDialog({
title: 'Clear all tracking data',
body: 'Removes seen/caught for every game plus all favorites and notes, and cant be undone. To keep favorites and notes, use “Reset caught / seen” on the Progress screen instead.',
choices: [
{ key: 'clear', label: 'Clear everything', class: 'button--danger' },
{ key: null, label: 'Cancel', class: 'button--ghost' },
],
});
if (pick === 'clear') selection.replace({ version: 3, pokemon: {}, games: {} });
},
},
'Clear tracking data',
@ -337,12 +351,21 @@ export async function SettingsView() {
type: 'button',
onclick: async () => {
if (!('caches' in window)) return;
if (
confirm('Clear cached PokéAPI data and sprites? They will re-download when online.')
) {
for (const key of await caches.keys()) await caches.delete(key);
alert('Caches cleared.');
}
const pick = await chooseDialog({
title: 'Clear cached data',
body: 'Drops cached PokéAPI responses and sprites. They re-download next time youre online.',
choices: [
{ key: 'clear', label: 'Clear cache', class: 'button--danger' },
{ key: null, label: 'Cancel', class: 'button--ghost' },
],
});
if (pick !== 'clear') return;
for (const key of await caches.keys()) await caches.delete(key);
await chooseDialog({
title: 'Cache cleared',
body: '',
choices: [{ key: null, label: 'OK' }],
});
},
},
'Clear cached API data',

View File

@ -6,6 +6,7 @@ import { toggle, entry } from '../store/selection.js';
import { buzz } from '../lib/haptics.js';
import { Sprite } from '../components/Sprite.js';
import { openPokemonPicker } from '../components/PokemonPicker.js';
import { chooseDialog } from '../lib/dialog.js';
// Representative full-game odds (as 1/N), with a shiny-charm variant.
export const METHODS = [
@ -131,8 +132,16 @@ export async function ShinyView() {
{
type: 'button',
class: 'button button--danger',
onclick: () => {
if (confirm('Delete this hunt?')) {
onclick: async () => {
const pick = await chooseDialog({
title: 'Delete this hunt?',
body: `${(sp?.name || 'This hunt').replace(/-/g, ' ')}${h.count} checks logged.`,
choices: [
{ key: 'del', label: 'Delete', class: 'button--danger' },
{ key: null, label: 'Cancel', class: 'button--ghost' },
],
});
if (pick === 'del') {
removeHunt(h.id);
render();
}