mirror of
https://github.com/Qortal/qapp-core.git
synced 2025-06-15 09:51:21 +00:00
fixes
This commit is contained in:
parent
5e05ecff28
commit
f83ce67072
@ -66,6 +66,7 @@ interface BaseProps {
|
|||||||
resourceCacheDuration?: number
|
resourceCacheDuration?: number
|
||||||
disablePagination?: boolean
|
disablePagination?: boolean
|
||||||
disableScrollTracker?: boolean
|
disableScrollTracker?: boolean
|
||||||
|
retryAttempts: number
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Restrict `direction` only when `disableVirtualization = false`
|
// ✅ Restrict `direction` only when `disableVirtualization = false`
|
||||||
@ -99,9 +100,10 @@ export const MemorizedComponent = ({
|
|||||||
resourceCacheDuration,
|
resourceCacheDuration,
|
||||||
disablePagination,
|
disablePagination,
|
||||||
disableScrollTracker,
|
disableScrollTracker,
|
||||||
entityParams
|
entityParams,
|
||||||
|
retryAttempts = 2
|
||||||
}: PropsResourceListDisplay) => {
|
}: PropsResourceListDisplay) => {
|
||||||
const { fetchResources } = useResources();
|
const { fetchResources } = useResources(retryAttempts);
|
||||||
const { filterOutDeletedResources } = useCacheStore();
|
const { filterOutDeletedResources } = useCacheStore();
|
||||||
const {identifierOperations} = useGlobal()
|
const {identifierOperations} = useGlobal()
|
||||||
const deletedResources = useCacheStore().deletedResources
|
const deletedResources = useCacheStore().deletedResources
|
||||||
|
106
src/hooks/usePublish.tsx
Normal file
106
src/hooks/usePublish.tsx
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
import { usePublishStore } from "../state/publishes";
|
||||||
|
import { QortalGetMetadata } from "../types/interfaces/resources";
|
||||||
|
import { base64ToObject, retryTransaction } from "../utils/publish";
|
||||||
|
|
||||||
|
|
||||||
|
export const usePublish = (
|
||||||
|
maxFetchTries: number = 3,
|
||||||
|
returnType: "PUBLIC_JSON" = "PUBLIC_JSON",
|
||||||
|
metadata?: QortalGetMetadata
|
||||||
|
) => {
|
||||||
|
const hasFetched = useRef(false);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
const publish = usePublishStore().getPublish(metadata || null);
|
||||||
|
const setPublish = usePublishStore().setPublish;
|
||||||
|
const [hasResource, setHasResource] = useState<boolean | null>(null)
|
||||||
|
const fetchRawData = useCallback(async (item: QortalGetMetadata) => {
|
||||||
|
const url = `/arbitrary/${item?.service}/${item?.name}/${item?.identifier}?encoding=base64`;
|
||||||
|
const res = await fetch(url);
|
||||||
|
const data = await res.text();
|
||||||
|
return base64ToObject(data);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const fetchPublish = useCallback(
|
||||||
|
async (metadataProp: QortalGetMetadata, returnTypeProp: "PUBLIC_JSON" = "PUBLIC_JSON") => {
|
||||||
|
let resourceExists = null;
|
||||||
|
let resource = null;
|
||||||
|
let error = null;
|
||||||
|
try {
|
||||||
|
if (metadata) {
|
||||||
|
setIsLoading(true);
|
||||||
|
}
|
||||||
|
const url = `/arbitrary/resources/search?mode=ALL&service=${metadataProp?.service}&limit=1&includemetadata=true&reverse=true&excludeblocked=true&name=${metadataProp?.name}&exactmatchnames=true&offset=0&identifier=${metadataProp?.identifier}`;
|
||||||
|
const responseMetadata = await fetch(url, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!responseMetadata?.ok) return false;
|
||||||
|
const resMetadata = await responseMetadata.json();
|
||||||
|
if (resMetadata?.length === 0) {
|
||||||
|
resourceExists = false;
|
||||||
|
setHasResource(false)
|
||||||
|
} else {
|
||||||
|
resourceExists = true
|
||||||
|
setHasResource(true)
|
||||||
|
const response = await retryTransaction(
|
||||||
|
fetchRawData,
|
||||||
|
[metadataProp],
|
||||||
|
true,
|
||||||
|
maxFetchTries
|
||||||
|
);
|
||||||
|
const fullData = {
|
||||||
|
qortalMetadata: resMetadata[0],
|
||||||
|
data: response,
|
||||||
|
};
|
||||||
|
if (metadata) {
|
||||||
|
setPublish(resMetadata[0], fullData);
|
||||||
|
}
|
||||||
|
resource = {
|
||||||
|
qortalMetadata: resMetadata[0],
|
||||||
|
data: response,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
setError(error?.message);
|
||||||
|
if (!metadata) {
|
||||||
|
error = error?.message;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (metadata) {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
resourceExists,
|
||||||
|
resource,
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[metadata]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (hasFetched.current) return;
|
||||||
|
if (metadata?.identifier && metadata?.name && metadata?.service) {
|
||||||
|
hasFetched.current = true;
|
||||||
|
fetchPublish(metadata, returnType);
|
||||||
|
}
|
||||||
|
}, [metadata, returnType]);
|
||||||
|
|
||||||
|
if (!metadata)
|
||||||
|
return {
|
||||||
|
fetchPublish,
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
resource: publish || null,
|
||||||
|
hasResource,
|
||||||
|
refetch: fetchPublish,
|
||||||
|
fetchPublish,
|
||||||
|
};
|
||||||
|
};
|
@ -6,17 +6,18 @@ import {
|
|||||||
import { ListItem, useCacheStore } from "../state/cache";
|
import { ListItem, useCacheStore } from "../state/cache";
|
||||||
import { RequestQueueWithPromise } from "../utils/queue";
|
import { RequestQueueWithPromise } from "../utils/queue";
|
||||||
import { base64ToUint8Array, uint8ArrayToObject } from "../utils/base64";
|
import { base64ToUint8Array, uint8ArrayToObject } from "../utils/base64";
|
||||||
|
import { retryTransaction } from "../utils/publish";
|
||||||
|
|
||||||
export const requestQueueProductPublishes = new RequestQueueWithPromise(20);
|
export const requestQueueProductPublishes = new RequestQueueWithPromise(20);
|
||||||
export const requestQueueProductPublishesBackup = new RequestQueueWithPromise(
|
export const requestQueueProductPublishesBackup = new RequestQueueWithPromise(
|
||||||
5
|
10
|
||||||
);
|
);
|
||||||
|
|
||||||
interface TemporaryResource {
|
export interface TemporaryResource {
|
||||||
qortalMetadata: QortalMetadata;
|
qortalMetadata: QortalMetadata;
|
||||||
data: any;
|
data: any;
|
||||||
}
|
}
|
||||||
export const useResources = () => {
|
export const useResources = (retryAttempts: number = 2) => {
|
||||||
const {
|
const {
|
||||||
setSearchCache,
|
setSearchCache,
|
||||||
getSearchCache,
|
getSearchCache,
|
||||||
@ -120,11 +121,12 @@ export const useResources = () => {
|
|||||||
await new Promise((res) => {
|
await new Promise((res) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
res(null);
|
res(null);
|
||||||
}, 15000);
|
}, 10000);
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
res = await requestQueueProductPublishesBackup.enqueue(
|
const fetchRetries = async ()=> {
|
||||||
|
return await requestQueueProductPublishesBackup.enqueue(
|
||||||
(): Promise<string> => {
|
(): Promise<string> => {
|
||||||
return getArbitraryResource(
|
return getArbitraryResource(
|
||||||
`/arbitrary/${item?.service}/${item?.name}/${item?.identifier}?encoding=base64`,
|
`/arbitrary/${item?.service}/${item?.name}/${item?.identifier}?encoding=base64`,
|
||||||
@ -132,6 +134,13 @@ export const useResources = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
res = await retryTransaction(
|
||||||
|
fetchRetries,
|
||||||
|
[],
|
||||||
|
true,
|
||||||
|
retryAttempts
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setResourceCache(
|
setResourceCache(
|
||||||
`${item?.service}-${item?.name}-${item?.identifier}`,
|
`${item?.service}-${item?.name}-${item?.identifier}`,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
import './index.css'
|
import './index.css'
|
||||||
export { GlobalProvider, useGlobal } from "./context/GlobalProvider";
|
export { GlobalProvider, useGlobal } from "./context/GlobalProvider";
|
||||||
|
export {usePublish} from "./hooks/usePublish"
|
||||||
export {ResourceListDisplay} from "./components/ResourceList/ResourceListDisplay"
|
export {ResourceListDisplay} from "./components/ResourceList/ResourceListDisplay"
|
35
src/state/publishes.ts
Normal file
35
src/state/publishes.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { create } from "zustand";
|
||||||
|
import { IdentifierBuilder } from "../utils/encryption";
|
||||||
|
import { QortalGetMetadata } from "../types/interfaces/resources";
|
||||||
|
import { TemporaryResource } from "../hooks/useResources";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
interface PublishState {
|
||||||
|
publishes: Record<string, TemporaryResource> ;
|
||||||
|
|
||||||
|
getPublish: (qortalGetMetadata: QortalGetMetadata | null) => TemporaryResource | null;
|
||||||
|
setPublish: (qortalGetMetadata: QortalGetMetadata, data: TemporaryResource) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Typed Zustand Store
|
||||||
|
export const usePublishStore = create<PublishState>((set, get) => ({
|
||||||
|
publishes: {},
|
||||||
|
getPublish: (qortalGetMetadata) => {
|
||||||
|
if(qortalGetMetadata === null) return null
|
||||||
|
const cache = get().publishes[`${qortalGetMetadata.service}-${qortalGetMetadata.name}-${qortalGetMetadata.identifier}`];
|
||||||
|
if (cache) {
|
||||||
|
return cache
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
setPublish: (qortalGetMetadata, data) =>
|
||||||
|
set((state) => {
|
||||||
|
return {
|
||||||
|
publishes: {
|
||||||
|
...state.publishes,
|
||||||
|
[`${qortalGetMetadata.service}-${qortalGetMetadata.name}-${qortalGetMetadata.identifier}`]: data,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
}));
|
@ -70,6 +70,12 @@ export interface QortalMetadata {
|
|||||||
updated?: number
|
updated?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface QortalGetMetadata {
|
||||||
|
name: string
|
||||||
|
identifier: string
|
||||||
|
service: Service
|
||||||
|
}
|
||||||
|
|
||||||
export interface QortalSearchParams {
|
export interface QortalSearchParams {
|
||||||
identifier: string;
|
identifier: string;
|
||||||
service: Service;
|
service: Service;
|
||||||
|
59
src/utils/publish.ts
Normal file
59
src/utils/publish.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
const MAX_RETRIES = 3; // Define your max retries constant
|
||||||
|
|
||||||
|
export async function retryTransaction<T>(
|
||||||
|
fn: (...args: any[]) => Promise<T>, // Function that returns a promise
|
||||||
|
args: any[], // Arguments for the function
|
||||||
|
throwError: boolean,
|
||||||
|
retries: number = MAX_RETRIES
|
||||||
|
): Promise<T | null> {
|
||||||
|
let attempt = 0;
|
||||||
|
|
||||||
|
while (attempt < retries) {
|
||||||
|
try {
|
||||||
|
return await fn(...args); // Attempt to execute the function
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(`Attempt ${attempt + 1} failed: ${error.message}`);
|
||||||
|
attempt++;
|
||||||
|
|
||||||
|
if (attempt === retries) {
|
||||||
|
console.error("Max retries reached. Skipping transaction.");
|
||||||
|
if (throwError) {
|
||||||
|
throw new Error(error?.message || "Unable to process transaction");
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait before retrying
|
||||||
|
await new Promise((res) => setTimeout(res, 10000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null; // This should never be reached, but added for type safety
|
||||||
|
}
|
||||||
|
|
||||||
|
export function base64ToUint8Array(base64: string) {
|
||||||
|
const binaryString = atob(base64)
|
||||||
|
const len = binaryString.length
|
||||||
|
const bytes = new Uint8Array(len)
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
bytes[i] = binaryString.charCodeAt(i)
|
||||||
|
}
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
export function uint8ArrayToObject(uint8Array: Uint8Array) {
|
||||||
|
// Decode the byte array using TextDecoder
|
||||||
|
const decoder = new TextDecoder()
|
||||||
|
const jsonString = decoder.decode(uint8Array)
|
||||||
|
// Convert the JSON string back into an object
|
||||||
|
return JSON.parse(jsonString)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function base64ToObject(base64: string){
|
||||||
|
const toUint = base64ToUint8Array(base64);
|
||||||
|
const toObject = uint8ArrayToObject(toUint);
|
||||||
|
|
||||||
|
return toObject
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user