added useResource status hook

This commit is contained in:
PhilReact 2025-04-01 20:05:07 +03:00
parent 069eca59d0
commit 6fbffcbb50
3 changed files with 231 additions and 2 deletions

View File

@ -0,0 +1,187 @@
import React, { useCallback, useEffect, useMemo, useRef } from "react";
import { usePublishStore } from "../state/publishes";
import { QortalGetMetadata } from "../types/interfaces/resources";
interface PropsUseResourceStatus {
resource: QortalGetMetadata;
retryAttempts?: number;
}
export const useResourceStatus = ({
resource,
retryAttempts = 15,
}: PropsUseResourceStatus) => {
const resourceId = `${resource.service}-${resource.name}-${resource.identifier}`;
const status = usePublishStore((state)=> state.getResourceStatus(resourceId)) || null
const intervalRef = useRef<null | number>(null)
const timeoutRef = useRef<null | number>(null)
const setResourceStatus = usePublishStore((state) => state.setResourceStatus);
const downloadResource = useCallback(
({ service, name, identifier }: QortalGetMetadata, build?: boolean) => {
try {
setResourceStatus(
{ service, name, identifier },
{
"status": "SEARCHING",
"localChunkCount": 0,
"totalChunkCount": 0,
"percentLoaded": 0
}
);
let isCalling = false;
let percentLoaded = 0;
let timer = 24;
let tries = 0;
let calledFirstTime = false;
const callFunction = async () => {
if (isCalling) return;
isCalling = true;
let res;
if (!build) {
const urlFirstTime = `/arbitrary/resource/status/${service}/${name}/${identifier}`;
const resCall = await fetch(urlFirstTime, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
res = await resCall.json();
setResourceStatus(
{ service, name, identifier },
{
...res
}
);
if (tries > retryAttempts) {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
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 = 24;
}
if (timer < 0) {
timer = 24;
isCalling = true;
setResourceStatus(
{ service, name, identifier },
{
...res,
status: "REFETCHING",
}
);
timeoutRef.current = setTimeout(() => {
isCalling = false;
downloadResource({ name, service, identifier }, true);
}, 25000);
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);
}
setResourceStatus({service, name, identifier}, {
...res,
})
}
if (res?.status === "DOWNLOADED") {
const url = `/arbitrary/resource/status/${service}/${name}/${identifier}?build=true`;
const resCall = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
res = await resCall.json();
}
};
callFunction();
intervalRef.current = setInterval(async () => {
callFunction();
}, 5000);
} catch (error) {
console.error("Error during resource fetch:", error);
}
},
[retryAttempts]
);
useEffect(() => {
if (resource?.identifier && resource?.name && resource?.service) {
downloadResource({
service: resource?.service,
name: resource?.name,
identifier: resource?.identifier,
});
}
return ()=> {
if(intervalRef.current){
clearInterval(intervalRef.current)
}
if(timeoutRef.current){
clearTimeout(timeoutRef.current)
}
}
}, [
resource?.identifier,
resource?.name,
resource?.service,
downloadResource,
]);
return !status ? null : {...(status || {}), isReady: status?.status === 'READY'};
};

View File

@ -1,3 +1,4 @@
export { useResourceStatus } from './hooks/useResourceStatus';
import './index.css'
export { EnumCollisionStrength } from './utils/encryption';
export { showLoading, dismissToast, showError, showSuccess } from './utils/toast';

View File

@ -7,16 +7,41 @@ interface PublishCache {
expiry: number;
}
export type Status =
| 'PUBLISHED'
| 'NOT_PUBLISHED'
| 'DOWNLOADING'
| 'DOWNLOADED'
| 'BUILDING'
| 'READY'
| 'MISSING_DATA'
| 'BUILD_FAILED'
| 'UNSUPPORTED'
| 'BLOCKED'
| 'FAILED_TO_DOWNLOAD'
| 'REFETCHING'
| 'SEARCHING'
export interface ResourceStatus {
status: Status
localChunkCount: number
totalChunkCount: number
percentLoaded: number
}
interface PublishState {
publishes: Record<string, PublishCache>;
resourceStatus: Record<string, ResourceStatus | null>;
setResourceStatus: (qortalGetMetadata: QortalGetMetadata, data: ResourceStatus | null) => void;
getPublish: (qortalGetMetadata: QortalGetMetadata | null, ignoreExpire?: boolean) => Resource | null;
getResourceStatus: (resourceId: string) => ResourceStatus | null;
setPublish: (qortalGetMetadata: QortalGetMetadata, data: Resource | null, customExpiry?: number) => void;
clearExpiredPublishes: () => void;
publishExpiryDuration: number; // Default expiry duration
}
export const usePublishStore = create<PublishState>((set, get) => ({
resourceStatus: {},
publishes: {},
publishExpiryDuration: 5 * 60 * 1000, // Default expiry: 5 minutes
@ -52,7 +77,23 @@ export const usePublishStore = create<PublishState>((set, get) => ({
},
}));
},
setResourceStatus: (qortalGetMetadata, data) => {
const id = `${qortalGetMetadata.service}-${qortalGetMetadata.name}-${qortalGetMetadata.identifier}`;
const existingData = get().resourceStatus[id] || {};
set((state) => ({
resourceStatus: {
...state.resourceStatus,
[id]: !data ? null : {
...existingData,
...data
},
},
}));
},
getResourceStatus: (resourceId) => {
const status = get().resourceStatus[resourceId];
return status || null;
},
clearExpiredPublishes: () => {
set((state) => {
const now = Date.now();