aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-03-28 08:54:30 +0000
committerFuwn <[email protected]>2026-03-28 08:54:30 +0000
commit802c3f85d1577dc32d670542cb7d2fc4329008dd (patch)
treef67b7b0a114ee42b5ab118870a1e6eab87dacaec /src
parentfix(filters): apply list filter changes immediately (diff)
downloaddue.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.svelte5
-rw-r--r--src/routes/+page.svelte5
-rw-r--r--src/routes/completed/+page.svelte5
-rw-r--r--src/stores/stateBin.ts88
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;
},
});