diff options
| author | Fuwn <[email protected]> | 2026-03-28 08:54:30 +0000 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-03-28 08:54:30 +0000 |
| commit | 802c3f85d1577dc32d670542cb7d2fc4329008dd (patch) | |
| tree | f67b7b0a114ee42b5ab118870a1e6eab87dacaec /src | |
| parent | fix(filters): apply list filter changes immediately (diff) | |
| download | due.moe-802c3f85d1577dc32d670542cb7d2fc4329008dd.tar.xz due.moe-802c3f85d1577dc32d670542cb7d2fc4329008dd.zip | |
fix(state): restore persisted list UI state
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/List/Anime/DueIndexColumn.svelte | 5 | ||||
| -rw-r--r-- | src/routes/+page.svelte | 5 | ||||
| -rw-r--r-- | src/routes/completed/+page.svelte | 5 | ||||
| -rw-r--r-- | src/stores/stateBin.ts | 88 |
4 files changed, 68 insertions, 35 deletions
diff --git a/src/lib/List/Anime/DueIndexColumn.svelte b/src/lib/List/Anime/DueIndexColumn.svelte index 1df355a4..3fadc8c2 100644 --- a/src/lib/List/Anime/DueIndexColumn.svelte +++ b/src/lib/List/Anime/DueIndexColumn.svelte @@ -5,12 +5,13 @@ import locale from "$stores/locale"; import ListTitle from "../ListTitle.svelte"; import AnimeList from "$lib/List/Anime/DueAnimeList.svelte"; import { onMount } from "svelte"; -import stateBin from "$stores/stateBin"; +import stateBin, { hydrateStateBin } from "$stores/stateBin"; export let userIdentity: { id: number }; export let user: AniListAuthorisation; -onMount(() => { +onMount(async () => { + await hydrateStateBin(); $stateBin.dueAnimeListOpen ??= true; }); </script> diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index f29e4ad5..268e9713 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -11,7 +11,7 @@ import Skeleton from "$lib/Loading/Skeleton.svelte"; import locale from "$stores/locale.js"; import Landing from "$lib/Landing.svelte"; import LandingHero from "$lib/LandingHero.svelte"; -import stateBin from "$stores/stateBin.js"; +import stateBin, { hydrateStateBin } from "$stores/stateBin.js"; import type { PageData } from "./$types"; let { data }: { data: PageData } = $props(); @@ -63,8 +63,9 @@ const loadAuthenticatedHomeSurface = () => { return authenticatedHomeSurfaceImport; }; -onMount(() => { +onMount(async () => { removeHeightObserver = createHeightObserver(); + await hydrateStateBin(); $stateBin.upcomingAnimeListOpen ??= true; $stateBin.dueMangaListOpen ??= true; void loadAuthenticatedHomeSurface(); diff --git a/src/routes/completed/+page.svelte b/src/routes/completed/+page.svelte index 220901bf..bcad912b 100644 --- a/src/routes/completed/+page.svelte +++ b/src/routes/completed/+page.svelte @@ -10,7 +10,7 @@ import { createHeightObserver } from "$lib/Utility/html.js"; import Skeleton from "$lib/Loading/Skeleton.svelte"; import locale from "$stores/locale.js"; import Landing from "$lib/Landing.svelte"; -import stateBin from "$stores/stateBin.js"; +import stateBin, { hydrateStateBin } from "$stores/stateBin.js"; import type { PageData } from "./$types"; let { data }: { data: PageData } = $props(); @@ -53,8 +53,9 @@ const loadCompletedSurface = () => { return completedSurfaceImport; }; -onMount(() => { +onMount(async () => { removeHeightObserver = createHeightObserver(); + await hydrateStateBin(); $stateBin.completedAnimeListOpen ??= true; $stateBin.completedMangaListOpen ??= true; void loadCompletedSurface(); diff --git a/src/stores/stateBin.ts b/src/stores/stateBin.ts index ca61ed77..3fd38a99 100644 --- a/src/stores/stateBin.ts +++ b/src/stores/stateBin.ts @@ -18,19 +18,26 @@ let state: StateBin = {}; let changedBeforeHydration = false; let initialEmission = true; let applyingStoredValue = false; +let hydrationPromise = Promise.resolve(); if (browser) { - localforage.getItem<StateBin>(STORAGE_KEY).then((value) => { - if (value && typeof value === "object" && !changedBeforeHydration) { - applyingStoredValue = true; - baseStore.set(value); - applyingStoredValue = false; - } + hydrationPromise = localforage + .getItem<StateBin>(STORAGE_KEY) + .then(async (value): Promise<void> => { + if (value && typeof value === "object") { + const nextState = changedBeforeHydration + ? { ...value, ...state } + : value; - hydrated = true; + applyingStoredValue = true; + baseStore.set(nextState); + applyingStoredValue = false; + } - localforage.setItem(STORAGE_KEY, state); - }); + hydrated = true; + + await localforage.setItem(STORAGE_KEY, state); + }); baseStore.subscribe((value) => { state = value; @@ -44,36 +51,59 @@ if (browser) { }); } +export const hydrateStateBin = () => hydrationPromise; + const createProxyStore = (store: Writable<StateBin>) => { - return new Proxy(store, { - get(target, prop: string) { - if (prop in target) - return (target as unknown as Record<string, unknown>)[prop]; + const keyStores = new Map< + string, + { + subscribe: (run: (value: StateBin[string]) => void) => () => void; + set: (value: StateBin[string]) => void; + update: (updater: (value: StateBin[string]) => StateBin[string]) => void; + } + >(); - const derivedKey = writable(get(store)[prop]); + const setKeyValue = (prop: string, value: StateBin[string]) => { + const state = get(store); + const updatedState = { ...state }; - derivedKey.subscribe((value) => { - const state = get(store); - const updatedState = { ...state }; + if (value === null || value === undefined) delete updatedState[prop]; + else updatedState[prop] = value; - if (value === null || value === undefined) delete updatedState[prop]; - else updatedState[prop] = value; + store.set(updatedState); + }; - store.set(updatedState); - }); + return new Proxy(store, { + get(target, prop: string | symbol) { + if (prop in target) return Reflect.get(target, prop); - return derivedKey; - }, + if (typeof prop !== "string") return undefined; + + const existingStore = keyStores.get(prop); - set(_, prop: string, value) { - const state = get(store); - const updatedState = { ...state }; + if (existingStore) return existingStore; - if (value === null || value === undefined) delete updatedState[prop]; - else updatedState[prop] = value; + const keyStore = { + subscribe(run: (value: StateBin[string]) => void) { + return store.subscribe((value) => run(value[prop])); + }, + set(value: StateBin[string]) { + setKeyValue(prop, value); + }, + update(updater: (value: StateBin[string]) => StateBin[string]) { + setKeyValue(prop, updater(get(store)[prop])); + }, + }; + + keyStores.set(prop, keyStore); + + return keyStore; + }, - store.set(updatedState); + set(_, prop: string | symbol, value) { + if (typeof prop !== "string") return false; + setKeyValue(prop, value); return true; }, }); |