import { useCallback, useEffect, useMemo, useRef } from 'react'; import { ResourceStatus, usePublishStore } from '../state/publishes'; import { QortalGetMetadata } from '../types/interfaces/resources'; interface PropsUseResourceStatus { resource: QortalGetMetadata | null; retryAttempts?: number; path?: string; filename?: string; isGlobal?: boolean; disableAutoFetch?: boolean; } export const useResourceStatus = ({ resource, retryAttempts = 40, path, filename, isGlobal, disableAutoFetch, }: PropsUseResourceStatus) => { const resourceId = !resource ? null : `${resource.service}-${resource.name}-${resource.identifier}`; const status = usePublishStore((state) => state.getResourceStatus(resourceId)) || null; const intervalRef = useRef(null); const timeoutRef = useRef(null); const setResourceStatus = usePublishStore((state) => state.setResourceStatus); const getResourceStatus = usePublishStore((state) => state.getResourceStatus); const statusRef = useRef(null); const startGlobalDownload = usePublishStore( (state) => state.startGlobalDownload ); const stopGlobalDownload = usePublishStore( (state) => state.stopGlobalDownload ); useEffect(() => { statusRef.current = status; }, [status]); const downloadResource = useCallback( ( { service, name, identifier }: QortalGetMetadata, build?: boolean, isRecalling?: boolean ) => { try { if (statusRef.current && statusRef.current?.status === 'READY') { if (intervalRef.current) { clearInterval(intervalRef.current); } if (timeoutRef.current) { clearTimeout(timeoutRef.current); } intervalRef.current = null; timeoutRef.current = null; return; } if (!isRecalling) { const id = `${service}-${name}-${identifier}`; const resourceStatus = getResourceStatus(id); if (!resourceStatus) { setResourceStatus( { service, name, identifier }, { status: 'SEARCHING', localChunkCount: 0, totalChunkCount: 0, percentLoaded: 0, path: path || '', filename: filename || '', } ); } } let isCalling = false; let percentLoaded = 0; let timer = 29; let tries = 0; let calledFirstTime = false; const callFunction = async () => { if (isCalling) return; isCalling = true; let res; if (!build) { res = await qortalRequest({ action: 'GET_QDN_RESOURCE_STATUS', name: name, service: service, identifier: identifier, }); setResourceStatus( { service, name, identifier }, { ...res, } ); if (tries > retryAttempts) { if (intervalRef.current) { clearInterval(intervalRef.current); } if (timeoutRef.current) { clearTimeout(timeoutRef.current); } intervalRef.current = null; timeoutRef.current = null; setResourceStatus( { service, name, identifier }, { ...res, status: 'FAILED_TO_DOWNLOAD', } ); return; } tries = tries + 1; } if (build || (calledFirstTime === false && res?.status !== 'READY')) { const url = `/arbitrary/resource/properties/${service}/${name}/${identifier}?build=true`; const resCall = await fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json', }, }); res = await resCall.json(); } calledFirstTime = true; isCalling = false; if (res.localChunkCount) { if (res.percentLoaded) { if ( res.percentLoaded === percentLoaded && res.percentLoaded !== 100 ) { timer = timer - 5; } else { timer = 29; } if (timer < 0) { timer = 29; isCalling = true; setResourceStatus( { service, name, identifier }, { ...res, status: 'REFETCHING', } ); timeoutRef.current = setTimeout(() => { isCalling = false; downloadResource({ name, service, identifier }, true, true); }, 10000); return; } percentLoaded = res.percentLoaded; } setResourceStatus( { service, name, identifier }, { ...res, } ); } // Check if progress is 100% and clear interval if true if (res?.status === 'READY') { if (intervalRef.current) { clearInterval(intervalRef.current); } if (timeoutRef.current) { clearTimeout(timeoutRef.current); } intervalRef.current = null; timeoutRef.current = null; setResourceStatus( { service, name, identifier }, { ...res, } ); return; } if (res?.status === 'DOWNLOADED') { res = await qortalRequest({ action: 'GET_QDN_RESOURCE_STATUS', name: name, service: service, identifier: identifier, build: true, }); } }; callFunction(); if (!intervalRef.current) { intervalRef.current = setInterval(callFunction, 5000); } } catch (error) { console.error('Error during resource fetch:', error); } return () => { if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } if (timeoutRef.current) { clearTimeout(timeoutRef.current); timeoutRef.current = null; } }; }, [retryAttempts] ); useEffect(() => { if (disableAutoFetch) return; if (resource?.identifier && resource?.name && resource?.service) { const id = `${resource.service}-${resource.name}-${resource.identifier}`; if (isGlobal) { startGlobalDownload(id, resource, retryAttempts, path, filename); } else { statusRef.current = null; downloadResource({ service: resource?.service, name: resource?.name, identifier: resource?.identifier, }); } } return () => { if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } if (timeoutRef.current) { clearTimeout(timeoutRef.current); timeoutRef.current = null; } }; }, [ resource?.identifier, resource?.name, resource?.service, downloadResource, isGlobal, retryAttempts, path, filename, disableAutoFetch, ]); const handledownloadResource = useCallback(() => { if (resource?.identifier && resource?.name && resource?.service) { if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } if (timeoutRef.current) { clearTimeout(timeoutRef.current); timeoutRef.current = null; } const id = `${resource?.service}-${resource?.name}-${resource?.identifier}`; const resourceStatus = getResourceStatus(id); if (!resourceStatus) { setResourceStatus( { service: resource.service, name: resource.name, identifier: resource.identifier, }, { status: 'SEARCHING', localChunkCount: 0, totalChunkCount: 0, percentLoaded: 0, path: path || '', filename: filename || '', } ); } if (isGlobal) { const id = `${resource.service}-${resource.name}-${resource.identifier}`; stopGlobalDownload(id); startGlobalDownload(id, resource, retryAttempts, path, filename); } else { downloadResource({ service: resource?.service, name: resource?.name, identifier: resource?.identifier, }); } } }, [ resource?.identifier, resource?.name, resource?.service, downloadResource, isGlobal, retryAttempts, path, filename, ]); const resourceUrl = resource ? `/arbitrary/${resource.service}/${resource.name}/${resource.identifier}` : null; return useMemo( () => ({ status: status?.status || 'INITIAL', localChunkCount: status?.localChunkCount || 0, totalChunkCount: status?.totalChunkCount || 0, percentLoaded: status?.percentLoaded || 0, isReady: status?.status === 'READY', resourceUrl, downloadResource: handledownloadResource, }), [ status?.status, status?.localChunkCount, status?.totalChunkCount, status?.percentLoaded, resourceUrl, downloadResource, ] ); };