mirror of
https://github.com/Novaloop-AG/healthchat-web.git
synced 2026-11-03 06:55:44 +00:00
fix(mobile-guide): German HealthChat guide for phones, proven in Chromium
Phones and tablets were bounced to the healthchat.ch website, where the visitor had no way back into the app. They now land on the mobile guide again, which pointed at Element X and spoke English while the rest of HealthChat web is German. The guide carries the HealthChat store links and German copy as static HTML, not only via JS: Safari reads the Smart App Banner meta tag at parse time. A charset meta is required now that the page has umlauts, since nginx sends text/html without one. Step 2 and the F-Droid line stay hidden because HealthChat has neither a mobile.element.io deep link nor an F-Droid build. Verified in Chromium at phone width with iPhone and Android user agents: redirect, store links, the way back via the cookie, and the no-JS view.
This commit is contained in:
@@ -12,7 +12,7 @@ import ToastStore from "../stores/ToastStore";
|
|||||||
import SdkConfig from "../SdkConfig";
|
import SdkConfig from "../SdkConfig";
|
||||||
|
|
||||||
const onAccept = (): void => {
|
const onAccept = (): void => {
|
||||||
window.location.href = "https://healthchat.ch";
|
window.location.href = "mobile_guide/";
|
||||||
};
|
};
|
||||||
|
|
||||||
const onReject = (): void => {
|
const onReject = (): void => {
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ async function start(): Promise<void> {
|
|||||||
const isAndroid = /Android/.test(navigator.userAgent);
|
const isAndroid = /Android/.test(navigator.userAgent);
|
||||||
if (isIos || isAndroid) {
|
if (isIos || isAndroid) {
|
||||||
if (document.cookie.indexOf("element_mobile_redirect_to_guide=false") === -1) {
|
if (document.cookie.indexOf("element_mobile_redirect_to_guide=false") === -1) {
|
||||||
window.location.href = "https://healthchat.ch";
|
window.location.href = "mobile_guide/";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,31 +20,31 @@
|
|||||||
<feGaussianBlur stdDeviation="16.3415" result="effect1_foregroundBlur_382_1634"/>
|
<feGaussianBlur stdDeviation="16.3415" result="effect1_foregroundBlur_382_1634"/>
|
||||||
</filter>
|
</filter>
|
||||||
<linearGradient id="paint0_linear_382_1634" x1="141.636" y1="133.796" x2="141.636" y2="32.6829" gradientUnits="userSpaceOnUse">
|
<linearGradient id="paint0_linear_382_1634" x1="141.636" y1="133.796" x2="141.636" y2="32.6829" gradientUnits="userSpaceOnUse">
|
||||||
<stop stop-color="#062993"/>
|
<stop stop-color="#12325C"/>
|
||||||
<stop offset="0.040404" stop-color="#02389D"/>
|
<stop offset="0.040404" stop-color="#143561"/>
|
||||||
<stop offset="0.0808081" stop-color="#0045A6"/>
|
<stop offset="0.0808081" stop-color="#153966"/>
|
||||||
<stop offset="0.121212" stop-color="#0051AD"/>
|
<stop offset="0.121212" stop-color="#173C6C"/>
|
||||||
<stop offset="0.161616" stop-color="#005DB4"/>
|
<stop offset="0.161616" stop-color="#194071"/>
|
||||||
<stop offset="0.20202" stop-color="#0069BA"/>
|
<stop offset="0.20202" stop-color="#1A4376"/>
|
||||||
<stop offset="0.242424" stop-color="#0075BB"/>
|
<stop offset="0.242424" stop-color="#1C467B"/>
|
||||||
<stop offset="0.282828" stop-color="#0081BB"/>
|
<stop offset="0.282828" stop-color="#1D4A80"/>
|
||||||
<stop offset="0.323232" stop-color="#008CB9"/>
|
<stop offset="0.323232" stop-color="#1F4D85"/>
|
||||||
<stop offset="0.363636" stop-color="#0098B7"/>
|
<stop offset="0.363636" stop-color="#225187"/>
|
||||||
<stop offset="0.40404" stop-color="#00A3B3"/>
|
<stop offset="0.40404" stop-color="#255588"/>
|
||||||
<stop offset="0.444444" stop-color="#00AEAD"/>
|
<stop offset="0.444444" stop-color="#28588A"/>
|
||||||
<stop offset="0.484848" stop-color="#00B8A4"/>
|
<stop offset="0.484848" stop-color="#2B5C8B"/>
|
||||||
<stop offset="0.525253" stop-color="#00C2A0"/>
|
<stop offset="0.525253" stop-color="#2E608D"/>
|
||||||
<stop offset="0.565657" stop-color="#00CC99"/>
|
<stop offset="0.565657" stop-color="#31638E"/>
|
||||||
<stop offset="0.606061" stop-color="#3AD396"/>
|
<stop offset="0.606061" stop-color="#346790"/>
|
||||||
<stop offset="0.646465" stop-color="#5DD898"/>
|
<stop offset="0.646465" stop-color="#3B6D93"/>
|
||||||
<stop offset="0.686869" stop-color="#79DD99"/>
|
<stop offset="0.686869" stop-color="#527E9F"/>
|
||||||
<stop offset="0.727273" stop-color="#92E29B"/>
|
<stop offset="0.727273" stop-color="#698FAB"/>
|
||||||
<stop offset="0.767677" stop-color="#A8E69F"/>
|
<stop offset="0.767677" stop-color="#81A0B8"/>
|
||||||
<stop offset="0.808081" stop-color="#BBEAA5"/>
|
<stop offset="0.808081" stop-color="#98B1C4"/>
|
||||||
<stop offset="0.848485" stop-color="#CDEEAE"/>
|
<stop offset="0.848485" stop-color="#AFC2D0"/>
|
||||||
<stop offset="0.888889" stop-color="#DCF2B9"/>
|
<stop offset="0.888889" stop-color="#C6D3DC"/>
|
||||||
<stop offset="0.929293" stop-color="#EAF6C7"/>
|
<stop offset="0.929293" stop-color="#DEE4E9"/>
|
||||||
<stop offset="0.969697" stop-color="#F5FBD5"/>
|
<stop offset="0.969697" stop-color="#F5F5F5"/>
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<clipPath id="clip0_382_1634">
|
<clipPath id="clip0_382_1634">
|
||||||
<rect width="375" height="178" fill="white"/>
|
<rect width="375" height="178" fill="white"/>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
@@ -3,11 +3,12 @@
|
|||||||
Please note: This page is similar to the one at mobile.element.io so any changes made here can potentially
|
Please note: This page is similar to the one at mobile.element.io so any changes made here can potentially
|
||||||
be applied to that site as well. They still share the mobile-apps.ts file for updating the app metadata.
|
be applied to that site as well. They still share the mobile-apps.ts file for updating the app metadata.
|
||||||
-->
|
-->
|
||||||
<html lang="en">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<title>Element Mobile Guide</title>
|
<meta charset="utf-8" />
|
||||||
|
<title>HealthChat für Smartphone und Tablet</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="apple-itunes-app" content="app-id=id1631335820" />
|
<meta name="apple-itunes-app" content="app-id=id6743060078" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -19,10 +20,15 @@
|
|||||||
<header>
|
<header>
|
||||||
<div class="mx_HomePage_header">
|
<div class="mx_HomePage_header">
|
||||||
<div>
|
<div>
|
||||||
<img src="./assets/element-logo.svg" alt="Element Logo" width="148px" height="32px" />
|
<img
|
||||||
|
src="../themes/element/img/logos/element-logo.png"
|
||||||
|
alt="HealthChat Logo"
|
||||||
|
width="32px"
|
||||||
|
height="32px"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p id="header_title">Join Element.</p>
|
<p id="header_title">HealthChat auf dem Smartphone</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
@@ -31,8 +37,8 @@
|
|||||||
<main>
|
<main>
|
||||||
<div class="mx_HomePage_col mx_Spacer">
|
<div class="mx_HomePage_col mx_Spacer">
|
||||||
<p>
|
<p>
|
||||||
The <a id="back_to_element_button" href="#" class="mx_DesktopLink">desktop site</a> does
|
Die Web-Version ist für den Desktop gedacht und funktioniert auf Smartphone und Tablet
|
||||||
<b>not</b> work on mobile, please download the app.
|
<b>nicht</b>. Für Smartphone und Tablet gibt es die HealthChat-App.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="mx_HomePage_col mx_Spacer">
|
<div class="mx_HomePage_col mx_Spacer">
|
||||||
@@ -41,44 +47,38 @@
|
|||||||
<span>1</span>
|
<span>1</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h1 id="step1_heading">Download Element</h1>
|
<h1 id="step1_heading">HealthChat-App laden</h1>
|
||||||
<div class="mx_StoreLinks">
|
<div class="mx_StoreLinks">
|
||||||
<a
|
<a
|
||||||
href="https://apps.apple.com/app/element-x-secure-chat-call/id1631335820"
|
href="https://apps.apple.com/ch/app/healthchat/id6743060078"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="mx_StoreBadge"
|
class="mx_StoreBadge"
|
||||||
id="app_store_link"
|
id="app_store_link"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="./assets/app-store-badge.svg"
|
src="./assets/app-store-badge.svg"
|
||||||
alt="Download on the App Store."
|
alt="Im App Store laden."
|
||||||
width="120px"
|
width="120px"
|
||||||
height="40px"
|
height="40px"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="https://play.google.com/store/apps/details?id=io.element.android.x"
|
href="https://play.google.com/store/apps/details?id=ch.healthchat.element"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="mx_StoreBadge"
|
class="mx_StoreBadge"
|
||||||
id="play_store_link"
|
id="play_store_link"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="./assets/google-play-badge.svg"
|
src="./assets/google-play-badge.svg"
|
||||||
alt="Get it on Google Play."
|
alt="Jetzt bei Google Play."
|
||||||
width="135px"
|
width="135px"
|
||||||
height="40px"
|
height="40px"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<p id="f_droid_section">
|
<p id="f_droid_section" style="display: none">
|
||||||
Also available on
|
Auch verfügbar auf
|
||||||
<a
|
<a href="#" target="_blank" class="mx_ClearDecoration" id="f_droid_link">F-Droid</a>
|
||||||
href="https://f-droid.org/packages/io.element.android.x"
|
|
||||||
target="_blank"
|
|
||||||
class="mx_ClearDecoration"
|
|
||||||
id="f_droid_link"
|
|
||||||
>F-Droid</a
|
|
||||||
>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -89,12 +89,19 @@
|
|||||||
<span>2</span>
|
<span>2</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h1>Come back here to sign in</h1>
|
<h1>Danach hier anmelden</h1>
|
||||||
<p id="step2_description">This will set up your app</p>
|
<p id="step2_description">Damit wird die App eingerichtet</p>
|
||||||
<a class="mx_Button" id="deep_link_button" href="#">Sign in</a>
|
<a class="mx_Button" id="deep_link_button" href="#">Anmelden</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mx_HomePage_col mx_Spacer">
|
||||||
|
<p>
|
||||||
|
<a id="back_to_element_button" href="#" class="mx_DesktopLink"
|
||||||
|
>Trotzdem die Web-Version öffnen</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ function onBackToElementClick(): void {
|
|||||||
// NEVER pass user-controlled content to this function! Hardcoded strings only please.
|
// NEVER pass user-controlled content to this function! Hardcoded strings only please.
|
||||||
function renderConfigError(message: string): void {
|
function renderConfigError(message: string): void {
|
||||||
const contactMsg =
|
const contactMsg =
|
||||||
"If this is unexpected, please contact your system administrator " + "or technical support representative.";
|
"Falls das unerwartet ist, bitte den Systemadministrator " + "oder den technischen Support kontaktieren.";
|
||||||
message = `<h2>Error loading Element</h2><p>${message}</p><p>${contactMsg}</p>`;
|
message = `<h2>HealthChat konnte nicht geladen werden</h2><p>${message}</p><p>${contactMsg}</p>`;
|
||||||
|
|
||||||
const toHide = document.getElementsByClassName("mx_HomePage_container");
|
const toHide = document.getElementsByClassName("mx_HomePage_container");
|
||||||
const errorContainers = document.getElementsByClassName(
|
const errorContainers = document.getElementsByClassName(
|
||||||
@@ -59,12 +59,12 @@ async function initPage(): Promise<void> {
|
|||||||
const incompatibleOptions = [wkConfig, serverName, defaultHsUrl].filter((i) => !!i);
|
const incompatibleOptions = [wkConfig, serverName, defaultHsUrl].filter((i) => !!i);
|
||||||
if (defaultHsUrl && (wkConfig || serverName)) {
|
if (defaultHsUrl && (wkConfig || serverName)) {
|
||||||
return renderConfigError(
|
return renderConfigError(
|
||||||
"Invalid configuration: a default_hs_url can't be specified along with default_server_name " +
|
"Ungültige Konfiguration: default_hs_url kann nicht zusammen mit default_server_name " +
|
||||||
"or default_server_config",
|
"oder default_server_config angegeben werden",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (incompatibleOptions.length < 1) {
|
if (incompatibleOptions.length < 1) {
|
||||||
return renderConfigError("Invalid configuration: no default server specified.");
|
return renderConfigError("Ungültige Konfiguration: kein Standard-Server angegeben.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let hsUrl: string | undefined;
|
let hsUrl: string | undefined;
|
||||||
@@ -100,7 +100,7 @@ async function initPage(): Promise<void> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.error(e);
|
logger.error(e);
|
||||||
return renderConfigError("Unable to fetch homeserver configuration");
|
return renderConfigError("Homeserver-Konfiguration konnte nicht geladen werden");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,7 +111,7 @@ async function initPage(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!hsUrl) {
|
if (!hsUrl) {
|
||||||
return renderConfigError("Unable to locate homeserver");
|
return renderConfigError("Homeserver konnte nicht gefunden werden");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hsUrl && !hsUrl.endsWith("/")) hsUrl += "/";
|
if (hsUrl && !hsUrl.endsWith("/")) hsUrl += "/";
|
||||||
@@ -131,7 +131,7 @@ async function initPage(): Promise<void> {
|
|||||||
// Not part of updateMobilePage as the link is only shown on mobile_guide and not on mobile.element.io
|
// Not part of updateMobilePage as the link is only shown on mobile_guide and not on mobile.element.io
|
||||||
document.getElementById("back_to_element_button")!.onclick = onBackToElementClick;
|
document.getElementById("back_to_element_button")!.onclick = onBackToElementClick;
|
||||||
|
|
||||||
updateMobilePage(metadata, deepLinkUrl, serverName ?? hsUrl);
|
updateMobilePage(metadata, deepLinkUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void initPage();
|
void initPage();
|
||||||
|
|||||||
@@ -38,12 +38,11 @@ export const mobileApps: Record<MobileAppVariant, MobileAppMetadata> = {
|
|||||||
isProApp: false,
|
isProApp: false,
|
||||||
},
|
},
|
||||||
[MobileAppVariant.X]: {
|
[MobileAppVariant.X]: {
|
||||||
name: "Element X",
|
name: "HealthChat",
|
||||||
appleAppId: "id1631335820",
|
appleAppId: "id6743060078",
|
||||||
appStoreUrl: "https://apps.apple.com/app/element-x-secure-chat-call/id1631335820",
|
appStoreUrl: "https://apps.apple.com/ch/app/healthchat/id6743060078",
|
||||||
playStoreUrl: "https://play.google.com/store/apps/details?id=io.element.android.x",
|
playStoreUrl: "https://play.google.com/store/apps/details?id=ch.healthchat.element",
|
||||||
fDroidUrl: "https://f-droid.org/packages/io.element.android.x",
|
deepLinkPath: "",
|
||||||
deepLinkPath: "/element",
|
|
||||||
usesLegacyDeepLink: false,
|
usesLegacyDeepLink: false,
|
||||||
isProApp: false,
|
isProApp: false,
|
||||||
},
|
},
|
||||||
@@ -58,18 +57,17 @@ export const mobileApps: Record<MobileAppVariant, MobileAppMetadata> = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export function updateMobilePage(metadata: MobileAppMetadata, deepLinkUrl: string, server: string | undefined): void {
|
export function updateMobilePage(metadata: MobileAppMetadata, deepLinkUrl: string): void {
|
||||||
const appleMeta = document.querySelector('meta[name="apple-itunes-app"]') as Element;
|
const appleMeta = document.querySelector('meta[name="apple-itunes-app"]') as Element;
|
||||||
appleMeta.setAttribute("content", `app-id=${metadata.appleAppId}`);
|
appleMeta.setAttribute("content", `app-id=${metadata.appleAppId}`);
|
||||||
|
|
||||||
if (server) {
|
(document.getElementById("header_title") as HTMLHeadingElement).innerText = `${metadata.name} auf dem Smartphone`;
|
||||||
(document.getElementById("header_title") as HTMLHeadingElement).innerText = `Join ${server} on Element`;
|
|
||||||
}
|
|
||||||
(document.getElementById("app_store_link") as HTMLAnchorElement).href = metadata.appStoreUrl;
|
(document.getElementById("app_store_link") as HTMLAnchorElement).href = metadata.appStoreUrl;
|
||||||
(document.getElementById("play_store_link") as HTMLAnchorElement).href = metadata.playStoreUrl;
|
(document.getElementById("play_store_link") as HTMLAnchorElement).href = metadata.playStoreUrl;
|
||||||
|
|
||||||
if (metadata.fDroidUrl) {
|
if (metadata.fDroidUrl) {
|
||||||
(document.getElementById("f_droid_link") as HTMLAnchorElement).href = metadata.fDroidUrl;
|
(document.getElementById("f_droid_link") as HTMLAnchorElement).href = metadata.fDroidUrl;
|
||||||
|
document.getElementById("f_droid_section")!.style.display = "";
|
||||||
} else {
|
} else {
|
||||||
document.getElementById("f_droid_section")!.style.display = "none";
|
document.getElementById("f_droid_section")!.style.display = "none";
|
||||||
}
|
}
|
||||||
@@ -77,11 +75,12 @@ export function updateMobilePage(metadata: MobileAppMetadata, deepLinkUrl: strin
|
|||||||
const step1Heading = document.getElementById("step1_heading")!;
|
const step1Heading = document.getElementById("step1_heading")!;
|
||||||
step1Heading.innerHTML = step1Heading!.innerHTML.replace("Element", metadata.name);
|
step1Heading.innerHTML = step1Heading!.innerHTML.replace("Element", metadata.name);
|
||||||
|
|
||||||
// Step 2 is only shown on the mobile guide, not on mobile.element.io
|
// Step 2 is only shown on the mobile guide, not on mobile.element.io,
|
||||||
if (document.getElementById("step2_container")) {
|
// and only for apps that offer a sign-in deep link
|
||||||
|
if (document.getElementById("step2_container") && (metadata.deepLinkPath || metadata.usesLegacyDeepLink)) {
|
||||||
document.getElementById("step2_container")!.style.display = "block";
|
document.getElementById("step2_container")!.style.display = "block";
|
||||||
if (metadata.isProApp) {
|
if (metadata.isProApp) {
|
||||||
document.getElementById("step2_description")!.innerHTML = "Use your work email to join";
|
document.getElementById("step2_description")!.innerHTML = "Mit der Arbeits-E-Mail-Adresse anmelden";
|
||||||
}
|
}
|
||||||
(document.getElementById("deep_link_button") as HTMLAnchorElement).href = deepLinkUrl;
|
(document.getElementById("deep_link_button") as HTMLAnchorElement).href = deepLinkUrl;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user