From f0a70d26ace9092bf804eaaca62683da46c025da Mon Sep 17 00:00:00 2001 From: 0xForkHunter <0xforkhunter@protonmail.com> Date: Wed, 5 Mar 2025 00:02:03 +0700 Subject: [PATCH] delete use-state-persisted and use user settings store. Fix store expiration --- .../src/components/charts/PrivateChart.tsx | 19 +++--- .../src/configs/local-storage-keys.ts | 2 +- .../frontend/src/hooks/use-state-persisted.ts | 64 ------------------- .../src/lib/store/user-settings-store.ts | 19 ++++-- 4 files changed, 24 insertions(+), 80 deletions(-) delete mode 100644 src/typescript/frontend/src/hooks/use-state-persisted.ts diff --git a/src/typescript/frontend/src/components/charts/PrivateChart.tsx b/src/typescript/frontend/src/components/charts/PrivateChart.tsx index a0a2e2cae..320e056ab 100644 --- a/src/typescript/frontend/src/components/charts/PrivateChart.tsx +++ b/src/typescript/frontend/src/components/charts/PrivateChart.tsx @@ -30,7 +30,7 @@ import { useRouter } from "next/navigation"; import { ROUTES } from "router/routes"; import path from "path"; import { emojisToName } from "lib/utils/emojis-to-name-or-symbol"; -import { useEventStore } from "context/event-store-context"; +import { useEventStore, useUserSettings } from "context/event-store-context"; import { getPeriodStartTimeFromTime } from "@sdk/utils"; import { getSymbolEmojisInString, symbolToEmojis, toMarketEmojiData } from "@sdk/emoji_data"; import { type PeriodicStateEventModel, type MarketMetadataModel } from "@sdk/indexer-v2/types"; @@ -45,7 +45,6 @@ import { import { emoji, parseJSON } from "utils"; import { Emoji } from "utils/emoji"; import { getAptosClient } from "@sdk/utils/aptos-client"; -import useStatePersisted from "@hooks/use-state-persisted"; import { createSwitch } from "components/charts/EmptyCandlesSwitch"; const configurationData: DatafeedConfiguration = { @@ -78,10 +77,10 @@ export const Chart = (props: ChartContainerProps) => { const ref = useRef(null); const router = useRouter(); const symbol = props.symbol; - const [showEmptyCandles, setShowEmptyCandles] = useStatePersisted( - "chart.showEmptyCandles", - false - ); + + const showEmptyBars = useUserSettings((s) => s.showEmptyBars); + const setShowEmptyBars = useUserSettings((s) => s.setShowEmptyBars); + const subscribeToPeriod = useEventStore((s) => s.subscribeToPeriod); const unsubscribeFromPeriod = useEventStore((s) => s.unsubscribeFromPeriod); const setLatestBars = useEventStore((s) => s.setLatestBars); @@ -146,8 +145,8 @@ export const Chart = (props: ChartContainerProps) => { exchange: EXCHANGE_NAME, listed_exchange: "", session: "24x7", - // If has_empty_bars is undefined, we use showEmptyCandles value, which contains the value from local storage. - has_empty_bars: has_empty_bars ? has_empty_bars === "true" || false : showEmptyCandles, + // If has_empty_bars is undefined, we use showEmptyBars value, which contains the value from local storage. + has_empty_bars: has_empty_bars ? has_empty_bars === "true" || false : showEmptyBars, has_seconds: false, has_intraday: true, has_daily: true, @@ -368,7 +367,7 @@ export const Chart = (props: ChartContainerProps) => { const btn = tvWidget.current.createButton(); const { setState } = createSwitch(btn, { - initialState: showEmptyCandles, + initialState: showEmptyBars, label: "Empty candles", onTitle: "Hide empty candles", offTitle: "Show empty candles", @@ -377,7 +376,7 @@ export const Chart = (props: ChartContainerProps) => { btn.addEventListener("click", () => { const chart = tvWidget.current?.activeChart(); if (!chart) return; - setShowEmptyCandles((prev) => { + setShowEmptyBars((prev) => { const show = !prev; chart.setSymbol(formatSymbolWithParams(chart.symbol(), { has_empty_bars: show })); setState(show); diff --git a/src/typescript/frontend/src/configs/local-storage-keys.ts b/src/typescript/frontend/src/configs/local-storage-keys.ts index f3459cca3..6312f12ee 100644 --- a/src/typescript/frontend/src/configs/local-storage-keys.ts +++ b/src/typescript/frontend/src/configs/local-storage-keys.ts @@ -51,7 +51,7 @@ export function readLocalStorageCache(key: keyof typeof LOCAL_STORAGE_KEYS): return null; } // Check for staleness. - if (new Date(cache.expiry) > new Date()) { + if (!cache.expiry || new Date(cache.expiry) > new Date()) { return cache.data; } } catch (e) { diff --git a/src/typescript/frontend/src/hooks/use-state-persisted.ts b/src/typescript/frontend/src/hooks/use-state-persisted.ts deleted file mode 100644 index ebdc10c91..000000000 --- a/src/typescript/frontend/src/hooks/use-state-persisted.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { useEffect, useState } from "react"; - -/** - * A custom React hook that behaves like useState but persists the state in localStorage/sessionStorage. - * Note: The "useLocalStorage" hook from "react-use" package doesn't provide the correct previous value - * when setting state using a callback function. This hook fixes that issue. - * - * Warning: This shouldn't be used to share values between components. If two separate components - * use the same key, updating one state will not update the other. - * - * @template T The type of the state value - * @param {string} key The key under which the value will be stored in storage - * @param {T} initialValue The initial value to use if no value exists in storage - * @param {"localStorage" | "sessionStorage"} [storageType="localStorage"] The type of storage to use - * @returns {[T, React.Dispatch>, () => void]} A tuple containing: - * - The current state value - * - A function to update the state - * - A function to clear the stored value - */ -function useStatePersisted( - key: string, - initialValue: T, - storageType: "localStorage" | "sessionStorage" = "localStorage" -): [T, React.Dispatch>, () => void] { - // Function to get the value from local storage or return the initial value if not found - const readValue = (): T => { - // If server-side rendering, return initialValue directly - if (typeof window === "undefined") { - return initialValue; - } - - try { - const item = window[storageType].getItem(key); - return item ? JSON.parse(item) : initialValue; - } catch (error) { - console.warn(`Error reading localStorage key “${key}”:`, error); - return initialValue; - } - }; - - // State and setter for our value - const [storedValue, setStoredValue] = useState(readValue); - - const clear = () => { - window[storageType].removeItem(key); - }; - - // Effect to persist changes to local storage - useEffect(() => { - if (typeof window == "undefined") { - return; - } - - try { - window[storageType].setItem(key, JSON.stringify(storedValue)); - } catch (error) { - console.warn(`Error setting localStorage key “${key}”:`, error); - } - }, [key, storedValue, storageType]); - - return [storedValue, setStoredValue, clear]; -} - -export default useStatePersisted; diff --git a/src/typescript/frontend/src/lib/store/user-settings-store.ts b/src/typescript/frontend/src/lib/store/user-settings-store.ts index 8292dd4da..73589de87 100644 --- a/src/typescript/frontend/src/lib/store/user-settings-store.ts +++ b/src/typescript/frontend/src/lib/store/user-settings-store.ts @@ -3,10 +3,12 @@ import { createStore } from "zustand"; export type UserSettingsState = { animate: boolean; + showEmptyBars: boolean; }; export type UserSettingsActions = { setAnimate: (value: boolean) => void; + setShowEmptyBars: (fn: (prev: boolean) => boolean) => void; userAgent: string; toggleAnimate: () => void; }; @@ -19,6 +21,7 @@ const saveSettings = (state: UserSettingsState) => { const defaultValues: UserSettingsState = { animate: true, + showEmptyBars: true, }; const readSettings = (): UserSettingsState => readLocalStorageCache("settings") ?? defaultValues; @@ -27,15 +30,21 @@ export const createUserSettingsStore = (userAgent: string) => createStore()((set) => ({ ...readSettings(), userAgent, + setShowEmptyBars: (fn: (prev: boolean) => boolean) => + set((state) => { + const newState = { ...state, showEmptyBars: fn(state.showEmptyBars) }; + saveSettings(newState); + return newState; + }), setAnimate: (value) => - set(() => { - const state = { animate: value }; - saveSettings(state); - return state; + set((state) => { + const newState = { ...state, animate: value }; + saveSettings(newState); + return newState; }), toggleAnimate: () => set((state) => { - const newState = { animate: !state.animate }; + const newState = { ...state, animate: !state.animate }; saveSettings(newState); return newState; }),