diff --git a/apps/user-ui/app/(drawer)/(tabs)/home/_layout.tsx b/apps/user-ui/app/(drawer)/(tabs)/home/_layout.tsx index ca8365f..29f3908 100644 --- a/apps/user-ui/app/(drawer)/(tabs)/home/_layout.tsx +++ b/apps/user-ui/app/(drawer)/(tabs)/home/_layout.tsx @@ -12,7 +12,7 @@ export default function HomeLayout() { }} > { - const query = trpc.common.essentialConsts.useQuery(undefined, { - refetchInterval: 60000, - }) - return { ...query, refetch: query.refetch } -} - -// --------------------------------------------------------------------------- -// Persisted cache helpers — store fetched JSON keyed by its versioned URL. -// Only refetch + repersist when the version (URL) changes. -// --------------------------------------------------------------------------- - -interface PersistedCache { - version: string - data: T -} - -const CACHE_STORAGE_KEYS = { - products: 'cache_products', - stores: 'cache_stores', - slots: 'cache_slots', - banners: 'cache_banners', - availability: 'cache_availability', - storeProducts: (storeId: number) => `cache_store_${storeId}`, -} as const - -async function readPersistedCache(key: string): Promise | null> { - const raw = await StorageServiceCasual.getItem(key) - if (!raw) return null - try { - return JSON.parse(raw) as PersistedCache - } catch { - return null - } -} - -async function writePersistedCache(key: string, version: string, data: T): Promise { - await StorageServiceCasual.setItem(key, JSON.stringify({ version, data })) -} - -/** - * Loads the persisted cache for `storageKey` and reports whether its version - * matches the current `version`. Returns { initialData, isReady }. - * - version matches → serve persisted data, no network refetch needed. - * - version differs → signal to refetch (caller's query fetches + repersists). - */ -function usePersistedCache(storageKey: string, version: string | null) { - const [initialData, setInitialData] = useState(undefined) + const [initialData, setInitialData] = useState(undefined) const [isReady, setIsReady] = useState(false) + // Load the last persisted response so config is available immediately. useEffect(() => { let cancelled = false - setIsReady(false) - setInitialData(undefined) - if (!version) { - setIsReady(true) - return - } - readPersistedCache(storageKey).then((persisted) => { + StorageServiceCasual.getItem(ESSENTIAL_CONSTS_KEY).then((raw) => { if (cancelled) return - if (persisted && persisted.version === version) { - setInitialData(persisted.data) + if (raw) { + try { + setInitialData(JSON.parse(raw)) + } catch { + // ignore corrupt cache + } } setIsReady(true) }) return () => { cancelled = true } - }, [storageKey, version]) + }, []) - return { initialData, isReady } + const query = trpc.common.essentialConsts.useQuery(undefined, { + refetchInterval: 60000, + enabled: isReady, + placeholderData: initialData, + }) + + // Persist the latest response. + useEffect(() => { + if (query.data) { + StorageServiceCasual.setItem(ESSENTIAL_CONSTS_KEY, JSON.stringify(query.data)) + } + }, [query.data]) + + return { ...query, refetch: query.refetch } } type ProductsResponse = AllProductsApiType; type StoresResponse = StoresApiType; type SlotsResponse = SlotsApiType; -type EssentialConstsResponse = EssentialConstsApiType; type BannersResponse = BannersApiType; type StoreWithProductsResponse = StoreWithProductsApiType; type AvailabilityResponse = AvailabilityApiType; @@ -142,7 +112,6 @@ export function useAllProducts() { const cacheUrl = useCacheUrl(CACHE_FILENAMES.products) const { data: availabilityData } = useAvailability() const version = cacheUrl ?? '' - const { initialData, isReady } = usePersistedCache(CACHE_STORAGE_KEYS.products, version) const productsQuery = useQuery({ queryKey: ['all-products', version], @@ -151,12 +120,10 @@ export function useAllProducts() { throw new Error('Cache URL not available') } const response = await axios.get(cacheUrl) - await writePersistedCache(CACHE_STORAGE_KEYS.products, version, response.data) return response.data }, staleTime: 60000, // 1 minute - enabled: !!cacheUrl && isReady, - placeholderData: initialData, + enabled: !!cacheUrl, }) const mergedProducts = React.useMemo(() => { @@ -197,7 +164,6 @@ export function useAllProducts() { export function useAvailability() { const cacheUrl = useAvailabilityCacheUrl() const version = cacheUrl ?? '' - const { initialData, isReady } = usePersistedCache(CACHE_STORAGE_KEYS.availability, version) return useQuery({ queryKey: ['availability', version], @@ -206,19 +172,16 @@ export function useAvailability() { throw new Error('Cache URL not available') } const response = await axios.get(cacheUrl) - await writePersistedCache(CACHE_STORAGE_KEYS.availability, version, response.data) return response.data }, staleTime: 60000, // 1 minute - enabled: !!cacheUrl && isReady, - placeholderData: initialData, + enabled: !!cacheUrl, }) } export function useStores() { const cacheUrl = useCacheUrl(CACHE_FILENAMES.stores) const version = cacheUrl ?? '' - const { initialData, isReady } = usePersistedCache(CACHE_STORAGE_KEYS.stores, version) return useQuery({ queryKey: ['stores', version], @@ -227,19 +190,16 @@ export function useStores() { throw new Error('Cache URL not available') } const response = await axios.get(cacheUrl) - await writePersistedCache(CACHE_STORAGE_KEYS.stores, version, response.data) return response.data }, staleTime: 60000, // 1 minute - enabled: !!cacheUrl && isReady, - placeholderData: initialData, + enabled: !!cacheUrl, }) } export function useSlots() { const cacheUrl = useSlotsCacheUrl() const version = cacheUrl ?? '' - const { initialData, isReady } = usePersistedCache(CACHE_STORAGE_KEYS.slots, version) return useQuery({ queryKey: ['slots', version], @@ -247,36 +207,29 @@ export function useSlots() { if (!cacheUrl) { throw new Error('Cache URL not available') } - const response = await axios.get(cacheUrl+'?v=123') - await writePersistedCache(CACHE_STORAGE_KEYS.slots, version, response.data) + const response = await axios.get(cacheUrl + '?v=123') return response.data }, staleTime: 60000, // 1 minute - enabled: !!cacheUrl && isReady, - placeholderData: initialData, + enabled: !!cacheUrl, }) } export function useBanners() { const cacheUrl = useCacheUrl(CACHE_FILENAMES.banners) const version = cacheUrl ?? '' - const { initialData, isReady } = usePersistedCache(CACHE_STORAGE_KEYS.banners, version) return useQuery({ queryKey: ['banners', version], queryFn: async () => { - - if (!cacheUrl) { - throw new Error('Cache URL not available') - } - const response = await axios.get(cacheUrl) - await writePersistedCache(CACHE_STORAGE_KEYS.banners, version, response.data) - return response.data - + if (!cacheUrl) { + throw new Error('Cache URL not available') + } + const response = await axios.get(cacheUrl) + return response.data }, staleTime: 60000, // 1 minute - enabled: !!cacheUrl && isReady, - placeholderData: initialData, + enabled: !!cacheUrl, }) } @@ -291,7 +244,6 @@ export function useStoreWithProducts(storeId: number) { ? `${assetsDomain}${apiCacheKey}/v-${cacheVersion}/stores/${storeId}.json` : null const version = cacheUrl ?? '' - const { initialData, isReady } = usePersistedCache(CACHE_STORAGE_KEYS.storeProducts(storeId), version) return useQuery({ queryKey: ['store-with-products', storeId, version], @@ -300,11 +252,9 @@ export function useStoreWithProducts(storeId: number) { throw new Error('Cache URL not available') } const response = await axios.get(cacheUrl) - await writePersistedCache(CACHE_STORAGE_KEYS.storeProducts(storeId), version, response.data) return response.data }, staleTime: 60000, // 1 minute - enabled: !!cacheUrl && isReady, - placeholderData: initialData, + enabled: !!cacheUrl, }) }