From 14c2732fa0e455b8b0d1b8fb19cdebdc83a30776 Mon Sep 17 00:00:00 2001 From: calli Date: Sat, 27 Dec 2025 21:49:57 +0200 Subject: [PATCH] some users have so many characters that we cant keep them in localSotrage. use db --- src/app/page.tsx | 19 ++++----- src/storage.ts | 107 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 12 deletions(-) create mode 100644 src/storage.ts diff --git a/src/app/page.tsx b/src/app/page.tsx index cac18d7..2d4aa12 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -18,6 +18,7 @@ import { useSearchParams } from "next/navigation"; import { EvePraisalResult, fetchAllPrices } from "@/eve-praisal"; import { getPlanet, getPlanetUniverse, getPlanets } from "@/planets"; import { PlanetConfig } from "@/types"; +import { saveCharacters as saveCharactersDB, loadCharacters } from "@/storage"; // Add batch processing utility const processInBatches = async ( @@ -108,13 +109,8 @@ const Home = () => { return Promise.resolve(characters); }; - const initializeCharacters = useCallback((): AccessToken[] => { - const localStorageCharacters = localStorage.getItem("characters"); - if (localStorageCharacters) { - const characterArray: AccessToken[] = JSON.parse(localStorageCharacters); - return characterArray.filter((c) => c.access_token && c.character); - } - return []; + const initializeCharacters = useCallback(async (): Promise => { + return await loadCharacters(); }, []); const initializeCharacterPlanets = ( @@ -139,9 +135,8 @@ const Home = () => { }; }); - const saveCharacters = (characters: AccessToken[]): AccessToken[] => { - localStorage.setItem("characters", JSON.stringify(characters)); - return characters; + const saveCharacters = async (characters: AccessToken[]): Promise => { + return await saveCharactersDB(characters); }; const restoreCharacters = (characters: AccessToken[]) => { @@ -279,8 +274,8 @@ const Home = () => { useEffect(() => { const ESI_CACHE_TIME_MS = 3000000; - const interval = setInterval(() => { - const characters = initializeCharacters(); + const interval = setInterval(async () => { + const characters = await initializeCharacters(); refreshSession(characters) .then(saveCharacters) .then(initializeCharacterPlanets) diff --git a/src/storage.ts b/src/storage.ts new file mode 100644 index 0000000..33f6af2 --- /dev/null +++ b/src/storage.ts @@ -0,0 +1,107 @@ +import { AccessToken } from "./types"; + +const DB_NAME = "eve-pi-db"; +const DB_VERSION = 1; +const STORE_NAME = "characters"; + +// Initialize IndexedDB +const initDB = (): Promise => { + return new Promise((resolve, reject) => { + const request = indexedDB.open(DB_NAME, DB_VERSION); + + request.onerror = () => reject(request.error); + request.onsuccess = () => resolve(request.result); + + request.onupgradeneeded = (event) => { + const db = (event.target as IDBOpenDBRequest).result; + if (!db.objectStoreNames.contains(STORE_NAME)) { + db.createObjectStore(STORE_NAME); + } + }; + }); +}; + +// Save characters to IndexedDB +export const saveCharacters = async ( + characters: AccessToken[] +): Promise => { + try { + const db = await initDB(); + const transaction = db.transaction([STORE_NAME], "readwrite"); + const store = transaction.objectStore(STORE_NAME); + + store.put(characters, "characters"); + + return new Promise((resolve, reject) => { + transaction.oncomplete = () => { + db.close(); + resolve(characters); + }; + transaction.onerror = () => { + db.close(); + reject(transaction.error); + }; + }); + } catch (error) { + console.error("Failed to save to IndexedDB:", error); + // Fallback: save minimal data to localStorage + try { + const minimalCharacters = characters.map((c) => ({ + ...c, + planets: [], // Strip planet data to reduce size + })); + localStorage.setItem("characters", JSON.stringify(minimalCharacters)); + console.warn("Saved minimal character data to localStorage fallback"); + } catch (storageError) { + console.error("Failed to save to localStorage fallback:", storageError); + } + return characters; + } +}; + +// Load characters from IndexedDB +export const loadCharacters = async (): Promise => { + try { + const db = await initDB(); + const transaction = db.transaction([STORE_NAME], "readonly"); + const store = transaction.objectStore(STORE_NAME); + const request = store.get("characters"); + + return new Promise((resolve, reject) => { + request.onsuccess = () => { + db.close(); + const characters = request.result as AccessToken[] | undefined; + if (characters && characters.length > 0) { + resolve(characters); + } else { + // Try localStorage migration + resolve(migrateFromLocalStorage()); + } + }; + request.onerror = () => { + db.close(); + reject(request.error); + }; + }); + } catch (error) { + console.error("Failed to load from IndexedDB:", error); + // Fallback to localStorage + return migrateFromLocalStorage(); + } +}; + +// Migrate data from localStorage to IndexedDB +const migrateFromLocalStorage = (): AccessToken[] => { + try { + const localStorageCharacters = localStorage.getItem("characters"); + if (localStorageCharacters) { + const characterArray: AccessToken[] = JSON.parse(localStorageCharacters); + const filtered = characterArray.filter((c) => c.access_token && c.character); + // Don't delete from localStorage yet - keep as backup + return filtered; + } + } catch (error) { + console.error("Failed to migrate from localStorage:", error); + } + return []; +};