Files
qapp-core/src/hooks/useResourceStatus.tsx
2025-08-31 00:08:50 +02:00

339 lines
9.5 KiB
TypeScript

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<any>(null);
const timeoutRef = useRef<any>(null);
const setResourceStatus = usePublishStore((state) => state.setResourceStatus);
const getResourceStatus = usePublishStore((state) => state.getResourceStatus);
const statusRef = useRef<ResourceStatus | null>(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,
]
);
};