From 51419e2d3b75856efd3249509828dec7a05d21bb Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Fri, 26 Jun 2026 18:47:34 +0200 Subject: [PATCH] App: allow native deep links from iOS widget (#31242) --- assets/js/app.ts | 5 +++-- assets/js/utils/native.ts | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/assets/js/app.ts b/assets/js/app.ts index bef8d56a0..0f68c9be8 100644 --- a/assets/js/app.ts +++ b/assets/js/app.ts @@ -79,15 +79,16 @@ applyUrlSettings(); const i18n = setupI18n(); const head = createHead(); +const router = setupRouter(i18n.global); app.use(i18n); -app.use(setupRouter(i18n.global)); +app.use(router); app.use(head); app.mixin(VueHeadMixin); window.app = app.mount("#app"); watchThemeChanges(); -appDetection(); +appDetection(router); if (window.evcc.customCss === "true") { const link = document.createElement("link"); diff --git a/assets/js/utils/native.ts b/assets/js/utils/native.ts index b378a84a8..bfc363d3d 100644 --- a/assets/js/utils/native.ts +++ b/assets/js/utils/native.ts @@ -1,23 +1,48 @@ +import type { Router } from "vue-router"; + +let router: Router; + export function isApp() { return navigator.userAgent.includes("evcc/"); } -export function appDetection() { - if (isApp()) { - const $html = document.querySelector("html"); - $html?.classList.add("app"); +type FromAppMessage = { + type: "navigate"; + path: string; // e.g. "/forecast", "/?lp=1" +}; + +function receiveFromApp(message: FromAppMessage) { + switch (message.type) { + case "navigate": + router.push(message.path); } } +export function appDetection(r: Router) { + router = r; + if (!isApp()) return; + document.querySelector("html")?.classList.add("app"); + + const handler = (event: Event) => { + try { + receiveFromApp(JSON.parse((event as MessageEvent).data)); + } catch { + return; + } + }; + window.addEventListener("message", handler); // iOS + document.addEventListener("message", handler); // Android +} + export function hasAppCapability(capability: string): boolean { return isApp() && window.evccAppCapabilities?.includes(capability) === true; } -type AppMessage = +type ToAppMessage = | { type: "online" | "offline" | "settings" } | { type: "download"; url: string; headers?: Record }; -export function sendToApp(data: AppMessage) { +export function sendToApp(data: ToAppMessage) { window.ReactNativeWebView?.postMessage(JSON.stringify(data)); }