From 9c9d04c947ae69cb06a9c57fe26ea9fc62fbf08f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20G=C3=B3mez?= Date: Thu, 11 Dec 2025 00:18:28 +0100 Subject: [PATCH] fix: more robust storage --- src/components/RelayViewer.tsx | 4 +-- src/core/state.ts | 52 ++++++++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/components/RelayViewer.tsx b/src/components/RelayViewer.tsx index 0e2a4a5..eddf0f8 100644 --- a/src/components/RelayViewer.tsx +++ b/src/components/RelayViewer.tsx @@ -1,4 +1,4 @@ -import { Copy, Check, Server } from "lucide-react"; +import { Copy, Check } from "lucide-react"; import { useRelayInfo } from "../hooks/useRelayInfo"; import { useCopy } from "../hooks/useCopy"; import { Button } from "./ui/button"; @@ -46,7 +46,7 @@ export function RelayViewer({ url }: RelayViewerProps) { {(info?.contact || info?.pubkey) && (

Operator

-
+
{info.contact && info.contact.length == 64 && ( )} diff --git a/src/core/state.ts b/src/core/state.ts index 5b26db1..55fff11 100644 --- a/src/core/state.ts +++ b/src/core/state.ts @@ -1,5 +1,6 @@ +import { useEffect } from "react"; import { useAtom } from "jotai"; -import { atomWithStorage } from "jotai/utils"; +import { atomWithStorage, createJSONStorage } from "jotai/utils"; import { GrimoireState, AppId } from "@/types/app"; import { useLocale } from "@/hooks/useLocale"; import * as Logic from "./logic"; @@ -18,10 +19,47 @@ const initialState: GrimoireState = { }, }; -// Persistence Atom +// Custom storage with error handling +const storage = createJSONStorage(() => ({ + getItem: (key: string) => { + try { + const value = localStorage.getItem(key); + return value; + } catch (error) { + console.warn("Failed to read from localStorage:", error); + return null; + } + }, + setItem: (key: string, value: string) => { + try { + localStorage.setItem(key, value); + } catch (error) { + console.error("Failed to write to localStorage:", error); + // Handle quota exceeded or other errors + if ( + error instanceof DOMException && + error.name === "QuotaExceededError" + ) { + console.error( + "localStorage quota exceeded. State will not be persisted.", + ); + } + } + }, + removeItem: (key: string) => { + try { + localStorage.removeItem(key); + } catch (error) { + console.warn("Failed to remove from localStorage:", error); + } + }, +})); + +// Persistence Atom with custom storage export const grimoireStateAtom = atomWithStorage( "grimoire_v6", initialState, + storage, ); // The Hook @@ -29,10 +67,12 @@ export const useGrimoire = () => { const [state, setState] = useAtom(grimoireStateAtom); const browserLocale = useLocale(); - // Initialize locale from browser if not set - if (!state.locale) { - setState((prev) => ({ ...prev, locale: browserLocale })); - } + // Initialize locale from browser if not set (moved to useEffect to avoid race condition) + useEffect(() => { + if (!state.locale) { + setState((prev) => ({ ...prev, locale: browserLocale })); + } + }, [state.locale, browserLocale, setState]); return { state,