diff --git a/src/components/ResourceList/ResourceListDisplay.tsx b/src/components/ResourceList/ResourceListDisplay.tsx index f7b2997..e35cd3d 100644 --- a/src/components/ResourceList/ResourceListDisplay.tsx +++ b/src/components/ResourceList/ResourceListDisplay.tsx @@ -137,10 +137,12 @@ export const MemorizedComponent = ({ const [isLoading, setIsLoading] = useState(list?.length > 0 ? false : true); const isListExpired = useCacheStore().isListExpired(listName) - const isListExpiredRef = useRef(true) + const isListExpiredRef = useRef(true) + const memoizedParamsRef = useRef('') useEffect(()=> { isListExpiredRef.current = isListExpired - }, [isListExpired]) + memoizedParamsRef.current = memoizedParams + }, [isListExpired, memoizedParams]) const filterOutDeletedResources = useCacheStore((s) => s.filterOutDeletedResources); const deletedResources = useCacheStore((s) => s.deletedResources); @@ -264,11 +266,18 @@ const addItems = useListStore((s) => s.addItems); }, [resetSearch]) useEffect(() => { if(!generatedIdentifier) return - - if(!isListExpiredRef.current && !initialized.current) { - setIsLoading(false) + + if(typeof isListExpiredRef.current === 'string' && typeof memoizedParamsRef.current === 'string') { + const parsedParams = {...(JSON.parse(memoizedParamsRef.current))}; + parsedParams.identifier = generatedIdentifier + const stringedParams = JSON.stringify(parsedParams) + + if(stringedParams === isListExpiredRef.current && !initialized.current){ + setIsLoading(false) initialized.current = true return + } + } sessionStorage.removeItem(`scroll-position-${listName}`); diff --git a/src/hooks/useResources.tsx b/src/hooks/useResources.tsx index 7452c0e..584cbff 100644 --- a/src/hooks/useResources.tsx +++ b/src/hooks/useResources.tsx @@ -240,7 +240,7 @@ export const useResources = (retryAttempts: number = 2) => { if (!lastCreated) break; } - setSearchCache(listName, cacheKey, filteredResults); + setSearchCache(listName, cacheKey, filteredResults, JSON.stringify(params)); fetchDataFromResults(filteredResults, returnType); return filteredResults; diff --git a/src/index.ts b/src/index.ts index d57c0ed..2d38579 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ export { useResourceStatus } from './hooks/useResourceStatus'; export { Spacer } from './common/Spacer'; export { useModal } from './hooks/useModal'; import './index.css' +export { formatBytes, formatDuration } from './utils/numbers'; export { createQortalLink } from './utils/qortal'; export { IndexCategory } from './state/indexes'; export { hashWordWithoutPublicSalt } from './utils/encryption'; diff --git a/src/state/cache.ts b/src/state/cache.ts index 37ac510..c9ea99a 100644 --- a/src/state/cache.ts +++ b/src/state/cache.ts @@ -10,6 +10,7 @@ interface SearchCache { }; temporaryNewResources: QortalMetadata[], expiry: number; // Expiry timestamp for the whole list + searchParamsStringified: string; }; } @@ -55,7 +56,7 @@ interface CacheState { // Search cache actions setResourceCache: (id: string, data: ListItem | false | null, customExpiry?: number) => void; - setSearchCache: (listName: string, searchTerm: string, data: QortalMetadata[], customExpiry?: number) => void; + setSearchCache: (listName: string, searchTerm: string, data: QortalMetadata[], searchParamsStringified: string, customExpiry?: number) => void; getSearchCache: (listName: string, searchTerm: string) => QortalMetadata[] | null; clearExpiredCache: () => void; getResourceCache: (id: string, ignoreExpire?: boolean) => ListItem | false | null; @@ -64,7 +65,7 @@ interface CacheState { deletedResources: DeletedResources; markResourceAsDeleted: (item: QortalMetadata) => void; filterOutDeletedResources: (items: QortalMetadata[]) => QortalMetadata[]; - isListExpired: (listName: string)=> boolean + isListExpired: (listName: string)=> boolean | string; searchCacheExpiryDuration: number; resourceCacheExpiryDuration: number; setSearchCacheExpiryDuration: (duration: number) => void; @@ -107,7 +108,7 @@ export const useCacheStore = create }; }), - setSearchCache: (listName, searchTerm, data, customExpiry) => + setSearchCache: (listName, searchTerm, data, searchParamsStringified, customExpiry) => set((state) => { const expiry = Date.now() + (customExpiry || get().searchCacheExpiryDuration); @@ -121,6 +122,7 @@ export const useCacheStore = create }, temporaryNewResources: state.searchCache[listName]?.temporaryNewResources || [], expiry, + searchParamsStringified }, }, }; @@ -207,9 +209,10 @@ export const useCacheStore = create (item) => !deletedResources[`${item.service}-${item.name}-${item.identifier}`] ); }, - isListExpired: (listName: string): boolean => { + isListExpired: (listName: string): boolean | string => { const cache = get().searchCache[listName]; - return cache ? cache.expiry <= Date.now() : true; // ✅ Expired if expiry timestamp is in the past + const isExpired = cache ? cache.expiry <= Date.now() : true; // ✅ Expired if expiry timestamp is in the past + return isExpired === true ? true : cache.searchParamsStringified }, diff --git a/src/utils/numbers.ts b/src/utils/numbers.ts new file mode 100644 index 0000000..1742ed5 --- /dev/null +++ b/src/utils/numbers.ts @@ -0,0 +1,30 @@ +type ByteFormat = "Decimal" | "Binary"; +export function formatBytes( + bytes: number, + decimals = 2, + format: ByteFormat = "Binary" +) { + if (bytes === 0) return "0 Bytes"; + + const k = format === "Binary" ? 1024 : 1000; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i]; +} + +export function formatDuration(seconds: number): string { + const hrs = Math.floor(seconds / 3600); + const mins = Math.floor((seconds % 3600) / 60); + const secs = Math.floor(seconds % 60); + + const paddedMins = mins.toString().padStart(hrs > 0 ? 2 : 1, '0'); + const paddedSecs = secs.toString().padStart(2, '0'); + + return hrs > 0 + ? `${hrs}:${paddedMins}:${paddedSecs}` + : `${paddedMins}:${paddedSecs}`; + } + \ No newline at end of file