diff --git a/src/app.tsx b/src/app.tsx index 9182f732..f35c2259 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,25 +1,14 @@ -import React, { useContext, useEffect } from 'react' +import React, { useContext } from 'react' import Config from './components/config.tsx' import { ConfigContext } from './context/config-context.tsx' import HelperUi from './helper-ui.tsx' import { isConfigPage } from './lib/is-config-page.ts' -import { isPathOrSubdomainRequest, findOriginIsolationRedirect } from './lib/path-or-subdomain.ts' +import { isPathOrSubdomainRequest } from './lib/path-or-subdomain.ts' import RedirectPage from './redirectPage.tsx' function App (): JSX.Element { const { isConfigExpanded, setConfigExpanded } = useContext(ConfigContext) - useEffect(() => { - async function originEnforcement (): Promise { - // enforce early when loaded before SW was registered - const originRedirect = await findOriginIsolationRedirect(window.location) - if (originRedirect !== null) { - window.location.replace(originRedirect) - } - } - void originEnforcement() - }, []) - if (isConfigPage()) { setConfigExpanded(true) return diff --git a/src/context/service-worker-context.tsx b/src/context/service-worker-context.tsx index c58db779..dce50eb0 100644 --- a/src/context/service-worker-context.tsx +++ b/src/context/service-worker-context.tsx @@ -1,5 +1,21 @@ +/** + * @file This file contains the ServiceWorkerProvider component which is used to register the service worker, + * and provide the isServiceWorkerRegistered state to the rest of the app. + * + * URL / location logic dependent upon the service worker being registered should be handled here. Some examples of this are: + * + * Before the Service Worker is registered (e.g. first requests to root hosted domain or subdomains): + * + * 1. Being redirected from _redirects file to a ?helia-sw= url + * 2. The app is loaded because service worker is not yet registered, we need to reload the page so the service worker intercepts the request + * + * After the service worker is loaded. Usually any react code isn't loaded, but some edge cases are: + * 1. The page being loaded using some /ip[fn]s/ url, but subdomain isolation is supported, so we need to redirect to the isolated origin + */ import React, { createContext, useEffect, useState } from 'react' +import { translateIpfsRedirectUrl } from '../lib/ipfs-hosted-redirect-utils.ts' import { error } from '../lib/logger.ts' +import { findOriginIsolationRedirect } from '../lib/path-or-subdomain.ts' import { registerServiceWorker } from '../service-worker-utils.ts' export const ServiceWorkerContext = createContext({ @@ -9,8 +25,32 @@ export const ServiceWorkerContext = createContext({ export const ServiceWorkerProvider = ({ children }): JSX.Element => { const [isServiceWorkerRegistered, setIsServiceWorkerRegistered] = useState(false) + const windowLocation = translateIpfsRedirectUrl(window.location.href) + useEffect(() => { if (isServiceWorkerRegistered) { + /** + * The service worker is registered, now we need to check for "helia-sw" and origin isolation support + */ + if (windowLocation.href !== window.location.href) { + /** + * We're at a domain with ?helia-sw=, we can reload the page so the service worker will + * capture the request + */ + window.location.replace(windowLocation.href) + } else { + /** + * ?helia-sw= url handling is done, now we can check for origin isolation redirects + */ + void findOriginIsolationRedirect(windowLocation).then((originRedirect) => { + if (originRedirect !== null) { + window.location.replace(originRedirect) + } + }) + } + /** + * The service worker is registered, we don't need to do any more work + */ return } async function doWork (): Promise { @@ -32,7 +72,7 @@ export const ServiceWorkerProvider = ({ children }): JSX.Element => { } } void doWork() - }, []) + }, [isServiceWorkerRegistered]) return ( diff --git a/src/lib/ipfs-hosted-redirect-utils.ts b/src/lib/ipfs-hosted-redirect-utils.ts new file mode 100644 index 00000000..d56da03d --- /dev/null +++ b/src/lib/ipfs-hosted-redirect-utils.ts @@ -0,0 +1,15 @@ +/** + * If you host helia-service-worker-gateway on an IPFS domain, the redirects file will route some requests from + * `/` to `https:///?helia-sw=`. + * + * This function will check for "?helia-sw=" in the URL and modify the URL so that it works with the rest of our logic + */ +export function translateIpfsRedirectUrl (urlString: string): URL { + const url = new URL(urlString) + const heliaSw = url.searchParams.get('helia-sw') + if (heliaSw != null) { + url.searchParams.delete('helia-sw') + url.pathname = heliaSw + } + return url +}