From b1e36ff9591e304104f2f948dd37bfd4f301ed72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl?= Date: Sun, 25 Oct 2026 09:49:42 +0100 Subject: [PATCH] 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. --- apps/web/src/toasts/MobileGuideToast.ts | 2 +- apps/web/src/vector/index.ts | 2 +- .../mobile_guide/assets/bottom-gradient.svg | 50 ++++++++--------- apps/web/src/vector/mobile_guide/index.html | 55 +++++++++++-------- apps/web/src/vector/mobile_guide/index.ts | 16 +++--- .../src/vector/mobile_guide/mobile-apps.ts | 25 ++++----- 6 files changed, 78 insertions(+), 72 deletions(-) diff --git a/apps/web/src/toasts/MobileGuideToast.ts b/apps/web/src/toasts/MobileGuideToast.ts index f4dd61f32f..566ea6aa93 100644 --- a/apps/web/src/toasts/MobileGuideToast.ts +++ b/apps/web/src/toasts/MobileGuideToast.ts @@ -12,7 +12,7 @@ import ToastStore from "../stores/ToastStore"; import SdkConfig from "../SdkConfig"; const onAccept = (): void => { - window.location.href = "https://healthchat.ch"; + window.location.href = "mobile_guide/"; }; const onReject = (): void => { diff --git a/apps/web/src/vector/index.ts b/apps/web/src/vector/index.ts index 70144683a7..6a01f32e53 100644 --- a/apps/web/src/vector/index.ts +++ b/apps/web/src/vector/index.ts @@ -149,7 +149,7 @@ async function start(): Promise { const isAndroid = /Android/.test(navigator.userAgent); if (isIos || isAndroid) { if (document.cookie.indexOf("element_mobile_redirect_to_guide=false") === -1) { - window.location.href = "https://healthchat.ch"; + window.location.href = "mobile_guide/"; return; } } diff --git a/apps/web/src/vector/mobile_guide/assets/bottom-gradient.svg b/apps/web/src/vector/mobile_guide/assets/bottom-gradient.svg index 0740440946..88be906d98 100644 --- a/apps/web/src/vector/mobile_guide/assets/bottom-gradient.svg +++ b/apps/web/src/vector/mobile_guide/assets/bottom-gradient.svg @@ -20,31 +20,31 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/src/vector/mobile_guide/index.html b/apps/web/src/vector/mobile_guide/index.html index 3bd8a8e20d..979b8bca0c 100644 --- a/apps/web/src/vector/mobile_guide/index.html +++ b/apps/web/src/vector/mobile_guide/index.html @@ -3,11 +3,12 @@ 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. --> - + - Element Mobile Guide + + HealthChat für Smartphone und Tablet - + @@ -19,10 +20,15 @@
- Element Logo + HealthChat Logo
-

Join Element.

+

HealthChat auf dem Smartphone


@@ -31,8 +37,8 @@

- The desktop site does - not work on mobile, please download the app. + Die Web-Version ist für den Desktop gedacht und funktioniert auf Smartphone und Tablet + nicht. Für Smartphone und Tablet gibt es die HealthChat-App.

@@ -41,44 +47,38 @@ 1
-

Download Element

+

HealthChat-App laden

-

- Also available on - F-Droid +

@@ -89,12 +89,19 @@ 2
-

Come back here to sign in

-

This will set up your app

- Sign in +

Danach hier anmelden

+

Damit wird die App eingerichtet

+ Anmelden
+
diff --git a/apps/web/src/vector/mobile_guide/index.ts b/apps/web/src/vector/mobile_guide/index.ts index e9732b4035..d166072b8a 100644 --- a/apps/web/src/vector/mobile_guide/index.ts +++ b/apps/web/src/vector/mobile_guide/index.ts @@ -23,8 +23,8 @@ function onBackToElementClick(): void { // NEVER pass user-controlled content to this function! Hardcoded strings only please. function renderConfigError(message: string): void { const contactMsg = - "If this is unexpected, please contact your system administrator " + "or technical support representative."; - message = `

Error loading Element

${message}

${contactMsg}

`; + "Falls das unerwartet ist, bitte den Systemadministrator " + "oder den technischen Support kontaktieren."; + message = `

HealthChat konnte nicht geladen werden

${message}

${contactMsg}

`; const toHide = document.getElementsByClassName("mx_HomePage_container"); const errorContainers = document.getElementsByClassName( @@ -59,12 +59,12 @@ async function initPage(): Promise { const incompatibleOptions = [wkConfig, serverName, defaultHsUrl].filter((i) => !!i); if (defaultHsUrl && (wkConfig || serverName)) { return renderConfigError( - "Invalid configuration: a default_hs_url can't be specified along with default_server_name " + - "or default_server_config", + "Ungültige Konfiguration: default_hs_url kann nicht zusammen mit default_server_name " + + "oder default_server_config angegeben werden", ); } 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; @@ -100,7 +100,7 @@ async function initPage(): Promise { } } else { 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 { } if (!hsUrl) { - return renderConfigError("Unable to locate homeserver"); + return renderConfigError("Homeserver konnte nicht gefunden werden"); } if (hsUrl && !hsUrl.endsWith("/")) hsUrl += "/"; @@ -131,7 +131,7 @@ async function initPage(): Promise { // 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; - updateMobilePage(metadata, deepLinkUrl, serverName ?? hsUrl); + updateMobilePage(metadata, deepLinkUrl); } void initPage(); diff --git a/apps/web/src/vector/mobile_guide/mobile-apps.ts b/apps/web/src/vector/mobile_guide/mobile-apps.ts index ff3430d6b7..9bf410d87f 100644 --- a/apps/web/src/vector/mobile_guide/mobile-apps.ts +++ b/apps/web/src/vector/mobile_guide/mobile-apps.ts @@ -38,12 +38,11 @@ export const mobileApps: Record = { isProApp: false, }, [MobileAppVariant.X]: { - name: "Element X", - appleAppId: "id1631335820", - appStoreUrl: "https://apps.apple.com/app/element-x-secure-chat-call/id1631335820", - playStoreUrl: "https://play.google.com/store/apps/details?id=io.element.android.x", - fDroidUrl: "https://f-droid.org/packages/io.element.android.x", - deepLinkPath: "/element", + name: "HealthChat", + appleAppId: "id6743060078", + appStoreUrl: "https://apps.apple.com/ch/app/healthchat/id6743060078", + playStoreUrl: "https://play.google.com/store/apps/details?id=ch.healthchat.element", + deepLinkPath: "", usesLegacyDeepLink: false, isProApp: false, }, @@ -58,18 +57,17 @@ export const mobileApps: Record = { }, }; -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; appleMeta.setAttribute("content", `app-id=${metadata.appleAppId}`); - if (server) { - (document.getElementById("header_title") as HTMLHeadingElement).innerText = `Join ${server} on Element`; - } + (document.getElementById("header_title") as HTMLHeadingElement).innerText = `${metadata.name} auf dem Smartphone`; (document.getElementById("app_store_link") as HTMLAnchorElement).href = metadata.appStoreUrl; (document.getElementById("play_store_link") as HTMLAnchorElement).href = metadata.playStoreUrl; if (metadata.fDroidUrl) { (document.getElementById("f_droid_link") as HTMLAnchorElement).href = metadata.fDroidUrl; + document.getElementById("f_droid_section")!.style.display = ""; } else { 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")!; step1Heading.innerHTML = step1Heading!.innerHTML.replace("Element", metadata.name); - // Step 2 is only shown on the mobile guide, not on mobile.element.io - if (document.getElementById("step2_container")) { + // Step 2 is only shown on the mobile guide, not on mobile.element.io, + // 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"; 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; }