diff --git a/src/components/ResourceList/HorizontalPaginationList.tsx b/src/components/ResourceList/HorizontalPaginationList.tsx index 7e29466..c9429cd 100644 --- a/src/components/ResourceList/HorizontalPaginationList.tsx +++ b/src/components/ResourceList/HorizontalPaginationList.tsx @@ -1,7 +1,7 @@ -import React, { useRef, useEffect, useState, useCallback } from "react"; +import React, { useEffect, useState, useCallback } from "react"; import DynamicGrid from "./DynamicGrid"; import LazyLoad from "../../common/LazyLoad"; -import { ListItem } from "../../state/cache"; +import { ListItem, useCacheStore } from "../../state/cache"; import { QortalMetadata } from "../../types/interfaces/resources"; import { ListItemWrapper } from "./ResourceListDisplay"; @@ -15,6 +15,7 @@ interface HorizontalPaginatedListProps { gap?: number; isLoading?: boolean; onSeenLastItem?: (listItem: ListItem) => void; + } export const HorizontalPaginatedList = ({ @@ -27,26 +28,25 @@ export const HorizontalPaginatedList = ({ gap, isLoading, onSeenLastItem, + }: HorizontalPaginatedListProps) => { - const listRef = useRef(null); const [displayedItems, setDisplayedItems] = useState(items); + useEffect(() => { setDisplayedItems(items); }, [items]); const preserveScroll = useCallback((updateFunction: () => void) => { - const container = listRef.current; - if (!container) return; - - const previousScrollLeft = container.scrollLeft; - const previousScrollWidth = container.scrollWidth; + const previousScrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; + const previousScrollWidth = document.documentElement.scrollWidth || document.body.scrollWidth; updateFunction(); // Perform the update (fetch new data, remove old) requestAnimationFrame(() => { - const newScrollWidth = container.scrollWidth; - container.scrollLeft = previousScrollLeft - (previousScrollWidth - newScrollWidth); + const newScrollWidth = document.documentElement.scrollWidth || document.body.scrollWidth; + document.documentElement.scrollLeft = document.body.scrollLeft = + previousScrollLeft - (previousScrollWidth - newScrollWidth); }); }, []); @@ -60,28 +60,23 @@ export const HorizontalPaginatedList = ({ }, [displayedItems, maxItems, preserveScroll]); useEffect(() => { - const container = listRef.current; - if (!container) return; - const handleScroll = () => { - if ( - container.scrollLeft + container.clientWidth >= container.scrollWidth - 10 && - !isLoading - ) { + const scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; + const clientWidth = document.documentElement.clientWidth || document.body.clientWidth; + const scrollWidth = document.documentElement.scrollWidth || document.body.scrollWidth; + + if (scrollLeft + clientWidth >= scrollWidth - 10 && !isLoading) { onLoadMore(); } }; - container.addEventListener("scroll", handleScroll); - return () => container.removeEventListener("scroll", handleScroll); + window.addEventListener("scroll", handleScroll); + return () => window.removeEventListener("scroll", handleScroll); }, [onLoadMore, isLoading]); return ( -
+
( @@ -95,19 +90,17 @@ export const HorizontalPaginatedList = ({ ))} > - {!isLoading && displayedItems.length > 0 && ( - { - onLoadMore(); - if (onSeenLastItem) { - // onSeenLastItem(displayedItems[displayedItems.length - 1]); - } - }} - /> - )} - - - + {!isLoading && displayedItems.length > 0 && ( + { + onLoadMore(); + if (onSeenLastItem) { + // onSeenLastItem(displayedItems[displayedItems.length - 1]); + } + }} + /> + )} +
); -}; \ No newline at end of file +}; diff --git a/src/components/ResourceList/ResourceListDisplay.tsx b/src/components/ResourceList/ResourceListDisplay.tsx index 64e86e5..dca7dde 100644 --- a/src/components/ResourceList/ResourceListDisplay.tsx +++ b/src/components/ResourceList/ResourceListDisplay.tsx @@ -23,6 +23,7 @@ import DynamicGrid from "./DynamicGrid"; import LazyLoad from "../../common/LazyLoad"; import { useListStore } from "../../state/lists"; import { useScrollTracker } from "../../common/useScrollTracker"; +import { HorizontalPaginatedList } from "./HorizontalPaginationList"; type Direction = "VERTICAL" | "HORIZONTAL"; interface ResourceListStyles { @@ -54,6 +55,8 @@ interface BaseProps { onSeenLastItem?: (listItem: QortalMetadata) => void; listName: string, children?: React.ReactNode; + searchCacheDuration?: number + resourceCacheDuration?: number } // ✅ Restrict `direction` only when `disableVirtualization = false` @@ -82,6 +85,8 @@ export const MemorizedComponent = ({ direction = "VERTICAL", onSeenLastItem, listName, + searchCacheDuration, + resourceCacheDuration }: PropsResourceListDisplay) => { const { fetchResources } = useResources(); const { getTemporaryResources, filterOutDeletedResources } = useCacheStore(); @@ -93,12 +98,6 @@ export const MemorizedComponent = ({ const list = useListStore().getListByName(listName) const isListExpired = useCacheStore().isListExpired(listName) const initialized = useRef(false) - useScrollTracker(listName); - - const listToDisplay = useMemo(()=> { - return filterOutDeletedResources([...getTemporaryResources(listName), ...list]) - }, [list, listName, filterOutDeletedResources, getTemporaryResources]) - const getResourceList = useCallback(async () => { try { @@ -122,6 +121,37 @@ export const MemorizedComponent = ({ } }, [memoizedParams, fetchResources]); // Added dependencies for re-fetching + useEffect(() => { + if(initialized.current) return + initialized.current = true + if(!isListExpired) return + + sessionStorage.removeItem(`scroll-position-${listName}`); + getResourceList(); + }, [getResourceList, isListExpired]); // Runs when dependencies change + + useScrollTracker(listName); + + const setSearchCacheExpiryDuration = useCacheStore().setSearchCacheExpiryDuration + const setResourceCacheExpiryDuration = useCacheStore().setResourceCacheExpiryDuration + useEffect(()=> { + if(searchCacheDuration){ + setSearchCacheExpiryDuration(searchCacheDuration) + } + }, []) + useEffect(()=> { + if(resourceCacheDuration){ + setResourceCacheExpiryDuration(resourceCacheDuration) + } + }, []) + + const listToDisplay = useMemo(()=> { + return filterOutDeletedResources([...getTemporaryResources(listName), ...list]) + }, [list, listName, filterOutDeletedResources, getTemporaryResources]) + + + + const getResourceMoreList = useCallback(async () => { try { // setIsLoading(true); @@ -137,13 +167,7 @@ export const MemorizedComponent = ({ } }, [memoizedParams, listName, list]); - useEffect(() => { - if(initialized.current) return - initialized.current = true - if(!isListExpired) return - sessionStorage.removeItem(`scroll-position-${listName}`); - getResourceList(); - }, [getResourceList, isListExpired]); // Runs when dependencies change + const disabledVirutalizationStyles: CSSProperties = useMemo(() => { if (styles?.disabledVirutalizationStyles?.parentContainer) @@ -212,7 +236,6 @@ export const MemorizedComponent = ({ )} {disableVirtualization && direction === "HORIZONTAL" && ( <> - { fetchIndividualPublish, addNewResources, updateNewResources, - deleteProduct + deleteProduct, }; }; diff --git a/src/state/cache.ts b/src/state/cache.ts index 2e631fb..b257881 100644 --- a/src/state/cache.ts +++ b/src/state/cache.ts @@ -65,14 +65,21 @@ interface CacheState { markResourceAsDeleted: (item: QortalMetadata) => void; filterOutDeletedResources: (items: QortalMetadata[]) => QortalMetadata[]; isListExpired: (listName: string)=> boolean + searchCacheExpiryDuration: number; + resourceCacheExpiryDuration: number; + setSearchCacheExpiryDuration: (duration: number) => void; + setResourceCacheExpiryDuration: (duration: number)=> void; } export const useCacheStore = create ((set, get) => ({ + searchCacheExpiryDuration: 5 * 60 * 1000, + resourceCacheExpiryDuration: 30 * 60 * 1000, resourceCache: {}, searchCache: {}, deletedResources: {}, - + setSearchCacheExpiryDuration: (duration) => set({ searchCacheExpiryDuration: duration }), + setResourceCacheExpiryDuration: (duration) => set({ resourceCacheExpiryDuration: duration }), getResourceCache: (id, ignoreExpire) => { const cache = get().resourceCache[id]; if (cache) { @@ -91,7 +98,7 @@ export const useCacheStore = create setResourceCache: (id, data, customExpiry) => set((state) => { - const expiry = Date.now() + (customExpiry || 30 * 60 * 1000); // 30 mins + const expiry = Date.now() + (customExpiry || get().resourceCacheExpiryDuration); return { resourceCache: { ...state.resourceCache, @@ -100,24 +107,25 @@ export const useCacheStore = create }; }), - setSearchCache: (listName, searchTerm, data, customExpiry) => - set((state) => { - const expiry = Date.now() + (customExpiry || 5 * 60 * 1000); // 5 mins - - return { - searchCache: { - ...state.searchCache, - [listName]: { - searches: { - ...(state.searchCache[listName]?.searches || {}), - [searchTerm]: data, + setSearchCache: (listName, searchTerm, data, customExpiry) => + set((state) => { + const expiry = Date.now() + (customExpiry || get().searchCacheExpiryDuration); + + return { + searchCache: { + ...state.searchCache, + [listName]: { + searches: { + ...(state.searchCache[listName]?.searches || {}), + [searchTerm]: data, + }, + temporaryNewResources: state.searchCache[listName]?.temporaryNewResources || [], + expiry, }, - temporaryNewResources: state.searchCache[listName]?.temporaryNewResources || [], - expiry, }, - }, - }; - }), + }; + }), + getSearchCache: (listName, searchTerm) => { const cache = get().searchCache[listName];