Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Touch screen support #6178

Merged
merged 11 commits into from
Aug 26, 2024
17 changes: 15 additions & 2 deletions services/static-webserver/client/source/boot/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,25 @@
<meta name="msapplication-tap-highlight" content="no"/>

<!-- Disable chrome translation requests and automatic phone number linking -->
<meta name="google" value="notranslate"/>
<meta name="google" content="notranslate"/>
<meta name="format-detection" content="telephone=no"/>

<!-- Shortcut icon setup -->
<link rel="shortcut icon" type="image/png" href="${resourcePath}osparc/favicon-osparc.png"/>
<link rel="mask-icon" href="${resourcePath}osparc/favicon-osparc.png"/>

<!-- Manifest -->
<!-- <link rel="manifest" href="/manifest.json"> - dynamically added -->

<!-- Safari Pinned Tab Icons -->
<link rel="mask-icon" href="" color="">
jsaq007 marked this conversation as resolved.
Show resolved Hide resolved

<!-- Microsoft Application Icons -->
<meta name="msapplication-TileColor" content="">
<meta name="msapplication-TileImage" content="">
<!-- <meta name="msapplication-config" content="/browserconfig.xml"> - dynamically added -->

<!-- Theme Color -->
<meta name="theme-color" content="">

<!-- Open Graph metadata -->
<meta property="og:title" content="replace_me_og_title" />
Expand Down
230 changes: 220 additions & 10 deletions services/static-webserver/client/source/class/osparc/Application.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ qx.Class.define("osparc.Application", {
this.__preventAutofillBrowserStyles();
this.__loadCommonCss();
this.__updateTabName();
this.__updateFavicon();
this.__updateMetaTags();

if (qx.core.Environment.get("product.name") === "s4lengine") {
const view = new osparc.auth.BlurredLoginPageS4LEngineering();
Expand Down Expand Up @@ -216,16 +216,226 @@ qx.Class.define("osparc.Application", {
}
},

__updateFavicon: function() {
let link = document.querySelector("link[rel~='icon']");
if (!link) {
link = document.createElement("link");
link.rel = "icon";
document.getElementsByTagName("head")[0].appendChild(link);
__updateMetaTags: function() {
// Update title and meta tags
const productColor = qx.theme.manager.Color.getInstance().resolve("product-color");
const backgroundColor = qx.theme.manager.Color.getInstance().resolve("primary-background-color");

const themeColorMeta = document.querySelector("meta[name='theme-color']");
const tileColorMeta = document.querySelector("meta[name='msapplication-TileColor']");
const tileImageMeta = document.querySelector("meta[name='msapplication-TileImage']");
const maskIconMeta = document.querySelector("meta[name='mask-icon']");

if (themeColorMeta) {
themeColorMeta.setAttribute("content", productColor);
}

if (tileColorMeta && tileImageMeta) {
const maskIcon = osparc.product.Utils.getManifestIconUrl("ms-icon-150x150.png")
Promise.resolve(maskIcon)
.then(resolvedUrl => {
// Create the manifest data object with resolved URLs
tileColorMeta.setAttribute("content", productColor);
tileImageMeta.setAttribute("content", resolvedUrl);
})
.catch(error => {
console.error("Failed to resolve icon URLs:", error);
});
}
link.href = "";
osparc.product.Utils.getFaviconUrl()
.then(url => link.href = url);
if (maskIconMeta) {
const maskIcon = osparc.product.Utils.getManifestIconUrl("safari-pinned-tab.svg")
Promise.resolve(maskIcon)
.then(resolvedUrl => {
// Create the manifest data object with resolved URLs
maskIconMeta.setAttribute("color", productColor);
maskIconMeta.setAttribute("href", resolvedUrl);
})
.catch(error => {
console.error("Failed to resolve icon URLs:", error);
});
}

// Update app icons
this.__updateAppIcons();

// Update manifest
this.__updateManifest(productColor, backgroundColor);

// Update browserconfig
this.__updateBrowserConfig(productColor);
},

__updateAppIcons: function() {
// Array of promises to resolve icon URLs for Apple Touch Icons
const appleIconUrls = [
osparc.product.Utils.getManifestIconUrl("apple-icon-57x57.png"),
osparc.product.Utils.getManifestIconUrl("apple-icon-60x60.png"),
osparc.product.Utils.getManifestIconUrl("apple-icon-72x72.png"),
osparc.product.Utils.getManifestIconUrl("apple-icon-76x76.png"),
osparc.product.Utils.getManifestIconUrl("apple-icon-114x114.png"),
osparc.product.Utils.getManifestIconUrl("apple-icon-120x120.png"),
osparc.product.Utils.getManifestIconUrl("apple-icon-144x144.png"),
osparc.product.Utils.getManifestIconUrl("apple-icon-152x152.png"),
osparc.product.Utils.getManifestIconUrl("apple-icon-180x180.png")
];

// Array of promises to resolve icon URLs for Favicons
const favIconUrls = [
osparc.product.Utils.getManifestIconUrl("android-icon-192x192.png"),
osparc.product.Utils.getManifestIconUrl("favicon-32x32.png"),
osparc.product.Utils.getManifestIconUrl("favicon-96x96.png"),
osparc.product.Utils.getManifestIconUrl("favicon-16x16.png")
];

// Wait for all icon URLs to be resolved
Promise.all([...appleIconUrls, ...favIconUrls])
.then(resolvedUrls => {
const appleResolvedUrls = resolvedUrls.slice(0, appleIconUrls.length);
const favResolvedUrls = resolvedUrls.slice(appleIconUrls.length);

// Remove existing Apple Touch Icons and Favicons
document.querySelectorAll("link[rel='apple-touch-icon'], link[rel='icon']").forEach(link => link.remove());

// Create and insert new Apple Touch Icon links
const appleIconSizes = [
"57x57", "60x60", "72x72", "76x76", "114x114", "120x120", "144x144", "152x152", "180x180"
];
appleResolvedUrls.forEach((url, index) => {
let link = document.createElement("link");
link.setAttribute("rel", "apple-touch-icon");
link.setAttribute("sizes", appleIconSizes[index]);
link.setAttribute("href", url);
document.head.appendChild(link);
});

// Create and insert new Favicon links
const favIconSizes = [
"192x192", "32x32", "96x96", "16x16"
];
favResolvedUrls.forEach((url, index) => {
let link = document.createElement("link");
link.setAttribute("rel", "icon");
link.setAttribute("type", "image/png");
link.setAttribute("sizes", favIconSizes[index]);
link.setAttribute("href", url);
document.head.appendChild(link);
});
})
.catch(error => {
console.error("Failed to resolve icon URLs:", error);
});
},

__updateManifest: function(themeColor, backgroundColor) {
const productName = osparc.product.Utils.getProductName();
let manifestLink = document.querySelector("link[rel='manifest']");

// Array of promises to resolve icon URLs
const iconUrls = [
osparc.product.Utils.getManifestIconUrl("android-icon-36x36.png"),
osparc.product.Utils.getManifestIconUrl("android-icon-48x48.png"),
osparc.product.Utils.getManifestIconUrl("android-icon-72x72.png"),
osparc.product.Utils.getManifestIconUrl("android-icon-96x96.png"),
osparc.product.Utils.getManifestIconUrl("android-icon-144x144.png"),
osparc.product.Utils.getManifestIconUrl("android-icon-192x192.png")
];

// Wait for all icon URLs to be resolved
Promise.all(iconUrls)
.then(resolvedUrls => {
// Create the manifest data object with resolved URLs
const manifestData = {
shortName: productName,
name: productName,
icons: [
{
src: resolvedUrls[0],
sizes: "36x36",
type: "image/png",
density: "0.75"
},
{
src: resolvedUrls[1],
sizes: "48x48",
type: "image/png",
density: "1.0"
},
{
src: resolvedUrls[2],
sizes: "72x72",
type: "image/png",
density: "1.5"
},
{
src: resolvedUrls[3],
sizes: "96x96",
type: "image/png",
density: "2.0"
},
{
src: resolvedUrls[4],
sizes: "144x144",
type: "image/png",
density: "3.0"
},
{
src: resolvedUrls[5],
sizes: "192x192",
type: "image/png",
density: "4.0"
}
],
display: "standalone",
themeColor: themeColor,
backgroundColor: backgroundColor
};

const blob = new Blob([JSON.stringify(manifestData)], { type: "application/json" });
const manifestURL = URL.createObjectURL(blob);

// Create or update the manifest link in the document head
if (!manifestLink) {
manifestLink = document.createElement("link");
manifestLink.setAttribute("rel", "manifest");
document.head.appendChild(manifestLink);
}
manifestLink.setAttribute("href", manifestURL);
})
.catch(error => {
console.error("Failed to resolve icon URLs:", error);
});
},

// Method to dynamically update the browserconfig
__updateBrowserConfig: function(tileColor) {
// Resolve the icon URL asynchronously
osparc.product.Utils.getManifestIconUrl("ms-icon-150x150.png")
.then(msTileIcon => {
const browserconfigData = `<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="${msTileIcon}"/>
<TileColor>${tileColor}</TileColor>
</tile>
</msapplication>
</browserconfig>`;

const blob = new Blob([browserconfigData], { type: "application/xml" });
const browserconfigURL = URL.createObjectURL(blob);

// Check if the meta tag exists, create it if not
let browserConfigLink = document.querySelector("meta[name='msapplication-config']");
if (!browserConfigLink) {
browserConfigLink = document.createElement("meta");
browserConfigLink.setAttribute("name", "msapplication-config");
document.head.appendChild(browserConfigLink);
}
browserConfigLink.setAttribute("content", browserconfigURL);
})
.catch(error => {
console.error("Failed to resolve msTileIcon URL:", error);
});
},

__startupChecks: function() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ qx.Class.define("osparc.WindowSizeTracker", {
},

statics: {
WIDTH_BREAKPOINT: 1280, // HD 1280x720
HEIGHT_BREAKPOINT: 720, // HD 1280x720
WIDTH_BREAKPOINT: 1180, // - iPad Pro 11" 1194x834 inclusion
HEIGHT_BREAKPOINT: 720, // - iPad Pro 11" 1194x834 inclusion
WIDTH_COMPACT_BREAKPOINT: 1100
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,16 @@ qx.Class.define("osparc.desktop.credits.CreditsIndicatorButton", {
this.addListener("tap", this.__buttonTapped, this);
},


members: {
__creditsContainer: null,
__tappedOut: null,
__tapListener: null,

__buttonTapped: function() {
if (this.__tappedOut) {
this.__tappedOut = false;
return;
if (this.__creditsContainer && this.__creditsContainer.isVisible()) {
this.__hideCreditsContainer();
} else {
this.__showCreditsContainer();
}
this.__showCreditsContainer();
},

__showCreditsContainer: function() {
Expand All @@ -55,40 +54,51 @@ qx.Class.define("osparc.desktop.credits.CreditsIndicatorButton", {
this.__creditsContainer.exclude();
}

const tapListener = event => {
// In case a notification was tapped propagate the event so it can be handled by the NotificationUI
if (osparc.utils.Utils.isMouseOnElement(this.__creditsContainer, event)) {
return;
}
// I somehow can't stop the propagation of the event so workaround:
// If the user tapped on the bell we don't want to show it again
if (osparc.utils.Utils.isMouseOnElement(this, event)) {
this.__tappedOut = true;
}
this.__hideNotifications();
document.removeEventListener("mousedown", tapListener, this);
};
this.__positionCreditsContainer();

// Show the container
this.__creditsContainer.show();

// Add listeners for taps outside the container to hide it
document.addEventListener("mousedown", this.__onTapOutsideMouse.bind(this), true);
},

__positionCreditsContainer: function() {
const bounds = this.getBounds();
const cel = this.getContentElement();
if (cel) {
const domeEle = cel.getDomElement();
if (domeEle) {
const rect = domeEle.getBoundingClientRect();
const domEle = cel.getDomElement();
if (domEle) {
const rect = domEle.getBoundingClientRect();
bounds.left = parseInt(rect.x);
bounds.top = parseInt(rect.y);
}
}
const bottom = bounds.top+bounds.height;
const right = bounds.left+bounds.width;
const bottom = bounds.top + bounds.height;
const right = bounds.left + bounds.width;
this.__creditsContainer.setPosition(right, bottom);
this.__creditsContainer.show();
},

__onTapOutsideMouse: function(event) {
this.__handleOutsideEvent(event);
},

document.addEventListener("mousedown", tapListener, this);
__handleOutsideEvent: function(event) {
if (
!osparc.utils.Utils.isMouseOnElement(this.__creditsContainer, event) &&
!osparc.utils.Utils.isMouseOnElement(this, event)
) {
this.__hideCreditsContainer();
}
},

__hideNotifications: function() {
this.__creditsContainer.exclude();
__hideCreditsContainer: function() {
if (this.__creditsContainer) {
this.__creditsContainer.exclude();
}

// Remove listeners for outside clicks/taps
document.removeEventListener("mousedown", this.__onTapOutsideMouse.bind(this), true);
}
}
});
Loading
Loading