pulled alpha's recent changes for hopefully final release candidate

This commit is contained in:
crowetic 2024-11-06 08:31:19 -08:00
parent fd5ba48611
commit 38fd0c55a0
2 changed files with 240 additions and 9 deletions

View File

@ -1,5 +1,57 @@
console.log("Gateway mode");
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";
@ -32,7 +84,7 @@ function qdnGatewayShowModal(message) {
document.body.appendChild(modalElement);
}
window.addEventListener("message", (event) => {
window.addEventListener("message", async (event) => {
if (event == null || event.data == null || event.data.length == 0) {
return;
}
@ -43,7 +95,7 @@ window.addEventListener("message", (event) => {
// Gateway mode only cares about requests that were intended for the UI
return;
}
let response;
let data = event.data;
@ -59,6 +111,8 @@ window.addEventListener("message", (event) => {
case "GET_LIST_ITEMS":
case "ADD_LIST_ITEMS":
case "DELETE_LIST_ITEM":
const isExtInstalledRes = await isExtensionInstalledFunc()
if(isExtInstalledRes?.version) 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 UI desktop app. More info at: https://qortal.org";
response = "{\"error\": \"" + errorString + "\"}"

View File

@ -1,3 +1,118 @@
let customQDNHistoryPaths = []; // Array to track visited paths
let currentIndex = -1; // Index to track the current position in the history
let isManualNavigation = true; // Flag to control when to add new paths. set to false when navigating through a back/forward call
function resetVariables(){
let customQDNHistoryPaths = [];
let currentIndex = -1;
let isManualNavigation = true;
}
function getNameAfterService(url) {
try {
const parsedUrl = new URL(url);
const pathParts = parsedUrl.pathname.split('/');
// Find the index of "WEBSITE" or "APP" and get the next part
const serviceIndex = pathParts.findIndex(part => part === 'WEBSITE' || part === 'APP');
if (serviceIndex !== -1 && pathParts[serviceIndex + 1]) {
return pathParts[serviceIndex + 1];
} else {
return null; // Return null if "WEBSITE" or "APP" is not found or has no following part
}
} catch (error) {
console.error("Invalid URL provided:", error);
return null;
}
}
function parseUrl(url) {
try {
const parsedUrl = new URL(url);
// Check if isManualNavigation query exists and is set to "false"
const isManual = parsedUrl.searchParams.get("isManualNavigation");
if (isManual !== null && isManual == "false") {
isManualNavigation = false
// Optional: handle this condition if needed (e.g., return or adjust the response)
}
// Remove theme, identifier, and time queries if they exist
parsedUrl.searchParams.delete("theme");
parsedUrl.searchParams.delete("identifier");
parsedUrl.searchParams.delete("time");
parsedUrl.searchParams.delete("isManualNavigation");
// Extract the pathname and remove the prefix if it matches "render/APP" or "render/WEBSITE"
const path = parsedUrl.pathname.replace(/^\/render\/(APP|WEBSITE)\/[^/]+/, "");
// Combine the path with remaining query params (if any)
return path + parsedUrl.search;
} catch (error) {
console.error("Invalid URL provided:", error);
return null;
}
}
// Tell the client to open a new tab. Done when an app is linking to another app
function openNewTab(data){
window.parent.postMessage({
action: 'SET_TAB',
requestedHandler:'UI',
payload: data
}, '*');
}
// sends navigation information to the client in order to manage back/forward navigation
function sendNavigationInfoToParent(isDOMContentLoaded){
window.parent.postMessage({
action: 'NAVIGATION_HISTORY',
requestedHandler:'UI',
payload: {
customQDNHistoryPaths,
currentIndex,
isDOMContentLoaded: isDOMContentLoaded ? true : false
}
}, '*');
}
function handleQDNResourceDisplayed(pathurl, isDOMContentLoaded) {
// make sure that an empty string the root path
const path = pathurl || '/'
if (!isManualNavigation) {
isManualNavigation = true
// If the navigation is automatic (back/forward), do not add new entries
return;
}
// If it's a new path, add it to the history array and adjust the index
if (customQDNHistoryPaths[currentIndex] !== path) {
customQDNHistoryPaths = customQDNHistoryPaths.slice(0, currentIndex + 1);
// Add the new path and move the index to the new position
customQDNHistoryPaths.push(path);
currentIndex = customQDNHistoryPaths.length - 1;
sendNavigationInfoToParent(isDOMContentLoaded)
} else {
currentIndex = customQDNHistoryPaths.length - 1
sendNavigationInfoToParent(isDOMContentLoaded)
}
// Reset isManualNavigation after handling
isManualNavigation = true;
}
function httpGet(url) {
var request = new XMLHttpRequest();
request.open("GET", url, false);
@ -156,7 +271,7 @@ function convertToResourceUrl(url, isLink) {
return buildResourceUrl(c.service, c.name, c.identifier, c.path, isLink);
}
window.addEventListener("message", (event) => {
window.addEventListener("message", async (event) => {
if (event == null || event.data == null || event.data.length == 0) {
return;
}
@ -199,10 +314,51 @@ window.addEventListener("message", (event) => {
if (data.identifier != null) url = url.concat("/" + data.identifier);
return httpGetAsyncWithEvent(event, url);
case "LINK_TO_QDN_RESOURCE":
if (data.service == null) data.service = "WEBSITE"; // Default to WEBSITE
window.location = buildResourceUrl(data.service, data.name, data.identifier, data.path, true);
return;
case "LINK_TO_QDN_RESOURCE":
if (data.service == null) data.service = "WEBSITE"; // Default to WEBSITE
const nameOfCurrentApp = getNameAfterService(window.location.href);
// Check to see if the link is an external app. If it is, request that the client opens a new tab instead of manipulating the window's history stack.
if (nameOfCurrentApp !== data.name) {
// Attempt to open a new tab and wait for a response
const navigationPromise = new Promise((resolve, reject) => {
function handleMessage(event) {
if (event.data?.action === 'SET_TAB_SUCCESS' && event.data.payload?.name === data.name) {
window.removeEventListener('message', handleMessage);
resolve();
}
}
window.addEventListener('message', handleMessage);
// Send the message to the parent window
openNewTab({
name: data.name,
service: data.service,
identifier: data.identifier,
path: data.path
});
// Set a timeout to reject the promise if no response is received within 200ms
setTimeout(() => {
window.removeEventListener('message', handleMessage);
reject(new Error("No response within 200ms"));
}, 200);
});
// Handle the promise, and if it times out, fall back to the else block
navigationPromise
.then(() => {
console.log('Tab opened successfully');
})
.catch(() => {
console.warn('No response, proceeding with window.location');
window.location = buildResourceUrl(data.service, data.name, data.identifier, data.path, true);
});
} else {
window.location = buildResourceUrl(data.service, data.name, data.identifier, data.path, true);
}
return;
case "LIST_QDN_RESOURCES":
url = "/arbitrary/resources?";
@ -351,10 +507,18 @@ window.addEventListener("message", (event) => {
if (data.inverse != null) url = url.concat("&inverse=" + data.inverse);
return httpGetAsyncWithEvent(event, url);
case "PERFORMING_NON_MANUAL":
isManualNavigation = false
currentIndex = data.currentIndex
return;
default:
// Pass to parent (UI), in case they can fulfil this request
event.data.requestedHandler = "UI";
parent.postMessage(event.data, '*', [event.ports[0]]);
return;
}
@ -523,7 +687,8 @@ const qortalRequestWithTimeout = (request, timeout) =>
/**
* Send current page details to UI
*/
document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('DOMContentLoaded', (event) => {
resetVariables()
qortalRequest({
action: "QDN_RESOURCE_DISPLAYED",
service: _qdnService,
@ -531,6 +696,10 @@ document.addEventListener('DOMContentLoaded', () => {
identifier: _qdnIdentifier,
path: _qdnPath
});
// send to the client the first path when the app loads.
const firstPath = parseUrl(window?.location?.href || "")
handleQDNResourceDisplayed(firstPath, true);
// Increment counter when page fully loads
});
/**
@ -538,12 +707,20 @@ document.addEventListener('DOMContentLoaded', () => {
*/
navigation.addEventListener('navigate', (event) => {
const url = new URL(event.destination.url);
let fullpath = url.pathname + url.hash;
const processedPath = (fullpath.startsWith(_qdnBase)) ? fullpath.slice(_qdnBase.length) : fullpath;
qortalRequest({
action: "QDN_RESOURCE_DISPLAYED",
service: _qdnService,
name: _qdnName,
identifier: _qdnIdentifier,
path: (fullpath.startsWith(_qdnBase)) ? fullpath.slice(_qdnBase.length) : fullpath
path: processedPath
});
// Put a timeout so that the DOMContentLoaded listener's logic executes before the navigate listener
setTimeout(()=> {
handleQDNResourceDisplayed(processedPath);
}, 100)
});