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 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<HTMLDivElement | null>(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 (
<div ref={listRef} style={{
overflow: 'auto', width: '100%', display: 'flex', flexGrow: 1
}}>
<div style={{ overflow: "auto", width: "100%", display: "flex", flexGrow: 1 }}>
<DynamicGrid
minItemWidth={minItemWidth}
gap={gap}
items={displayedItems.map((item, index) => (
@ -95,19 +90,17 @@ export const HorizontalPaginatedList = ({
</React.Fragment>
))}
>
{!isLoading && displayedItems.length > 0 && (
<LazyLoad
onLoadMore={() => {
onLoadMore();
if (onSeenLastItem) {
// onSeenLastItem(displayedItems[displayedItems.length - 1]);
}
}}
/>
)}
</DynamicGrid>
{!isLoading && displayedItems.length > 0 && (
<LazyLoad
onLoadMore={() => {
onLoadMore();
if (onSeenLastItem) {
// onSeenLastItem(displayedItems[displayedItems.length - 1]);
}
}}
/>
)}
</DynamicGrid>
</div>
);
};
};

View File

@ -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" && (
<>
<DynamicGrid
minItemWidth={styles?.horizontalStyles?.minItemWidth}
gap={styles?.gap}

View File

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

View File

@ -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<CacheState>
((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<CacheState>
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<CacheState>
};
}),
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];