-
Notifications
You must be signed in to change notification settings - Fork 7
/
PrismicPreviewClient.tsx
156 lines (130 loc) · 4.29 KB
/
PrismicPreviewClient.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
"use client";
import { useEffect } from "react";
import { useRouter as usePagesRouter } from "next/router";
import { useRouter } from "next/navigation";
import { getPrismicPreviewCookie } from "./lib/getPrismicPreviewCookie";
import { getPreviewCookieRepositoryName } from "./lib/getPreviewCookieRepositoryName";
import { PrismicPreviewProps } from "./PrismicPreview";
type PrismicPreviewClientProps = Omit<PrismicPreviewProps, "children"> & {
isDraftMode: boolean;
};
export function PrismicPreviewClient({
repositoryName,
updatePreviewURL = "/api/preview",
exitPreviewURL = "/api/exit-preview",
isDraftMode,
}: PrismicPreviewClientProps): null {
let isPreviewActive = isDraftMode;
let isAppRouter = true;
let basePath = "";
let refresh: () => void;
try {
// eslint-disable-next-line react-hooks/rules-of-hooks
const router = usePagesRouter();
isAppRouter = false;
basePath = router.basePath;
isPreviewActive ||= router.isPreview;
refresh = () => router.replace(router.asPath, undefined, { scroll: false });
} catch {
// Assume we are in App Router. Ignore the error.
// eslint-disable-next-line react-hooks/rules-of-hooks
const router = useRouter();
refresh = router.refresh;
}
useEffect(() => {
/**
* Starts Preview Mode and refreshes the page's props.
*/
const startPreviewMode = async () => {
const resolvedUpdatePreviewURL = basePath + updatePreviewURL;
// Start Next.js Preview Mode via the given preview API endpoint.
const res = await globalThis.fetch(resolvedUpdatePreviewURL);
// We check for `res.redirected` rather than `res.ok`
// since the update preview endpoint may redirect to a
// 404 page. As long as it redirects, we know the
// endpoint exists and at least attempted to set
// preview data.
if (res.redirected) {
refresh();
} else {
console.error(
`[<PrismicPreview>] Failed to start or update Preview Mode using the "${resolvedUpdatePreviewURL}" API endpoint. Does it exist?`,
);
}
};
const handlePrismicPreviewUpdate = async (event: Event) => {
// Prevent the toolbar from reloading the page.
event.preventDefault();
if (isAppRouter) {
refresh();
} else {
await startPreviewMode();
}
};
const handlePrismicPreviewEnd = async (event: Event) => {
// Prevent the toolbar from reloading the page.
event.preventDefault();
const resolvedExitPreviewURL = basePath + exitPreviewURL;
// Exit Next.js Preview Mode via the given preview API endpoint.
const res = await globalThis.fetch(resolvedExitPreviewURL);
if (res.ok) {
refresh();
} else {
console.error(
`[<PrismicPreview>] Failed to exit Preview Mode using the "${resolvedExitPreviewURL}" API endpoint. Does it exist?`,
);
}
};
window.addEventListener("prismicPreviewUpdate", handlePrismicPreviewUpdate);
window.addEventListener("prismicPreviewEnd", handlePrismicPreviewEnd);
if (!isPreviewActive) {
const prismicPreviewCookie = getPrismicPreviewCookie(
globalThis.document.cookie,
);
if (prismicPreviewCookie) {
// If a Prismic preview cookie is present, but Next.js Preview
// Mode is not active, we must activate Preview Mode manually.
//
// This will happen when a visitor accesses the page using a
// Prismic preview share link.
/**
* Determines if the current location is a descendant of the app's base
* path.
*
* This is used to prevent infinite refrehes; when
* `isDescendantOfBasePath` is `false`, `router.isPreview` is also
* `false`.
*
* If the app does not have a base path, this should always be `true`.
*/
const locationIsDescendantOfBasePath = window.location.href.startsWith(
window.location.origin + basePath,
);
const prismicPreviewCookieRepositoryName =
getPreviewCookieRepositoryName(prismicPreviewCookie);
if (
locationIsDescendantOfBasePath &&
prismicPreviewCookieRepositoryName === repositoryName
) {
startPreviewMode();
}
}
}
return () => {
window.removeEventListener(
"prismicPreviewUpdate",
handlePrismicPreviewUpdate,
);
window.removeEventListener("prismicPreviewEnd", handlePrismicPreviewEnd);
};
}, [
basePath,
exitPreviewURL,
isAppRouter,
isPreviewActive,
refresh,
repositoryName,
updatePreviewURL,
]);
return null;
}