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:
parent
edef6bf879
commit
3f887b2f8a
@ -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
|
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
|
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
|
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`)
|
### Shiny hunt tracker (`#/shiny`)
|
||||||
|
|
||||||
|
|||||||
@ -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'). */
|
/** Aggregate seen/caught over a species list for one game (union when 'all'). */
|
||||||
export function stats(speciesIds, vgKey = curGame()) {
|
export function stats(speciesIds, vgKey = curGame()) {
|
||||||
const get = vgKey === 'all' ? unionEntry : (id) => selection.get().games[vgKey]?.[id] || GBLANK;
|
const get = vgKey === 'all' ? unionEntry : (id) => selection.get().games[vgKey]?.[id] || GBLANK;
|
||||||
|
|||||||
@ -3382,6 +3382,13 @@
|
|||||||
color: var(--text-dim);
|
color: var(--text-dim);
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
.prog__bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
.prog__legend {
|
.prog__legend {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -3389,6 +3396,19 @@
|
|||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
color: var(--text-dim);
|
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 {
|
.prog__key {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 1.1rem;
|
width: 1.1rem;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { el, clear, onTeardown } from '../lib/dom.js';
|
import { el, clear, onTeardown } from '../lib/dom.js';
|
||||||
import { loadSnapshot } from '../data/snapshot.js';
|
import { loadSnapshot } from '../data/snapshot.js';
|
||||||
import { settings } from '../store/settings.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 { ui } from '../store/ui.js';
|
||||||
import { playedGames, isPlayed, unmarkPlayed } from '../store/playedGames.js';
|
import { playedGames, isPlayed, unmarkPlayed } from '../store/playedGames.js';
|
||||||
import { dexesForVersionGroup, versionGroupsByGeneration } from '../data/pokedex-resolver.js';
|
import { dexesForVersionGroup, versionGroupsByGeneration } from '../data/pokedex-resolver.js';
|
||||||
@ -172,6 +172,23 @@ export async function ProgressView() {
|
|||||||
}
|
}
|
||||||
render();
|
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(
|
clear(view).append(
|
||||||
el(
|
el(
|
||||||
'nav',
|
'nav',
|
||||||
@ -185,12 +202,21 @@ export async function ProgressView() {
|
|||||||
el('p', {}, 'What you’ve caught in each game. Tap one to switch to it.'),
|
el('p', {}, 'What you’ve caught in each game. Tap one to switch to it.'),
|
||||||
),
|
),
|
||||||
el(
|
el(
|
||||||
'p',
|
'div',
|
||||||
{ class: 'prog__legend' },
|
{ class: 'prog__bar' },
|
||||||
el('span', { class: 'prog__key prog__key--caught' }),
|
el(
|
||||||
' caught ',
|
'p',
|
||||||
el('span', { class: 'prog__key prog__key--seen' }),
|
{ class: 'prog__legend' },
|
||||||
' seen',
|
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,
|
list,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -223,9 +223,17 @@ export async function SettingsView() {
|
|||||||
if (data.formTracking) formTracking.replace(data.formTracking);
|
if (data.formTracking) formTracking.replace(data.formTracking);
|
||||||
if (data.shinyHunts) shinyHunts.replace(data.shinyHunts);
|
if (data.shinyHunts) shinyHunts.replace(data.shinyHunts);
|
||||||
if (data.playedGames) playedGames.replace(data.playedGames);
|
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 {
|
} catch {
|
||||||
alert('That file could not be read as a Pokédex backup.');
|
await chooseDialog({
|
||||||
|
title: 'Couldn’t read that file',
|
||||||
|
body: 'It doesn’t look like a Pocketdex backup.',
|
||||||
|
choices: [{ key: null, label: 'OK' }],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -322,10 +330,16 @@ export async function SettingsView() {
|
|||||||
{
|
{
|
||||||
class: 'button button--danger',
|
class: 'button button--danger',
|
||||||
type: 'button',
|
type: 'button',
|
||||||
onclick: () => {
|
onclick: async () => {
|
||||||
if (confirm('Clear all seen/caught/favorite tracking? This cannot be undone.')) {
|
const pick = await chooseDialog({
|
||||||
selection.replace({ version: 3, pokemon: {}, games: {} });
|
title: 'Clear all tracking data',
|
||||||
}
|
body: 'Removes seen/caught for every game plus all favorites and notes, and can’t 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',
|
'Clear tracking data',
|
||||||
@ -337,12 +351,21 @@ export async function SettingsView() {
|
|||||||
type: 'button',
|
type: 'button',
|
||||||
onclick: async () => {
|
onclick: async () => {
|
||||||
if (!('caches' in window)) return;
|
if (!('caches' in window)) return;
|
||||||
if (
|
const pick = await chooseDialog({
|
||||||
confirm('Clear cached PokéAPI data and sprites? They will re-download when online.')
|
title: 'Clear cached data',
|
||||||
) {
|
body: 'Drops cached PokéAPI responses and sprites. They re-download next time you’re online.',
|
||||||
for (const key of await caches.keys()) await caches.delete(key);
|
choices: [
|
||||||
alert('Caches cleared.');
|
{ 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',
|
'Clear cached API data',
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { toggle, entry } from '../store/selection.js';
|
|||||||
import { buzz } from '../lib/haptics.js';
|
import { buzz } from '../lib/haptics.js';
|
||||||
import { Sprite } from '../components/Sprite.js';
|
import { Sprite } from '../components/Sprite.js';
|
||||||
import { openPokemonPicker } from '../components/PokemonPicker.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.
|
// Representative full-game odds (as 1/N), with a shiny-charm variant.
|
||||||
export const METHODS = [
|
export const METHODS = [
|
||||||
@ -131,8 +132,16 @@ export async function ShinyView() {
|
|||||||
{
|
{
|
||||||
type: 'button',
|
type: 'button',
|
||||||
class: 'button button--danger',
|
class: 'button button--danger',
|
||||||
onclick: () => {
|
onclick: async () => {
|
||||||
if (confirm('Delete this hunt?')) {
|
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);
|
removeHunt(h.id);
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user