diff --git a/bifrost/lib/hardNavigate.ts b/bifrost/lib/hardNavigate.ts new file mode 100644 index 0000000..421cfc0 --- /dev/null +++ b/bifrost/lib/hardNavigate.ts @@ -0,0 +1,10 @@ +/** + * Hard navigate to a URL using `history.pushState` and `window.location.reload` instead of + * `window.location.href`. This prevents the mobile apps from opening the URL in a browser tab. + */ +export async function hardNavigate(url: string): Promise { + history.pushState(null, "", url); + window.Turbolinks.controller.viewInvalidated(); + // stop vike rendering to let navigation happen + await new Promise(() => {}); +} diff --git a/bifrost/lib/hardRedirect.ts b/bifrost/lib/hardRedirect.ts deleted file mode 100644 index a297af6..0000000 --- a/bifrost/lib/hardRedirect.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 681d75f..9c552af 100644 --- a/bifrost/renderer/wrapped/onBeforeRender.client.ts +++ b/bifrost/renderer/wrapped/onBeforeRender.client.ts @@ -1,8 +1,8 @@ import "../../lib/type"; import type { PageContextClient } from "vike/types"; import { redirect } from "vike/abort"; -import { hardRedirect } from "../../lib/hardRedirect"; import { getElementAttributes } from "../../lib/elementUtils"; +import { hardNavigate } from "../../lib/hardNavigate"; // onBeforeRender runs before changing the browser location, so `throw redirect` works // we wait for onBeforeRenderClient to call mergeHead, which runs after browser location change @@ -56,7 +56,9 @@ export default async function wrappedOnBeforeRender( if (!resp) { // hard reload. can happen on cors errors when redirected to external page - await hardRedirect(pageContext.urlParsed.href); + window.location.href = pageContext.urlParsed.href; + // stop vike rendering to let navigation happen + await new Promise(() => {}); return; } @@ -70,26 +72,21 @@ export default async function wrappedOnBeforeRender( throw redirect(parsedUrl.pathname + parsedUrl.search + parsedUrl.hash); } else { // external redirect - await hardRedirect(resp.url); - return; + throw redirect(resp.url); } } - if (!resp.ok) { - await hardRedirect(resp.url); - return; + await hardNavigate(resp.url); } - const html = await resp.text(); const layoutInfo = pageContext.config.getLayout!( Object.fromEntries(resp.headers.entries()) ); if (!layoutInfo) { // Fallback to full reload if layout not found - // window.location.href = resp.url; - await hardRedirect(resp.url); + await hardNavigate(resp.url); return; - } + } const parsed = document.createElement("html"); parsed.innerHTML = html;