added custom expiration to caches

This commit is contained in:
PhilReact 2025-03-15 15:53:35 +02:00
parent 52f1d570a6
commit dcfe746b5b
4 changed files with 94 additions and 70 deletions

View File

@ -1,7 +1,7 @@
import React, { useRef, useEffect, useState, useCallback } from "react"; import React, { useEffect, useState, useCallback } from "react";
import DynamicGrid from "./DynamicGrid"; import DynamicGrid from "./DynamicGrid";
import LazyLoad from "../../common/LazyLoad"; import LazyLoad from "../../common/LazyLoad";
import { ListItem } from "../../state/cache"; import { ListItem, useCacheStore } from "../../state/cache";
import { QortalMetadata } from "../../types/interfaces/resources"; import { QortalMetadata } from "../../types/interfaces/resources";
import { ListItemWrapper } from "./ResourceListDisplay"; import { ListItemWrapper } from "./ResourceListDisplay";
@ -15,6 +15,7 @@ interface HorizontalPaginatedListProps {
gap?: number; gap?: number;
isLoading?: boolean; isLoading?: boolean;
onSeenLastItem?: (listItem: ListItem) => void; onSeenLastItem?: (listItem: ListItem) => void;
} }
export const HorizontalPaginatedList = ({ export const HorizontalPaginatedList = ({
@ -27,26 +28,25 @@ export const HorizontalPaginatedList = ({
gap, gap,
isLoading, isLoading,
onSeenLastItem, onSeenLastItem,
}: HorizontalPaginatedListProps) => { }: HorizontalPaginatedListProps) => {
const listRef = useRef<HTMLDivElement | null>(null);
const [displayedItems, setDisplayedItems] = useState(items); const [displayedItems, setDisplayedItems] = useState(items);
useEffect(() => { useEffect(() => {
setDisplayedItems(items); setDisplayedItems(items);
}, [items]); }, [items]);
const preserveScroll = useCallback((updateFunction: () => void) => { const preserveScroll = useCallback((updateFunction: () => void) => {
const container = listRef.current; const previousScrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
if (!container) return; const previousScrollWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
const previousScrollLeft = container.scrollLeft;
const previousScrollWidth = container.scrollWidth;
updateFunction(); // Perform the update (fetch new data, remove old) updateFunction(); // Perform the update (fetch new data, remove old)
requestAnimationFrame(() => { requestAnimationFrame(() => {
const newScrollWidth = container.scrollWidth; const newScrollWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
container.scrollLeft = previousScrollLeft - (previousScrollWidth - newScrollWidth); document.documentElement.scrollLeft = document.body.scrollLeft =
previousScrollLeft - (previousScrollWidth - newScrollWidth);
}); });
}, []); }, []);
@ -60,28 +60,23 @@ export const HorizontalPaginatedList = ({
}, [displayedItems, maxItems, preserveScroll]); }, [displayedItems, maxItems, preserveScroll]);
useEffect(() => { useEffect(() => {
const container = listRef.current;
if (!container) return;
const handleScroll = () => { const handleScroll = () => {
if ( const scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
container.scrollLeft + container.clientWidth >= container.scrollWidth - 10 && const clientWidth = document.documentElement.clientWidth || document.body.clientWidth;
!isLoading const scrollWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
) {
if (scrollLeft + clientWidth >= scrollWidth - 10 && !isLoading) {
onLoadMore(); onLoadMore();
} }
}; };
container.addEventListener("scroll", handleScroll); window.addEventListener("scroll", handleScroll);
return () => container.removeEventListener("scroll", handleScroll); return () => window.removeEventListener("scroll", handleScroll);
}, [onLoadMore, isLoading]); }, [onLoadMore, isLoading]);
return ( return (
<div ref={listRef} style={{ <div style={{ overflow: "auto", width: "100%", display: "flex", flexGrow: 1 }}>
overflow: 'auto', width: '100%', display: 'flex', flexGrow: 1
}}>
<DynamicGrid <DynamicGrid
minItemWidth={minItemWidth} minItemWidth={minItemWidth}
gap={gap} gap={gap}
items={displayedItems.map((item, index) => ( items={displayedItems.map((item, index) => (
@ -106,8 +101,6 @@ export const HorizontalPaginatedList = ({
/> />
)} )}
</DynamicGrid> </DynamicGrid>
</div> </div>
); );
}; };

View File

@ -23,6 +23,7 @@ import DynamicGrid from "./DynamicGrid";
import LazyLoad from "../../common/LazyLoad"; import LazyLoad from "../../common/LazyLoad";
import { useListStore } from "../../state/lists"; import { useListStore } from "../../state/lists";
import { useScrollTracker } from "../../common/useScrollTracker"; import { useScrollTracker } from "../../common/useScrollTracker";
import { HorizontalPaginatedList } from "./HorizontalPaginationList";
type Direction = "VERTICAL" | "HORIZONTAL"; type Direction = "VERTICAL" | "HORIZONTAL";
interface ResourceListStyles { interface ResourceListStyles {
@ -54,6 +55,8 @@ interface BaseProps {
onSeenLastItem?: (listItem: QortalMetadata) => void; onSeenLastItem?: (listItem: QortalMetadata) => void;
listName: string, listName: string,
children?: React.ReactNode; children?: React.ReactNode;
searchCacheDuration?: number
resourceCacheDuration?: number
} }
// ✅ Restrict `direction` only when `disableVirtualization = false` // ✅ Restrict `direction` only when `disableVirtualization = false`
@ -82,6 +85,8 @@ export const MemorizedComponent = ({
direction = "VERTICAL", direction = "VERTICAL",
onSeenLastItem, onSeenLastItem,
listName, listName,
searchCacheDuration,
resourceCacheDuration
}: PropsResourceListDisplay) => { }: PropsResourceListDisplay) => {
const { fetchResources } = useResources(); const { fetchResources } = useResources();
const { getTemporaryResources, filterOutDeletedResources } = useCacheStore(); const { getTemporaryResources, filterOutDeletedResources } = useCacheStore();
@ -93,12 +98,6 @@ export const MemorizedComponent = ({
const list = useListStore().getListByName(listName) const list = useListStore().getListByName(listName)
const isListExpired = useCacheStore().isListExpired(listName) const isListExpired = useCacheStore().isListExpired(listName)
const initialized = useRef(false) const initialized = useRef(false)
useScrollTracker(listName);
const listToDisplay = useMemo(()=> {
return filterOutDeletedResources([...getTemporaryResources(listName), ...list])
}, [list, listName, filterOutDeletedResources, getTemporaryResources])
const getResourceList = useCallback(async () => { const getResourceList = useCallback(async () => {
try { try {
@ -122,6 +121,37 @@ export const MemorizedComponent = ({
} }
}, [memoizedParams, fetchResources]); // Added dependencies for re-fetching }, [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 () => { const getResourceMoreList = useCallback(async () => {
try { try {
// setIsLoading(true); // setIsLoading(true);
@ -137,13 +167,7 @@ export const MemorizedComponent = ({
} }
}, [memoizedParams, listName, list]); }, [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(() => { const disabledVirutalizationStyles: CSSProperties = useMemo(() => {
if (styles?.disabledVirutalizationStyles?.parentContainer) if (styles?.disabledVirutalizationStyles?.parentContainer)
@ -212,7 +236,6 @@ export const MemorizedComponent = ({
)} )}
{disableVirtualization && direction === "HORIZONTAL" && ( {disableVirtualization && direction === "HORIZONTAL" && (
<> <>
<DynamicGrid <DynamicGrid
minItemWidth={styles?.horizontalStyles?.minItemWidth} minItemWidth={styles?.horizontalStyles?.minItemWidth}
gap={styles?.gap} gap={styles?.gap}

View File

@ -249,7 +249,7 @@ export const useResources = () => {
fetchIndividualPublish, fetchIndividualPublish,
addNewResources, addNewResources,
updateNewResources, updateNewResources,
deleteProduct deleteProduct,
}; };
}; };

View File

@ -65,14 +65,21 @@ interface CacheState {
markResourceAsDeleted: (item: QortalMetadata) => void; markResourceAsDeleted: (item: QortalMetadata) => void;
filterOutDeletedResources: (items: QortalMetadata[]) => QortalMetadata[]; filterOutDeletedResources: (items: QortalMetadata[]) => QortalMetadata[];
isListExpired: (listName: string)=> boolean isListExpired: (listName: string)=> boolean
searchCacheExpiryDuration: number;
resourceCacheExpiryDuration: number;
setSearchCacheExpiryDuration: (duration: number) => void;
setResourceCacheExpiryDuration: (duration: number)=> void;
} }
export const useCacheStore = create<CacheState> export const useCacheStore = create<CacheState>
((set, get) => ({ ((set, get) => ({
searchCacheExpiryDuration: 5 * 60 * 1000,
resourceCacheExpiryDuration: 30 * 60 * 1000,
resourceCache: {}, resourceCache: {},
searchCache: {}, searchCache: {},
deletedResources: {}, deletedResources: {},
setSearchCacheExpiryDuration: (duration) => set({ searchCacheExpiryDuration: duration }),
setResourceCacheExpiryDuration: (duration) => set({ resourceCacheExpiryDuration: duration }),
getResourceCache: (id, ignoreExpire) => { getResourceCache: (id, ignoreExpire) => {
const cache = get().resourceCache[id]; const cache = get().resourceCache[id];
if (cache) { if (cache) {
@ -91,7 +98,7 @@ export const useCacheStore = create<CacheState>
setResourceCache: (id, data, customExpiry) => setResourceCache: (id, data, customExpiry) =>
set((state) => { set((state) => {
const expiry = Date.now() + (customExpiry || 30 * 60 * 1000); // 30 mins const expiry = Date.now() + (customExpiry || get().resourceCacheExpiryDuration);
return { return {
resourceCache: { resourceCache: {
...state.resourceCache, ...state.resourceCache,
@ -102,7 +109,7 @@ export const useCacheStore = create<CacheState>
setSearchCache: (listName, searchTerm, data, customExpiry) => setSearchCache: (listName, searchTerm, data, customExpiry) =>
set((state) => { set((state) => {
const expiry = Date.now() + (customExpiry || 5 * 60 * 1000); // 5 mins const expiry = Date.now() + (customExpiry || get().searchCacheExpiryDuration);
return { return {
searchCache: { searchCache: {
@ -119,6 +126,7 @@ export const useCacheStore = create<CacheState>
}; };
}), }),
getSearchCache: (listName, searchTerm) => { getSearchCache: (listName, searchTerm) => {
const cache = get().searchCache[listName]; const cache = get().searchCache[listName];
if (cache) { if (cache) {