add resources and update resources

This commit is contained in:
PhilReact 2025-03-14 02:51:41 +02:00
parent ae5111618f
commit e8866f1585
3 changed files with 269 additions and 192 deletions

View File

@ -1,10 +1,12 @@
import React, { createContext, useContext, useMemo } from "react";
import { useAuth, UseAuthProps } from "../hooks/useAuth";
import { useResources } from "../hooks/useResources";
// ✅ Define Global Context Type
interface GlobalContextType {
auth: ReturnType<typeof useAuth>;
resources: ReturnType<typeof useResources>;
}
// ✅ Define Config Type for Hook Options
@ -23,10 +25,10 @@ const GlobalContext = createContext<GlobalContextType | null>(null);
export const GlobalProvider = ({ children, config }: GlobalProviderProps) => {
// ✅ Call hooks and pass in options dynamically
const auth = useAuth(config?.auth || {});
const resources = useResources()
// ✅ Merge all hooks into a single `contextValue`
const contextValue = useMemo(() => ({ auth }), [auth]);
const contextValue = useMemo(() => ({ auth, resources }), [auth, resources]);
return (
<GlobalContext.Provider value={contextValue}>

View File

@ -1,21 +1,36 @@
import React, { useCallback } from 'react'
import { QortalMetadata, QortalSearchParams } from '../types/interfaces/resources';
import { useCacheStore } from '../state/cache';
import { RequestQueueWithPromise } from '../utils/queue';
import { base64ToUint8Array, uint8ArrayToObject } from '../utils/base64';
import React, { useCallback } from "react";
import {
QortalMetadata,
QortalSearchParams,
} from "../types/interfaces/resources";
import { useCacheStore } from "../state/cache";
import { RequestQueueWithPromise } from "../utils/queue";
import { base64ToUint8Array, uint8ArrayToObject } from "../utils/base64";
export const requestQueueProductPublishes = new RequestQueueWithPromise(20);
export const requestQueueProductPublishesBackup = new RequestQueueWithPromise(5);
export const requestQueueProductPublishesBackup = new RequestQueueWithPromise(
5
);
interface TemporaryResource {
qortalMetadata: QortalMetadata
data: any
qortalMetadata: QortalMetadata;
data: any;
}
export const useResources = () => {
const { setSearchCache, getSearchCache, getResourceCache, setResourceCache, addTemporaryResource, getTemporaryResources } = useCacheStore();
const {
setSearchCache,
getSearchCache,
getResourceCache,
setResourceCache,
addTemporaryResource
} = useCacheStore();
const requestControllers = new Map<string, AbortController>();
const getArbitraryResource = async (url: string, key: string): Promise<string> => {
const getArbitraryResource = async (
url: string,
key: string
): Promise<string> => {
// ✅ Create or reuse an existing controller
let controller = requestControllers.get(key);
if (!controller) {
@ -46,52 +61,74 @@ export const useResources = () => {
requestControllers.clear();
};
const fetchIndividualPublish = useCallback(
async (item: QortalMetadata) => {
try {
const key = `${item?.service}-${item?.name}-${item?.identifier}`;
const cachedProduct = getResourceCache(`${item?.service}-${item?.name}-${item?.identifier}`);
const cachedProduct = getResourceCache(
`${item?.service}-${item?.name}-${item?.identifier}`
);
if (cachedProduct) return;
setResourceCache(`${item?.service}-${item?.name}-${item?.identifier}`, null);
let hasFailedToDownload = false
let res: string | undefined = undefined
setResourceCache(
`${item?.service}-${item?.name}-${item?.identifier}`,
null
);
let hasFailedToDownload = false;
let res: string | undefined = undefined;
try {
res = await requestQueueProductPublishes.enqueue((): Promise<string> => {
return getArbitraryResource(`/arbitrary/${item?.service}/${item?.name}/${item?.identifier}?encoding=base64`, key)
});
} catch (error) {
hasFailedToDownload = true
res = await requestQueueProductPublishes.enqueue(
(): Promise<string> => {
return getArbitraryResource(
`/arbitrary/${item?.service}/${item?.name}/${item?.identifier}?encoding=base64`,
key
);
}
if(res === 'canceled') return false
);
} catch (error) {
hasFailedToDownload = true;
}
if (res === "canceled") return false;
if (hasFailedToDownload) {
await new Promise((res) => {
setTimeout(() => {
res(null)
}, 15000)
})
res(null);
}, 15000);
});
try {
res = await requestQueueProductPublishesBackup.enqueue((): Promise<string> => {
return getArbitraryResource(`/arbitrary/${item?.service}/${item?.name}/${item?.identifier}?encoding=base64`, key)
});
res = await requestQueueProductPublishesBackup.enqueue(
(): Promise<string> => {
return getArbitraryResource(
`/arbitrary/${item?.service}/${item?.name}/${item?.identifier}?encoding=base64`,
key
);
}
);
} catch (error) {
setResourceCache(`${item?.service}-${item?.name}-${item?.identifier}`, false);
return false
setResourceCache(
`${item?.service}-${item?.name}-${item?.identifier}`,
false
);
return false;
}
}
if (res) {
const toUint = base64ToUint8Array(res);
const toObject = uint8ArrayToObject(toUint);
const fullDataObject = { data: {...toObject}, qortalMetadata: item };
setResourceCache(`${item?.service}-${item?.name}-${item?.identifier}`, fullDataObject);
return fullDataObject
const fullDataObject = {
data: { ...toObject },
qortalMetadata: item,
};
setResourceCache(
`${item?.service}-${item?.name}-${item?.identifier}`,
fullDataObject
);
return fullDataObject;
}
} catch (error) {
return false
return false;
}
},
[getResourceCache, setResourceCache]
@ -107,14 +144,18 @@ export const useResources = () => {
);
const fetchResources = useCallback(
async (params: QortalSearchParams, listName: string, cancelRequests?: boolean): Promise<QortalMetadata[]> => {
async (
params: QortalSearchParams,
listName: string,
cancelRequests?: boolean
): Promise<QortalMetadata[]> => {
if (cancelRequests) {
cancelAllRequests()
cancelAllRequests();
await new Promise((res) => {
setTimeout(() => {
res(null)
res(null);
}, 250);
})
});
}
const cacheKey = generateCacheKey(params);
const searchCache = getSearchCache(listName, cacheKey);
@ -125,12 +166,12 @@ export const useResources = () => {
} else {
const response = await qortalRequest({
action: "SEARCH_QDN_RESOURCES",
mode: 'ALL',
mode: "ALL",
limit: 20,
...params,
});
if (!response) throw new Error("Unable to fetch resources");
responseData = response
responseData = response;
}
setSearchCache(listName, cacheKey, responseData);
fetchDataFromResults(responseData);
@ -140,18 +181,41 @@ export const useResources = () => {
[getSearchCache, setSearchCache, fetchDataFromResults]
);
const addNewResources = useCallback(
(listName: string, resources: TemporaryResource[]) => {
addTemporaryResource(
listName,
resources.map((item) => item.qortalMetadata)
);
resources.forEach((temporaryResource) => {
setResourceCache(
`${temporaryResource?.qortalMetadata?.service}-${temporaryResource?.qortalMetadata?.name}-${temporaryResource?.qortalMetadata?.identifier}`,
temporaryResource.data
);
});
},
[]
);
const updateNewResources = useCallback(
(resources: TemporaryResource[]) => {
const addNewResources = useCallback((listName:string, temporaryResources: TemporaryResource[])=> {
addTemporaryResource(listName, temporaryResources.map((item)=> item.qortalMetadata))
}, [])
resources.forEach((temporaryResource) => {
setResourceCache(
`${temporaryResource?.qortalMetadata?.service}-${temporaryResource?.qortalMetadata?.name}-${temporaryResource?.qortalMetadata?.identifier}`,
temporaryResource.data
);
});
},
[]
);
return {
fetchResources,
fetchIndividualPublish,
addNewResources
}
}
addNewResources,
updateNewResources
};
};
export const generateCacheKey = (params: QortalSearchParams): string => {
const {
@ -174,7 +238,7 @@ export const generateCacheKey = (params: QortalSearchParams): string => {
limit,
offset,
reverse,
mode
mode,
} = params;
const keyParts = [
@ -204,4 +268,3 @@ export const generateCacheKey = (params: QortalSearchParams): string => {
return keyParts;
};

View File

@ -64,23 +64,35 @@ export class RequestQueueWithPromise<T = any> {
}
export async function retryTransaction(fn, args, throwError, retries) {
export async function retryTransaction<T>(
fn: (...args: any[]) => Promise<T>,
args: any[],
throwError: boolean,
retries: number
): Promise<T | null> {
let attempt = 0;
while (attempt < retries) {
try {
return await fn(...args);
} catch (error) {
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`Attempt ${attempt + 1} failed: ${error.message}`);
} else {
console.error(`Attempt ${attempt + 1} failed: Unknown error`);
}
attempt++;
if (attempt === retries) {
console.error("Max retries reached. Skipping transaction.");
if (throwError) {
throw new Error(error?.message || "Unable to process transaction")
throw new Error(error instanceof Error ? error.message : "Unable to process transaction");
} else {
return null
return null;
}
}
await new Promise(res => setTimeout(res, 10000));
await new Promise((res) => setTimeout(res, 10000));
}
}
return null;
}