From 07620054ca3eda08700e505c037fa27a8ceeb8f2 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 21 Jun 2025 06:21:07 +0300 Subject: [PATCH] fixes to publish and status --- src/App.tsx | 5 + src/components/Apps/AppViewer.tsx | 15 ++- .../Apps/useQortalMessageListener.tsx | 58 ++++++++- src/components/Chat/ChatGroup.tsx | 2 +- src/qdn/publish/pubish.ts | 117 +++++++++++++++--- src/qortalRequests.ts | 2 +- src/qortalRequests/get.ts | 38 +++--- 7 files changed, 191 insertions(+), 46 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 71eb3e3..64d0ba4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -826,6 +826,11 @@ function App() { executeEvent("openThreadNewPost", { data: message.payload.data, }); + } else if ( + message.action === "receiveChunks" && + isMainWindow + ) { + executeEvent('receiveChunks', message.payload?.data); } // Call the permission request handler for "QORTAL_REQUEST_PERMISSION" diff --git a/src/components/Apps/AppViewer.tsx b/src/components/Apps/AppViewer.tsx index 2f1ab45..a83d4f0 100644 --- a/src/components/Apps/AppViewer.tsx +++ b/src/components/Apps/AppViewer.tsx @@ -102,12 +102,15 @@ export const AppViewer = React.forwardRef(({ app , hide, isDevMode, skipAuth}, i const receiveChunksFunc = useCallback( (e) => { + console.log('eee', e) const iframe = iframeRef?.current; if (!iframe || !iframe?.src) return; if (app?.tabId !== e.detail?.tabId) return; const publishLocation = e.detail?.publishLocation; const chunksSubmitted = e.detail?.chunksSubmitted; const totalChunks = e.detail?.totalChunks; + const retry = e.detail?.retry; + const filename = e.detail?.filename; try { if (publishLocation === undefined || publishLocation === null) return; const dataToBeSent = {}; @@ -117,8 +120,15 @@ export const AppViewer = React.forwardRef(({ app , hide, isDevMode, skipAuth}, i if (totalChunks !== undefined && totalChunks !== null) { dataToBeSent.totalChunks = totalChunks; } - const targetOrigin = new URL(iframe.src).origin; - iframe.contentWindow?.postMessage( + if (retry !== undefined && retry !== null) { + dataToBeSent.retry = retry; + } + if (filename !== undefined && filename !== null) { + dataToBeSent.filename = filename; + } + const targetOrigin = iframeRef.current ? new URL(iframeRef.current.src).origin : "*"; + console.log('targetOrigin', targetOrigin) + iframeRef.current?.contentWindow?.postMessage( { action: 'PUBLISH_STATUS', publishLocation, @@ -128,6 +138,7 @@ export const AppViewer = React.forwardRef(({ app , hide, isDevMode, skipAuth}, i }, targetOrigin ); + } catch (err) { console.error('Failed to send status to iframe:', err); } diff --git a/src/components/Apps/useQortalMessageListener.tsx b/src/components/Apps/useQortalMessageListener.tsx index 2343a2e..dc815cd 100644 --- a/src/components/Apps/useQortalMessageListener.tsx +++ b/src/components/Apps/useQortalMessageListener.tsx @@ -6,6 +6,7 @@ import { navigationControllerAtom } from '../../atoms/global'; import { extractComponents } from '../Chat/MessageDisplay'; import { isRunningGateway } from '../../qortalRequests'; import { MAX_SIZE_PUBLIC_NODE, MAX_SIZE_PUBLISH } from '../../constants/constants'; +import { createEndpoint } from '../../background'; @@ -303,14 +304,26 @@ async function handleGetFileFromIndexedDB(fileId, sendResponse) { } } - async function handleSendDataChunksToCore(fileId, chunkUrl, sendResponse){ + async function handleSendDataChunksToCore(fileId, chunkUrl, sendResponse, appInfo, resourceInfo){ try { if(!fileReferences[fileId]) throw new Error('No file reference found') const chunkSize = 5 * 1024 * 1024; // 5MB const file = fileReferences[fileId] const totalChunks = Math.ceil(file.size / chunkSize); - + executeEvent('receiveChunks', { + tabId: appInfo.tabId, + publishLocation: { + name: resourceInfo?.name, + identifier: resourceInfo?.identifier, + service: resourceInfo?.service, + }, + chunksSubmitted: 0, + totalChunks, + processed: false, + filename: + resourceInfo?.filename + }) for (let index = 0; index < totalChunks; index++) { const start = index * chunkSize; const end = Math.min(start + chunkSize, file.size); @@ -321,6 +334,16 @@ async function handleGetFileFromIndexedDB(fileId, sendResponse) { formData.append('index', index); await uploadChunkWithRetry(chunkUrl, formData, index); + executeEvent('receiveChunks', { + tabId: appInfo.tabId, + publishLocation: { + name: resourceInfo?.name, + identifier: resourceInfo?.identifier, + service: resourceInfo?.service, + }, + chunksSubmitted: index + 1, + totalChunks, + }) } sendResponse({ result: true }); } catch (error) { @@ -678,7 +701,9 @@ isDOMContentLoaded: false } if (data) { sendMessageToRuntime( - { action: event.data.action, type: 'qortalRequest', payload: data, isExtension: true }, + { action: event.data.action, type: 'qortalRequest', payload: data, isExtension: true, appInfo: { + name: appName, service: appService, tabId + } }, event.ports[0] ); } else { @@ -709,7 +734,9 @@ isDOMContentLoaded: false } if (data) { sendMessageToRuntime( - { action: event.data.action, type: 'qortalRequest', payload: data, isExtension: true }, + { action: event.data.action, type: 'qortalRequest', payload: data, isExtension: true, appInfo: { + name: appName, service: appService, tabId + } }, event.ports[0] ); } else { @@ -761,6 +788,22 @@ isDOMContentLoaded: false ); } } + const totalFileSize = resources.reduce((acc, resource) => { + const file = resource?.file; + if (file && file?.size && !isNaN(file?.size)) { + return acc + file.size; + } + return acc; + }, 0); + if (totalFileSize > 0) { + const urlCheck = `/arbitrary/check/tmp?totalSize=${totalFileSize}`; + + const checkEndpoint = await createEndpoint(urlCheck); + const checkRes = await fetch(checkEndpoint); + if (!checkRes.ok) { + throw new Error('Not enough space on your hard drive'); + } + } const hasOversizedFile = resources.some((resource) => { const file = resource?.file; @@ -793,7 +836,9 @@ isDOMContentLoaded: false } sendMessageToRuntime( - { action: event.data.action, type: 'qortalRequest', payload: data, isExtension: true }, + { action: event.data.action, type: 'qortalRequest', payload: data, isExtension: true , appInfo: { + name: appName, service: appService, tabId + }}, event.ports[0] ); } else if(event?.data?.action === 'LINK_TO_QDN_RESOURCE' || @@ -917,7 +962,8 @@ isDOMContentLoaded: false handleGetFileFromIndexedDB(message.fileId, sendResponse); return true; // Keep channel open for async } else if (message.action === 'sendDataChunksToCore') { - handleSendDataChunksToCore(message.fileId, message.chunkUrl, sendResponse); + console.log('message', message) + handleSendDataChunksToCore(message.fileId, message.chunkUrl, sendResponse, message?.appInfo, message?.resourceInfo); return true; // Keep channel open for async } else if (message.action === 'getFileBase64') { handleGetFileBase64(message.fileId, sendResponse); diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index b41f4f4..23ae382 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -702,7 +702,7 @@ const sendMessage = async ()=> { } ); }); - if (res !== true) throw new Error('Unable to publish images'); + if (res?.error) throw new Error('Unable to publish images'); } const images = diff --git a/src/qdn/publish/pubish.ts b/src/qdn/publish/pubish.ts index 10e7814..7017e9a 100644 --- a/src/qdn/publish/pubish.ts +++ b/src/qdn/publish/pubish.ts @@ -26,6 +26,10 @@ async function reusablePost(endpoint, _body) { }, body: _body, }); + if (!response.ok) { + const errorText = await response.text(); + throw new Error(errorText); + } let data; try { data = await response.clone().json(); @@ -73,7 +77,48 @@ async function uploadChunkWithRetry(endpoint, formData, index, maxRetries = 3) { } } +async function resuablePostRetry( + endpoint, + body, + maxRetries = 3, + appInfo, + resourceInfo +) { + let attempt = 0; + while (attempt < maxRetries) { + try { + const response = await reusablePost(endpoint, body); + return response; + } catch (err) { + attempt++; + if (attempt >= maxRetries) { + throw new Error( + err instanceof Error + ? err?.message || `Failed to make request` + : `Failed to make request` + ); + } + if (appInfo?.tabId && resourceInfo) { + chrome.runtime.sendMessage({ + action: "receiveChunks", + payload: { data: { + tabId: appInfo.tabId, + publishLocation: { + name: resourceInfo?.name, + identifier: resourceInfo?.identifier, + service: resourceInfo?.service, + }, + retry: true, + } }, + }); + + } + // Wait 10 seconds before next retry + await new Promise((res) => setTimeout(res, 25_000)); + } + } +} export const publishData = async ({ registeredName, @@ -100,7 +145,13 @@ export const publishData = async ({ }; const convertBytesForSigning = async (transactionBytesBase58: string) => { - return await reusablePost('/transactions/convert', transactionBytesBase58); + return await resuablePostRetry( + '/transactions/convert', + transactionBytesBase58, + 3, + appInfo, + { identifier, name: registeredName, service } + ); }; const getArbitraryFee = async () => { @@ -157,9 +208,12 @@ export const publishData = async ({ }; const processTransactionVersion2 = async (bytes) => { - return await reusablePost( + return await resuablePostRetry( '/transactions/process?apiVersion=2', - Base58.encode(bytes) + Base58.encode(bytes), + 3, + appInfo, + { identifier, name: registeredName, service } ); }; @@ -196,15 +250,19 @@ export const publishData = async ({ myResponse = response; } if (appInfo?.tabId) { - executeEvent('receiveChunks', { - tabId: appInfo.tabId, + + chrome.runtime.sendMessage({ + action: "receiveChunks", + payload: { data: { + tabId: appInfo.tabId, publishLocation: { name: registeredName, identifier, service, }, processed: true, - }); + } }, + }); } return myResponse; }; @@ -319,8 +377,11 @@ export const publishData = async ({ } uploadDataUrl = uploadDataUrl + paramQueries; if (appInfo?.tabId) { - executeEvent('receiveChunks', { - tabId: appInfo.tabId, + + chrome.runtime.sendMessage({ + action: "receiveChunks", + payload: { data: { + tabId: appInfo.tabId, publishLocation: { name: registeredName, identifier, @@ -329,9 +390,14 @@ export const publishData = async ({ chunksSubmitted: 1, totalChunks: 1, processed: false, - }); + } }, + }); } - return await reusablePost(uploadDataUrl, postBody); + return await resuablePostRetry(uploadDataUrl, postBody, 3, appInfo, { + identifier, + name: registeredName, + service, + }); } const file = data; @@ -346,15 +412,23 @@ export const publishData = async ({ const chunkUrl = uploadDataUrl + `/chunk`; const createdChunkUrl = await createEndpoint(chunkUrl) if(sender){ - await sendDataChunksToCore(file, createdChunkUrl, sender) + await sendDataChunksToCore(file, createdChunkUrl, sender, appInfo, { + name: registeredName, + identifier, + service, + filename: file?.name || filename || title || `${service}-${identifier || ''}` + }) } else { const chunkSize = 5 * 1024 * 1024; // 5MB const totalChunks = Math.ceil(file.size / chunkSize); if (appInfo?.tabId) { - executeEvent('receiveChunks', { - tabId: appInfo.tabId, + + chrome.runtime.sendMessage({ + action: "receiveChunks", + payload: { data: { + tabId: appInfo.tabId, publishLocation: { name: registeredName, identifier, @@ -363,7 +437,10 @@ export const publishData = async ({ chunksSubmitted: 0, totalChunks, processed: false, - }); + filename: + file?.name || filename || title || `${service}-${identifier || ''}`, + } }, + }); } for (let index = 0; index < totalChunks; index++) { const start = index * chunkSize; @@ -375,8 +452,11 @@ export const publishData = async ({ await uploadChunkWithRetry(chunkUrl, formData, index); if (appInfo?.tabId) { - executeEvent('receiveChunks', { - tabId: appInfo.tabId, + + chrome.runtime.sendMessage({ + action: "receiveChunks", + payload: { data: { + tabId: appInfo.tabId, publishLocation: { name: registeredName, identifier, @@ -384,7 +464,8 @@ export const publishData = async ({ }, chunksSubmitted: index + 1, totalChunks, - }); + } }, + }); } } } @@ -398,7 +479,7 @@ export const publishData = async ({ headers: {}, }); - if (!response.ok) { + if (!response?.ok) { const errorText = await response.text(); throw new Error(`Finalize failed: ${errorText}`); } diff --git a/src/qortalRequests.ts b/src/qortalRequests.ts index 1724daf..22a7bd1 100644 --- a/src/qortalRequests.ts +++ b/src/qortalRequests.ts @@ -947,7 +947,7 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => { case "GET_PRIMARY_NAME": { const data = request.payload; - getNameInfoForOthers(data) + getNameInfoForOthers(data?.address) .then((res) => { const resData = res ? res : ""; sendResponse(resData); diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index bc06513..2baae31 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -425,11 +425,11 @@ function getFileFromContentScript(fileId, sender) { }); } -export function sendDataChunksToCore(fileId, chunkUrl, sender) { +export function sendDataChunksToCore(fileId, chunkUrl, sender, appInfo, resourceInfo) { return new Promise((resolve, reject) => { chrome.tabs.sendMessage( sender.tab.id, - { action: "sendDataChunksToCore", fileId: fileId, chunkUrl }, + { action: "sendDataChunksToCore", fileId: fileId, chunkUrl, appInfo, resourceInfo }, (response) => { if (response && response.result) { resolve(response.result); @@ -1220,6 +1220,7 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten throw new Error("User declined request"); } let failedPublishesIdentifiers = []; + const publishedResponses = []; for (const resource of resources) { try { const requiredFields = ["service"]; @@ -1322,28 +1323,29 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten (resource?.base64 || resource?.data64 || resourceEncrypt) ? 'base64' : 'file'; - await retryTransaction(publishData, [ - { - registeredName: encodeURIComponent(name), - data: rawData, - service: service, - identifier: encodeURIComponent(identifier), - uploadType: dataType, - filename: filename, - title, - description, + const response = await publishData({ + apiVersion: 2, category, + data: rawData, + description, + filename: filename, + identifier: encodeURIComponent(identifier), + registeredName: encodeURIComponent(resource?.name || name), + service: service, tag1, tag2, tag3, tag4, tag5, - apiVersion: 2, - withFee: true, + title, sender, - appInfo - }, - ], true); + uploadType: dataType, + withFee: true, + appInfo, + }); + if (response?.signature) { + publishedResponses.push(response); + } await new Promise((res) => { setTimeout(() => { res(); @@ -1382,7 +1384,7 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten receiver: appFeeRecipient }, true) } - return true; + return publishedResponses; }; export const voteOnPoll = async (data, isFromExtension) => {