console.log("Gateway mode"); const gatewayInteractiveActions = new Set([ "GET_USER_ACCOUNT", "SAVE_FILE", "SIGN_TRANSACTION", "DECRYPT_DATA", "PUBLISH_QDN_RESOURCE", "PUBLISH_MULTIPLE_QDN_RESOURCES", "SEND_CHAT_MESSAGE", "CREATE_TRADE_BUY_ORDER", "CREATE_TRADE_SELL_ORDER", "CANCEL_TRADE_SELL_ORDER", "VOTE_ON_POLL", "CREATE_POLL", "JOIN_GROUP", "DEPLOY_AT", "GET_WALLET_BALANCE", "SEND_COIN", "GET_LIST_ITEMS", "ADD_LIST_ITEMS", "DELETE_LIST_ITEM", ]); const gatewayLocalActions = new Set(["QDN_RESOURCE_DISPLAYED"]); function sendRequestToExtension( requestType, payload, timeout = 750 ) { return new Promise((resolve, reject) => { const requestId = Math.random().toString(36).substring(2, 15); // Generate a unique ID for the request const detail = { type: requestType, payload, requestId, timeout: timeout / 1000, }; // Store the timeout ID so it can be cleared later const timeoutId = setTimeout(() => { document.removeEventListener("qortalExtensionResponses", handleResponse); reject(new Error("Request timed out")); }, timeout); // Adjust timeout as necessary function handleResponse(event) { const { requestId: responseId, data } = event.detail; if (requestId === responseId) { // Match the response with the request document.removeEventListener("qortalExtensionResponses", handleResponse); clearTimeout(timeoutId); // Clear the timeout upon successful response resolve(data); } } document.addEventListener("qortalExtensionResponses", handleResponse); document.dispatchEvent( new CustomEvent("qortalExtensionRequests", { detail }) ); }); } const isExtensionInstalledFunc = async () => { try { const response = await sendRequestToExtension( "REQUEST_IS_INSTALLED", {}, 750 ); return response; } catch (error) { // not installed } }; function qdnGatewayShowModal(message) { const modalElementId = "qdnGatewayModal"; if (document.getElementById(modalElementId) != null) { document.body.removeChild(document.getElementById(modalElementId)); } var modalElement = document.createElement('div'); modalElement.style.cssText = 'position:fixed; z-index:99999; background:#fff; padding:20px; border-radius:5px; font-family:sans-serif; bottom:20px; right:20px; color:#000; max-width:400px; box-shadow:0 3px 10px rgb(0 0 0 / 0.2); font-family:arial; font-weight:normal; font-size:16px;'; modalElement.innerHTML = message + "

"; modalElement.id = modalElementId; var closeButton = document.createElement('button'); closeButton.style.cssText = 'background-color:#008CBA; border:none; color:white; cursor:pointer; float: right; margin: 10px; padding:15px; border-radius:5px; display:inline-block; text-align:center; text-decoration:none; font-family:arial; font-weight:normal; font-size:16px;'; closeButton.innerText = "Close"; closeButton.addEventListener ("click", function() { document.body.removeChild(document.getElementById(modalElementId)); }); modalElement.appendChild(closeButton); var qortalButton = document.createElement('button'); qortalButton.style.cssText = 'background-color:#4CAF50; border:none; color:white; cursor:pointer; float: right; margin: 10px; padding:15px; border-radius:5px; text-align:center; text-decoration:none; display:inline-block; font-family:arial; font-weight:normal; font-size:16px;'; qortalButton.innerText = "Learn more"; qortalButton.addEventListener ("click", function() { document.body.removeChild(document.getElementById(modalElementId)); window.open("https://qortal.dev"); }); modalElement.appendChild(qortalButton); document.body.appendChild(modalElement); } window.addEventListener("message", async (event) => { if (event == null || event.data == null || event.data.length == 0) { return; } if (event.data.action == null) { return; } if (gatewayLocalActions.has(event.data.action)) { // Gateway pages do not have a Qortal UI to receive navigation // tracking messages. Acknowledge these locally so the app's // qortalRequest() promise does not time out during page load. event.stopImmediatePropagation(); const port = event.ports?.[0]; if (port != null) { port.postMessage({ result: null, error: null }); } return; } // Requests without a handler are the original qortalRequest() messages. // Handle them here before q-apps.js can transfer their MessagePort to the // parent. A request already marked for the UI has already been forwarded. if (event.data.requestedHandler === "UI") { return; } if (!gatewayInteractiveActions.has(event.data.action)) { return; } // q-apps.js also listens for message events. This handler must claim the // request synchronously because the extension check below is asynchronous. event.stopImmediatePropagation(); let response; let data = event.data; switch (data.action) { case "GET_USER_ACCOUNT": case "SAVE_FILE": case "SIGN_TRANSACTION": case "DECRYPT_DATA": case "PUBLISH_QDN_RESOURCE": case "PUBLISH_MULTIPLE_QDN_RESOURCES": case "SEND_CHAT_MESSAGE": case "CREATE_TRADE_BUY_ORDER": case "CREATE_TRADE_SELL_ORDER": case "CANCEL_TRADE_SELL_ORDER": case "VOTE_ON_POLL": case "CREATE_POLL": case "JOIN_GROUP": case "DEPLOY_AT": case "GET_WALLET_BALANCE": case "SEND_COIN": case "GET_LIST_ITEMS": case "ADD_LIST_ITEMS": case "DELETE_LIST_ITEM": const isExtInstalledRes = await isExtensionInstalledFunc(); if (isExtInstalledRes?.version) { // Preserve the normal UI flow when the extension is present. // The forwarded event is marked so this gateway handler does // not process it again if parent === window. event.data.requestedHandler = "UI"; const port = event.ports?.[0]; if (port != null) { parent.postMessage(event.data, "*", [port]); } else { parent.postMessage(event.data, "*"); } return; } const errorString = "Interactive features were requested, but these are not yet supported when viewing via a gateway. To use interactive features, please access using the Qortal Hub on a desktop. More info at: https://qortal.dev/onboarding"; response = "{\"error\": \"" + errorString + "\"}"; const modalText = "This app is powered by the Qortal blockchain. You are viewing in read-only mode. To use interactive features, please access using the Qortal Hub on a desktop. More info at: https://qortal.dev/onboarding"; qdnGatewayShowModal(modalText); break; default: console.log('Unhandled gateway message: ' + JSON.stringify(data)); return; } handleResponse(event, response); }, false);