From 1b671f4cbde59968738ef7bba16b420ec3347d1c Mon Sep 17 00:00:00 2001 From: Reina To Date: Mon, 16 Feb 2026 14:17:05 -0500 Subject: [PATCH] Fix IOS opening in safari --- bifrost/lib/hardRedirect.ts | 24 +++++++++++++++++++ .../renderer/wrapped/onBeforeRender.client.ts | 18 ++++++++------ 2 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 bifrost/lib/hardRedirect.ts diff --git a/bifrost/lib/hardRedirect.ts b/bifrost/lib/hardRedirect.ts new file mode 100644 index 0000000..a297af6 --- /dev/null +++ b/bifrost/lib/hardRedirect.ts @@ -0,0 +1,24 @@ +/** + * Hard redirect (full page reload) that converts same-origin URLs to relative paths. + * Turbolinks IOS requires relative URLs to open within the mobile app instead of safari. + */ +export async function hardRedirect(url: string) { + if (url.startsWith("/")) { + window.location.href = url; + await new Promise(() => {}); + return; + } + + const parsedUrl = new URL(url); + + if (window.location.origin === parsedUrl.origin) { + // Relative redirect + window.location.href = parsedUrl.pathname + parsedUrl.search + parsedUrl.hash; + } else { + // External redirect + window.location.href = url; + } + + // stop vike rendering to let navigation happen + await new Promise(() => {}); +} diff --git a/bifrost/renderer/wrapped/onBeforeRender.client.ts b/bifrost/renderer/wrapped/onBeforeRender.client.ts index 929e845..681d75f 100644 --- a/bifrost/renderer/wrapped/onBeforeRender.client.ts +++ b/bifrost/renderer/wrapped/onBeforeRender.client.ts @@ -1,6 +1,7 @@ import "../../lib/type"; import type { PageContextClient } from "vike/types"; import { redirect } from "vike/abort"; +import { hardRedirect } from "../../lib/hardRedirect"; import { getElementAttributes } from "../../lib/elementUtils"; // onBeforeRender runs before changing the browser location, so `throw redirect` works @@ -55,9 +56,7 @@ export default async function wrappedOnBeforeRender( if (!resp) { // hard reload. can happen on cors errors when redirected to external page - window.location.href = pageContext.urlParsed.href; - // stop vike rendering to let navigation happen - await new Promise(() => {}); + await hardRedirect(pageContext.urlParsed.href); return; } @@ -71,12 +70,16 @@ export default async function wrappedOnBeforeRender( throw redirect(parsedUrl.pathname + parsedUrl.search + parsedUrl.hash); } else { // external redirect - throw redirect(resp.url); + await hardRedirect(resp.url); + return; } } + if (!resp.ok) { - throw redirect(resp.url); + await hardRedirect(resp.url); + return; } + const html = await resp.text(); const layoutInfo = pageContext.config.getLayout!( Object.fromEntries(resp.headers.entries()) @@ -84,8 +87,9 @@ export default async function wrappedOnBeforeRender( if (!layoutInfo) { // Fallback to full reload if layout not found // window.location.href = resp.url; - throw redirect(resp.url); - } + await hardRedirect(resp.url); + return; + } const parsed = document.createElement("html"); parsed.innerHTML = html;