mirror of
https://github.com/Qortal/qapp-core.git
synced 2025-06-15 18:01:21 +00:00
updated usePublish
This commit is contained in:
parent
f83ce67072
commit
770080b942
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "qapp-core",
|
"name": "qapp-core",
|
||||||
"version": "1.0.7",
|
"version": "1.0.9",
|
||||||
"description": "Qortal's core React library with global state, UI components, and utilities",
|
"description": "Qortal's core React library with global state, UI components, and utilities",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"module": "dist/index.mjs",
|
"module": "dist/index.mjs",
|
||||||
|
@ -103,9 +103,8 @@ export const MemorizedComponent = ({
|
|||||||
entityParams,
|
entityParams,
|
||||||
retryAttempts = 2
|
retryAttempts = 2
|
||||||
}: PropsResourceListDisplay) => {
|
}: PropsResourceListDisplay) => {
|
||||||
const { fetchResources } = useResources(retryAttempts);
|
|
||||||
const { filterOutDeletedResources } = useCacheStore();
|
const { filterOutDeletedResources } = useCacheStore();
|
||||||
const {identifierOperations} = useGlobal()
|
const {identifierOperations, lists} = useGlobal()
|
||||||
const deletedResources = useCacheStore().deletedResources
|
const deletedResources = useCacheStore().deletedResources
|
||||||
const memoizedParams = useMemo(() => JSON.stringify(search), [search]);
|
const memoizedParams = useMemo(() => JSON.stringify(search), [search]);
|
||||||
const addList = useListStore().addList
|
const addList = useListStore().addList
|
||||||
@ -119,9 +118,7 @@ export const MemorizedComponent = ({
|
|||||||
const [isLoadingMore, setIsLoadingMore] = useState(false)
|
const [isLoadingMore, setIsLoadingMore] = useState(false)
|
||||||
const initialized = useRef(false)
|
const initialized = useRef(false)
|
||||||
const [generatedIdentifier, setGeneratedIdentifier] = useState("")
|
const [generatedIdentifier, setGeneratedIdentifier] = useState("")
|
||||||
|
const prevGeneratedIdentifierRef = useRef('')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const stringifiedEntityParams = useMemo(()=> {
|
const stringifiedEntityParams = useMemo(()=> {
|
||||||
@ -157,6 +154,7 @@ export const MemorizedComponent = ({
|
|||||||
try {
|
try {
|
||||||
|
|
||||||
if(!generatedIdentifier) return
|
if(!generatedIdentifier) return
|
||||||
|
|
||||||
await new Promise((res)=> {
|
await new Promise((res)=> {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
res(null)
|
res(null)
|
||||||
@ -165,7 +163,7 @@ export const MemorizedComponent = ({
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const parsedParams = {...(JSON.parse(memoizedParams))};
|
const parsedParams = {...(JSON.parse(memoizedParams))};
|
||||||
parsedParams.identifier = generatedIdentifier
|
parsedParams.identifier = generatedIdentifier
|
||||||
const responseData = await fetchResources(parsedParams, listName, true); // Awaiting the async function
|
const responseData = await lists.fetchResources(parsedParams, listName, true); // Awaiting the async function
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -176,16 +174,18 @@ export const MemorizedComponent = ({
|
|||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}, [memoizedParams, fetchResources, generatedIdentifier]); // Added dependencies for re-fetching
|
}, [memoizedParams, lists.fetchResources, generatedIdentifier]); // Added dependencies for re-fetching
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(initialized.current || !generatedIdentifier) return
|
if(!generatedIdentifier) return
|
||||||
initialized.current = true
|
|
||||||
if(!isListExpired) {
|
if(!isListExpired && !initialized.current) {
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
return
|
initialized.current = true
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
|
||||||
sessionStorage.removeItem(`scroll-position-${listName}`);
|
sessionStorage.removeItem(`scroll-position-${listName}`);
|
||||||
|
prevGeneratedIdentifierRef.current = generatedIdentifier
|
||||||
getResourceList();
|
getResourceList();
|
||||||
}, [getResourceList, isListExpired, generatedIdentifier]); // Runs when dependencies change
|
}, [getResourceList, isListExpired, generatedIdentifier]); // Runs when dependencies change
|
||||||
|
|
||||||
@ -221,7 +221,7 @@ export const MemorizedComponent = ({
|
|||||||
if(displayLimit){
|
if(displayLimit){
|
||||||
parsedParams.limit = displayLimit
|
parsedParams.limit = displayLimit
|
||||||
}
|
}
|
||||||
const responseData = await fetchResources(parsedParams, listName); // Awaiting the async function
|
const responseData = await lists.fetchResources(parsedParams, listName); // Awaiting the async function
|
||||||
addItems(listName, responseData || [])
|
addItems(listName, responseData || [])
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch resources:", error);
|
console.error("Failed to fetch resources:", error);
|
||||||
|
@ -5,17 +5,19 @@ import { useAppInfo } from "../hooks/useAppInfo";
|
|||||||
import { IdentifierBuilder } from "../utils/encryption";
|
import { IdentifierBuilder } from "../utils/encryption";
|
||||||
import { useIdentifiers } from "../hooks/useIdentifiers";
|
import { useIdentifiers } from "../hooks/useIdentifiers";
|
||||||
import { objectToBase64 } from "../utils/base64";
|
import { objectToBase64 } from "../utils/base64";
|
||||||
|
import { base64ToObject } from "../utils/publish";
|
||||||
|
|
||||||
|
|
||||||
const utils = {
|
const utils = {
|
||||||
objectToBase64
|
objectToBase64,
|
||||||
|
base64ToObject
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ✅ Define Global Context Type
|
// ✅ Define Global Context Type
|
||||||
interface GlobalContextType {
|
interface GlobalContextType {
|
||||||
auth: ReturnType<typeof useAuth>;
|
auth: ReturnType<typeof useAuth>;
|
||||||
resources: ReturnType<typeof useResources>;
|
lists: ReturnType<typeof useResources>;
|
||||||
appInfo: ReturnType<typeof useAppInfo>;
|
appInfo: ReturnType<typeof useAppInfo>;
|
||||||
identifierOperations: ReturnType<typeof useIdentifiers>
|
identifierOperations: ReturnType<typeof useIdentifiers>
|
||||||
utils: typeof utils
|
utils: typeof utils
|
||||||
@ -44,11 +46,11 @@ export const GlobalProvider = ({ children, config, identifierBuilder }: GlobalPr
|
|||||||
// ✅ Call hooks and pass in options dynamically
|
// ✅ Call hooks and pass in options dynamically
|
||||||
const auth = useAuth(config?.auth || {});
|
const auth = useAuth(config?.auth || {});
|
||||||
const appInfo = useAppInfo(config?.appName, config?.publicSalt)
|
const appInfo = useAppInfo(config?.appName, config?.publicSalt)
|
||||||
const resources = useResources()
|
const lists = useResources()
|
||||||
const identifierOperations = useIdentifiers(identifierBuilder, config?.publicSalt)
|
const identifierOperations = useIdentifiers(identifierBuilder, config?.publicSalt)
|
||||||
|
|
||||||
// ✅ Merge all hooks into a single `contextValue`
|
// ✅ Merge all hooks into a single `contextValue`
|
||||||
const contextValue = useMemo(() => ({ auth, resources, appInfo, identifierOperations, utils }), [auth, resources, appInfo, identifierOperations]);
|
const contextValue = useMemo(() => ({ auth, lists, appInfo, identifierOperations, utils }), [auth, lists, appInfo, identifierOperations]);
|
||||||
return (
|
return (
|
||||||
<GlobalContext.Provider value={contextValue}>
|
<GlobalContext.Provider value={contextValue}>
|
||||||
{children}
|
{children}
|
||||||
|
@ -10,7 +10,7 @@ export const useAppInfo = (appName?: string, publicSalt?: string) => {
|
|||||||
|
|
||||||
|
|
||||||
const handleAppInfoSetup = useCallback(async (name: string, salt: string)=> {
|
const handleAppInfoSetup = useCallback(async (name: string, salt: string)=> {
|
||||||
const appNameHashed = await hashWord(name, EnumCollisionStrength.LOW, salt)
|
const appNameHashed = await hashWord(name, EnumCollisionStrength.HIGH, salt)
|
||||||
setAppState({
|
setAppState({
|
||||||
appName: name,
|
appName: name,
|
||||||
publicSalt: salt,
|
publicSalt: salt,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useAuthStore } from "../state/auth";
|
import { useAuthStore } from "../state/auth";
|
||||||
import { useAppStore } from "../state/app";
|
import { useAppStore } from "../state/app";
|
||||||
import { buildIdentifier, buildSearchPrefix, IdentifierBuilder } from "../utils/encryption";
|
import { buildIdentifier, buildSearchPrefix, EnumCollisionStrength, hashWord, IdentifierBuilder } from "../utils/encryption";
|
||||||
|
|
||||||
|
|
||||||
export const useIdentifiers = (builder?: IdentifierBuilder, publicSalt?: string) => {
|
export const useIdentifiers = (builder?: IdentifierBuilder, publicSalt?: string) => {
|
||||||
@ -25,7 +25,11 @@ export const useIdentifiers = (builder?: IdentifierBuilder, publicSalt?: string)
|
|||||||
return buildSearchPrefix(appName, publicSalt, entityType, parentId, identifierBuilder)
|
return buildSearchPrefix(appName, publicSalt, entityType, parentId, identifierBuilder)
|
||||||
}, [appName, publicSalt, identifierBuilder])
|
}, [appName, publicSalt, identifierBuilder])
|
||||||
|
|
||||||
|
const createSingleIdentifier = useCallback(async ( partialIdentifier: string)=> {
|
||||||
|
if(!partialIdentifier || !appName || !publicSalt) return null
|
||||||
|
const appNameHashed = await hashWord(appName, EnumCollisionStrength.HIGH, publicSalt)
|
||||||
|
return appNameHashed + '_' + partialIdentifier
|
||||||
|
}, [appName, publicSalt])
|
||||||
|
|
||||||
|
|
||||||
useEffect(()=> {
|
useEffect(()=> {
|
||||||
@ -35,6 +39,7 @@ export const useIdentifiers = (builder?: IdentifierBuilder, publicSalt?: string)
|
|||||||
}, [stringifiedBuilder])
|
}, [stringifiedBuilder])
|
||||||
return {
|
return {
|
||||||
buildIdentifier: buildIdentifierFunc,
|
buildIdentifier: buildIdentifierFunc,
|
||||||
buildSearchPrefix: buildSearchPrefixFunc
|
buildSearchPrefix: buildSearchPrefixFunc,
|
||||||
|
createSingleIdentifier
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,20 +1,31 @@
|
|||||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { usePublishStore } from "../state/publishes";
|
import { usePublishStore } from "../state/publishes";
|
||||||
import { QortalGetMetadata } from "../types/interfaces/resources";
|
import { QortalGetMetadata, QortalMetadata } from "../types/interfaces/resources";
|
||||||
import { base64ToObject, retryTransaction } from "../utils/publish";
|
import { base64ToObject, retryTransaction } from "../utils/publish";
|
||||||
|
import { useGlobal } from "../context/GlobalProvider";
|
||||||
|
|
||||||
|
const STORAGE_EXPIRY_DURATION = 5 * 60 * 1000;
|
||||||
|
interface StoredPublish {
|
||||||
|
qortalMetadata: QortalMetadata;
|
||||||
|
data: any;
|
||||||
|
timestamp: number;
|
||||||
|
}
|
||||||
export const usePublish = (
|
export const usePublish = (
|
||||||
maxFetchTries: number = 3,
|
maxFetchTries: number = 3,
|
||||||
returnType: "PUBLIC_JSON" = "PUBLIC_JSON",
|
returnType: "PUBLIC_JSON" = "PUBLIC_JSON",
|
||||||
metadata?: QortalGetMetadata
|
metadata?: QortalGetMetadata
|
||||||
) => {
|
) => {
|
||||||
|
const {auth, appInfo} = useGlobal()
|
||||||
|
const username = auth?.name
|
||||||
|
const appNameHashed = appInfo?.appNameHashed
|
||||||
const hasFetched = useRef(false);
|
const hasFetched = useRef(false);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState<null | string>(null);
|
||||||
const publish = usePublishStore().getPublish(metadata || null);
|
const publish = usePublishStore().getPublish(metadata || null);
|
||||||
const setPublish = usePublishStore().setPublish;
|
const setPublish = usePublishStore().setPublish;
|
||||||
const [hasResource, setHasResource] = useState<boolean | null>(null)
|
const getPublish = usePublishStore().getPublish;
|
||||||
|
|
||||||
|
const [hasResource, setHasResource] = useState<boolean | null>(null);
|
||||||
const fetchRawData = useCallback(async (item: QortalGetMetadata) => {
|
const fetchRawData = useCallback(async (item: QortalGetMetadata) => {
|
||||||
const url = `/arbitrary/${item?.service}/${item?.name}/${item?.identifier}?encoding=base64`;
|
const url = `/arbitrary/${item?.service}/${item?.name}/${item?.identifier}?encoding=base64`;
|
||||||
const res = await fetch(url);
|
const res = await fetch(url);
|
||||||
@ -22,8 +33,41 @@ export const usePublish = (
|
|||||||
return base64ToObject(data);
|
return base64ToObject(data);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const getStorageKey = useCallback(() => {
|
||||||
|
if (!username || !appNameHashed) return null;
|
||||||
|
return `qortal_publish_${username}_${appNameHashed}`;
|
||||||
|
}, [username, appNameHashed]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!username || !appNameHashed) return;
|
||||||
|
|
||||||
|
const storageKey = getStorageKey();
|
||||||
|
if (!storageKey) return;
|
||||||
|
|
||||||
|
const storedData: StoredPublish[] = JSON.parse(localStorage.getItem(storageKey) || "[]");
|
||||||
|
|
||||||
|
if (Array.isArray(storedData) && storedData.length > 0) {
|
||||||
|
const now = Date.now();
|
||||||
|
const validPublishes = storedData.filter((item) => now - item.timestamp < STORAGE_EXPIRY_DURATION);
|
||||||
|
|
||||||
|
// ✅ Re-populate the Zustand store only with recent publishes
|
||||||
|
validPublishes.forEach((publishData) => {
|
||||||
|
setPublish(publishData.qortalMetadata, {
|
||||||
|
qortalMetadata: publishData.qortalMetadata,
|
||||||
|
data: publishData.data
|
||||||
|
}, Date.now() - publishData.timestamp);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ✅ Re-store only valid (non-expired) publishes
|
||||||
|
localStorage.setItem(storageKey, JSON.stringify(validPublishes));
|
||||||
|
}
|
||||||
|
}, [username, appNameHashed, getStorageKey, setPublish]);
|
||||||
|
|
||||||
const fetchPublish = useCallback(
|
const fetchPublish = useCallback(
|
||||||
async (metadataProp: QortalGetMetadata, returnTypeProp: "PUBLIC_JSON" = "PUBLIC_JSON") => {
|
async (
|
||||||
|
metadataProp: QortalGetMetadata,
|
||||||
|
returnTypeProp: "PUBLIC_JSON" = "PUBLIC_JSON"
|
||||||
|
) => {
|
||||||
let resourceExists = null;
|
let resourceExists = null;
|
||||||
let resource = null;
|
let resource = null;
|
||||||
let error = null;
|
let error = null;
|
||||||
@ -31,6 +75,31 @@ export const usePublish = (
|
|||||||
if (metadata) {
|
if (metadata) {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
}
|
}
|
||||||
|
const hasCache = getPublish(metadataProp)
|
||||||
|
|
||||||
|
if(hasCache){
|
||||||
|
if(hasCache?.qortalMetadata.size === 32){
|
||||||
|
if(metadata){
|
||||||
|
setHasResource(false)
|
||||||
|
setError(null)
|
||||||
|
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
resource: null,
|
||||||
|
error: null,
|
||||||
|
resourceExists: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(metadata){
|
||||||
|
setHasResource(true)
|
||||||
|
setError(null)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
resource: hasCache,
|
||||||
|
error: null,
|
||||||
|
resourceExists: 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 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, {
|
const responseMetadata = await fetch(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@ -38,14 +107,33 @@ export const usePublish = (
|
|||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!responseMetadata?.ok) return false;
|
if (!responseMetadata?.ok) {
|
||||||
|
if (metadata) {
|
||||||
|
setError("Invalid search params");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
resourceExists,
|
||||||
|
resource,
|
||||||
|
error: "Invalid search params",
|
||||||
|
};
|
||||||
|
}
|
||||||
const resMetadata = await responseMetadata.json();
|
const resMetadata = await responseMetadata.json();
|
||||||
if (resMetadata?.length === 0) {
|
if (resMetadata?.length === 0) {
|
||||||
resourceExists = false;
|
resourceExists = false;
|
||||||
setHasResource(false)
|
if (metadata) {
|
||||||
|
setHasResource(false);
|
||||||
|
}
|
||||||
|
} else if (resMetadata[0]?.size === 32) {
|
||||||
|
resourceExists = false;
|
||||||
|
if (metadata) {
|
||||||
|
setHasResource(false);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
resourceExists = true
|
resourceExists = true;
|
||||||
setHasResource(true)
|
if (metadata) {
|
||||||
|
setHasResource(true);
|
||||||
|
}
|
||||||
|
|
||||||
const response = await retryTransaction(
|
const response = await retryTransaction(
|
||||||
fetchRawData,
|
fetchRawData,
|
||||||
[metadataProp],
|
[metadataProp],
|
||||||
@ -91,10 +179,86 @@ export const usePublish = (
|
|||||||
}
|
}
|
||||||
}, [metadata, returnType]);
|
}, [metadata, returnType]);
|
||||||
|
|
||||||
|
|
||||||
|
const deleteResource = useCallback(async (publish: QortalGetMetadata) => {
|
||||||
|
const res = await qortalRequest({
|
||||||
|
action: "PUBLISH_QDN_RESOURCE",
|
||||||
|
service: publish.service,
|
||||||
|
identifier: publish.identifier,
|
||||||
|
base64: "RA==",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res?.signature) {
|
||||||
|
const storageKey = getStorageKey();
|
||||||
|
if (storageKey) {
|
||||||
|
const existingPublishes = JSON.parse(localStorage.getItem(storageKey) || "[]");
|
||||||
|
|
||||||
|
// Remove any previous entries for the same identifier
|
||||||
|
const updatedPublishes = existingPublishes.filter(
|
||||||
|
(item: StoredPublish) => item.qortalMetadata.identifier !== publish.identifier && item.qortalMetadata.service !== publish.service && item.qortalMetadata.name !== publish.name
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add the new one with timestamp
|
||||||
|
updatedPublishes.push({ qortalMetadata: {
|
||||||
|
...publish,
|
||||||
|
created: Date.now(),
|
||||||
|
updated: Date.now(),
|
||||||
|
size: 32
|
||||||
|
}, data: "RA==", timestamp: Date.now() });
|
||||||
|
|
||||||
|
// Save back to storage
|
||||||
|
localStorage.setItem(storageKey, JSON.stringify(updatedPublishes));
|
||||||
|
}
|
||||||
|
setPublish(publish, null);
|
||||||
|
setError(null)
|
||||||
|
setIsLoading(false)
|
||||||
|
setHasResource(false)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}, [getStorageKey]);
|
||||||
|
|
||||||
|
|
||||||
|
const updatePublish = useCallback(async (publish: QortalGetMetadata, data: any) => {
|
||||||
|
setError(null)
|
||||||
|
setIsLoading(false)
|
||||||
|
setHasResource(true)
|
||||||
|
setPublish(publish, {qortalMetadata: {
|
||||||
|
...publish,
|
||||||
|
created: Date.now(),
|
||||||
|
updated: Date.now(),
|
||||||
|
size: 100
|
||||||
|
}, data});
|
||||||
|
|
||||||
|
const storageKey = getStorageKey();
|
||||||
|
if (storageKey) {
|
||||||
|
const existingPublishes = JSON.parse(localStorage.getItem(storageKey) || "[]");
|
||||||
|
|
||||||
|
// Remove any previous entries for the same identifier
|
||||||
|
const updatedPublishes = existingPublishes.filter(
|
||||||
|
(item: StoredPublish) => item.qortalMetadata.identifier !== publish.identifier && item.qortalMetadata.service !== publish.service && item.qortalMetadata.name !== publish.name
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add the new one with timestamp
|
||||||
|
updatedPublishes.push({ qortalMetadata: {
|
||||||
|
...publish,
|
||||||
|
created: Date.now(),
|
||||||
|
updated: Date.now(),
|
||||||
|
size: 100
|
||||||
|
}, data, timestamp: Date.now() });
|
||||||
|
|
||||||
|
// Save back to storage
|
||||||
|
localStorage.setItem(storageKey, JSON.stringify(updatedPublishes));
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [getStorageKey, setPublish]);
|
||||||
|
|
||||||
if (!metadata)
|
if (!metadata)
|
||||||
return {
|
return {
|
||||||
fetchPublish,
|
fetchPublish,
|
||||||
|
updatePublish,
|
||||||
|
deletePublish: deleteResource,
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isLoading,
|
isLoading,
|
||||||
error,
|
error,
|
||||||
@ -102,5 +266,7 @@ export const usePublish = (
|
|||||||
hasResource,
|
hasResource,
|
||||||
refetch: fetchPublish,
|
refetch: fetchPublish,
|
||||||
fetchPublish,
|
fetchPublish,
|
||||||
|
updatePublish,
|
||||||
|
deletePublish: deleteResource,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -259,7 +259,7 @@ export const useResources = (retryAttempts: number = 2) => {
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const deleteProduct = useCallback(async (resourcesToDelete: QortalMetadata[]) => {
|
const deleteResource = useCallback(async (resourcesToDelete: QortalMetadata[]) => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -288,10 +288,9 @@ export const useResources = (retryAttempts: number = 2) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
fetchResources,
|
fetchResources,
|
||||||
fetchIndividualPublishJson,
|
|
||||||
addNewResources,
|
addNewResources,
|
||||||
updateNewResources,
|
updateNewResources,
|
||||||
deleteProduct,
|
deleteResource,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,3 +2,4 @@ import './index.css'
|
|||||||
export { GlobalProvider, useGlobal } from "./context/GlobalProvider";
|
export { GlobalProvider, useGlobal } from "./context/GlobalProvider";
|
||||||
export {usePublish} from "./hooks/usePublish"
|
export {usePublish} from "./hooks/usePublish"
|
||||||
export {ResourceListDisplay} from "./components/ResourceList/ResourceListDisplay"
|
export {ResourceListDisplay} from "./components/ResourceList/ResourceListDisplay"
|
||||||
|
export {QortalSearchParams} from './types/interfaces/resources'
|
||||||
|
@ -1,35 +1,65 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { IdentifierBuilder } from "../utils/encryption";
|
|
||||||
import { QortalGetMetadata } from "../types/interfaces/resources";
|
import { QortalGetMetadata } from "../types/interfaces/resources";
|
||||||
import { TemporaryResource } from "../hooks/useResources";
|
import { TemporaryResource } from "../hooks/useResources";
|
||||||
|
|
||||||
|
interface PublishCache {
|
||||||
|
data: TemporaryResource | null;
|
||||||
interface PublishState {
|
expiry: number;
|
||||||
publishes: Record<string, TemporaryResource> ;
|
}
|
||||||
|
|
||||||
getPublish: (qortalGetMetadata: QortalGetMetadata | null) => TemporaryResource | null;
|
interface PublishState {
|
||||||
setPublish: (qortalGetMetadata: QortalGetMetadata, data: TemporaryResource) => void;
|
publishes: Record<string, PublishCache>;
|
||||||
|
|
||||||
|
getPublish: (qortalGetMetadata: QortalGetMetadata | null, ignoreExpire?: boolean) => TemporaryResource | null;
|
||||||
|
setPublish: (qortalGetMetadata: QortalGetMetadata, data: TemporaryResource | null, customExpiry?: number) => void;
|
||||||
|
clearExpiredPublishes: () => void;
|
||||||
|
publishExpiryDuration: number; // Default expiry duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Typed Zustand Store
|
|
||||||
export const usePublishStore = create<PublishState>((set, get) => ({
|
export const usePublishStore = create<PublishState>((set, get) => ({
|
||||||
publishes: {},
|
publishes: {},
|
||||||
getPublish: (qortalGetMetadata) => {
|
publishExpiryDuration: 5 * 60 * 1000, // Default expiry: 5 minutes
|
||||||
if(qortalGetMetadata === null) return null
|
|
||||||
const cache = get().publishes[`${qortalGetMetadata.service}-${qortalGetMetadata.name}-${qortalGetMetadata.identifier}`];
|
getPublish: (qortalGetMetadata, ignoreExpire = false) => {
|
||||||
if (cache) {
|
if (!qortalGetMetadata) return null;
|
||||||
return cache
|
|
||||||
}
|
const id = `${qortalGetMetadata.service}-${qortalGetMetadata.name}-${qortalGetMetadata.identifier}`;
|
||||||
return null;
|
const cache = get().publishes[id];
|
||||||
},
|
|
||||||
setPublish: (qortalGetMetadata, data) =>
|
if (cache) {
|
||||||
|
if (cache.expiry > Date.now() || ignoreExpire) {
|
||||||
|
if(cache?.data?.qortalMetadata?.size === 32) return null
|
||||||
|
return cache.data;
|
||||||
|
} else {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
return {
|
const updatedPublishes = { ...state.publishes };
|
||||||
publishes: {
|
delete updatedPublishes[id];
|
||||||
...state.publishes,
|
return { publishes: updatedPublishes };
|
||||||
[`${qortalGetMetadata.service}-${qortalGetMetadata.name}-${qortalGetMetadata.identifier}`]: data,
|
});
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
}),
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
setPublish: (qortalGetMetadata, data, customExpiry) => {
|
||||||
|
const id = `${qortalGetMetadata.service}-${qortalGetMetadata.name}-${qortalGetMetadata.identifier}`;
|
||||||
|
const expiry = Date.now() + (customExpiry || get().publishExpiryDuration);
|
||||||
|
|
||||||
|
set((state) => ({
|
||||||
|
publishes: {
|
||||||
|
...state.publishes,
|
||||||
|
[id]: { data, expiry },
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
clearExpiredPublishes: () => {
|
||||||
|
set((state) => {
|
||||||
|
const now = Date.now();
|
||||||
|
const updatedPublishes = Object.fromEntries(
|
||||||
|
Object.entries(state.publishes).filter(([_, cache]) => cache.expiry > now)
|
||||||
|
);
|
||||||
|
return { publishes: updatedPublishes };
|
||||||
|
});
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user