From 404b31bd13fd0f4e12e4f473c124f9d1e0bdad39 Mon Sep 17 00:00:00 2001 From: Hikari Date: Mon, 9 Mar 2026 22:17:12 -0700 Subject: [PATCH] fix: persist UI preferences across navigation and sessions (#48) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - **#35** — Adventure multiplier selection is now persisted in `localStorage` (`"elysium_batch_size"`). The chosen batch size is restored automatically on the next visit, with a graceful fallback to `1` for missing or unrecognisable values. - **#36** — Zone selection in the boss panel and quest panel is now persisted in `sessionStorage` (`"elysium_boss_zone"` / `"elysium_quest_zone"`). The selected zone survives navigation within a session and resets cleanly when the session ends, defaulting to Verdant Vale if no stored value exists. ## Test plan - [x] Lint — zero errors, zero warnings - [x] Build — all packages build cleanly - [x] Tests — 415 tests passing, 100% coverage across all packages - [ ] Manual: select a non-default batch size, refresh the page — multiplier should be restored - [ ] Manual: switch to a non-default zone in the boss panel, navigate away and back — zone should still be selected - [ ] Manual: repeat for the quest panel - [ ] Manual: log out and back in — zone selection should reset to Verdant Vale ✨ This PR was created with help from Hikari~ 🌸 Reviewed-on: https://git.nhcarrigan.com/nhcarrigan/elysium/pulls/48 Co-authored-by: Hikari Co-committed-by: Hikari --- .../src/components/game/adventurerPanel.tsx | 30 ++++++++++++++++++- apps/web/src/components/game/bossPanel.tsx | 11 +++++-- .../web/src/components/game/characterPage.tsx | 2 +- .../src/components/game/explorationPanel.tsx | 5 +++- apps/web/src/components/game/questPanel.tsx | 15 +++++++--- 5 files changed, 54 insertions(+), 9 deletions(-) diff --git a/apps/web/src/components/game/adventurerPanel.tsx b/apps/web/src/components/game/adventurerPanel.tsx index efa7790..c996374 100644 --- a/apps/web/src/components/game/adventurerPanel.tsx +++ b/apps/web/src/components/game/adventurerPanel.tsx @@ -16,6 +16,31 @@ import type { Adventurer } from "@elysium/types"; type BatchSize = 1 | 5 | 10 | 25 | 100 | "max"; const batchOptions: Array = [ 1, 5, 10, 25, 100, "max" ]; +/** + * Parses a localStorage string back into a valid BatchSize, defaulting to 1. + * @param stored - The raw string from localStorage (or null if absent). + * @returns A valid BatchSize value. + */ +const parseBatchSize = (stored: string | null): BatchSize => { + if (stored === "max") { + return "max"; + } + const numeric = Number(stored); + if (numeric === 5) { + return 5; + } + if (numeric === 10) { + return 10; + } + if (numeric === 25) { + return 25; + } + if (numeric === 100) { + return 100; + } + return 1; +}; + /** * Computes the total cost to buy a batch of adventurers. * @param adventurer - The adventurer to buy. @@ -148,7 +173,9 @@ const AdventurerCard = ({ const AdventurerPanel = (): JSX.Element => { const { state, formatNumber } = useGame(); const [ showLocked, setShowLocked ] = useState(true); - const [ batchSize, setBatchSize ] = useState(1); + const [ batchSize, setBatchSize ] = useState(() => { + return parseBatchSize(localStorage.getItem("elysium_batch_size")); + }); if (state === null) { return ( @@ -196,6 +223,7 @@ const AdventurerPanel = (): JSX.Element => { {batchOptions.map((option) => { function handleBatchSelect(): void { setBatchSize(option); + localStorage.setItem("elysium_batch_size", String(option)); } return (