import { getToolbarSrc, cookie as prismicCookie } from "@prismicio/client" import { useRouter } from "next/router" import Script from "next/script" import type { FC } from "react" import { type ReactNode, useEffect } from "react" /** Props for ``. */ export type PrismicPreviewProps = { /** * The name of your Prismic repository. A Prismic Toolbar will be registered using this * repository. */ repositoryName: string /** * The URL of your app's Prismic preview endpoint (default: `/api/preview`). This URL will be * fetched on preview update events. */ updatePreviewURL?: string /** * The URL of your app's exit preview endpoint (default: `/api/exit-preview`). This URL will be * fetched on preview exit events. */ exitPreviewURL?: string /** Children to render adjacent to the Prismic Toolbar. */ children?: ReactNode } /** * React component that sets up Prismic Previews using the Prismic Toolbar. When the Prismic Toolbar * send events to the browser, such as on preview updates and exiting, this component will * automatically refresh the page with the changes. * * This component can be wrapped around your app or added anywhere in your app's tree. It must be * rendered on every page. */ export const PrismicPreview: FC = (props) => { const { repositoryName, updatePreviewURL = "/api/preview", exitPreviewURL = "/api/exit-preview", children, } = props const router = useRouter() const toolbarSrc = getToolbarSrc(repositoryName) useEffect(() => { const controller = new AbortController() window.addEventListener("prismicPreviewUpdate", onUpdate, { signal: controller.signal, }) window.addEventListener("prismicPreviewEnd", onEnd, { signal: controller.signal, }) // Start the preview for preview share links. Previews from // share links do not go to the `updatePreviewURL` like a normal // preview. // // We check that the current URL is a descendant of the base // path to prevent infinite refrehes. if ( window.location.href.startsWith(window.location.origin + router.basePath) && getPreviewCookieRepositoryName() === repositoryName && !router.isPreview ) { start() } function onEnd(event: Event) { event.preventDefault() fetch(router.basePath + exitPreviewURL, { signal: controller.signal }) .then((res) => { if (!res.ok) { console.error( `[] Failed to exit Preview Mode using the "${exitPreviewURL}" API endpoint. Does it exist?`, ) return } refresh() }) .catch(() => {}) } function onUpdate(event: Event) { event.preventDefault() start() } function start() { // We check `opaqueredirect` because we don't care if // the redirect was successful or not. As long as it // redirects, we know the endpoint exists and at least // attempted to set preview data. fetch(router.basePath + updatePreviewURL, { redirect: "manual", signal: controller.signal, }) .then((res) => { if (res.type !== "opaqueredirect") { console.error( `[] Failed to start or update the preview using "${updatePreviewURL}". Does it exist?`, ) return } refresh() }) .catch(() => {}) } function refresh() { router.replace(router.asPath, undefined, { scroll: false }) } return () => controller.abort() }, [exitPreviewURL, updatePreviewURL, repositoryName, router]) return ( <> {children}