next-sanity
Version:
Sanity.io toolkit for Next.js
180 lines (179 loc) • 7.21 kB
JavaScript
"use client";
import { isCorsOriginError } from "../../isCorsOriginError.js";
import { sanitizePerspective } from "../../utils.js";
import { PUBLISHED_SYNC_TAG_PREFIX } from "../../constants.js";
import { setEnvironment, setPerspective } from "../../context.js";
import { createClient } from "@sanity/client";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import { useRouter } from "next/navigation";
import { useEffect, useMemo, useRef, useState } from "react";
import { isMaybePresentation, isMaybePreviewWindow } from "@sanity/presentation-comlink";
import { useEffectEvent } from "use-effect-event";
import dynamic from "next/dynamic";
const PresentationComlink = dynamic(() => import("../../PresentationComlink.js"), { ssr: false });
const RefreshOnMount = dynamic(() => import("../../RefreshOnMount.js"), { ssr: false });
const RefreshOnFocus = dynamic(() => import("../../RefreshOnFocus.js"), { ssr: false });
const RefreshOnReconnect = dynamic(() => import("../../RefreshOnReconnect.js"), { ssr: false });
function handleError(error) {
if (isCorsOriginError(error)) console.warn(`Sanity Live is unable to connect to the Sanity API as the current origin - ${window.origin} - is not in the list of allowed CORS origins for this Sanity Project.`, error.addOriginUrl && `Add it here:`, error.addOriginUrl?.toString());
else console.error(error);
}
function handleOnGoAway(event, intervalOnGoAway) {
if (intervalOnGoAway) console.warn("Sanity Live connection closed, switching to long polling set to a interval of", intervalOnGoAway / 1e3, "seconds and the server gave this reason:", event.reason);
else console.error("Sanity Live connection closed, automatic revalidation is disabled, the server gave this reason:", event.reason);
}
function SanityLive(props) {
const { config, draftModeEnabled, refreshOnMount = false, refreshOnFocus = draftModeEnabled ? false : typeof window === "undefined" ? true : window.self === window.top, refreshOnReconnect = true, intervalOnGoAway = 3e4, requestTag = "next-loader.live", onError = handleError, onGoAway = handleOnGoAway, revalidateSyncTags, resolveDraftModePerspective } = props;
const { projectId, dataset, apiHost, apiVersion, useProjectHostname, token, requestTagPrefix } = config;
const client = useMemo(() => createClient({
projectId,
dataset,
apiHost,
apiVersion,
useProjectHostname,
ignoreBrowserTokenWarning: true,
token,
useCdn: false,
requestTagPrefix
}), [
apiHost,
apiVersion,
dataset,
projectId,
requestTagPrefix,
token,
useProjectHostname
]);
const [longPollingInterval, setLongPollingInterval] = useState(false);
const [resolvedInitialPerspective, setResolvedInitialPerspective] = useState(false);
const router = useRouter();
const handleLiveEvent = useEffectEvent((event) => {
if (process.env.NODE_ENV !== "production" && event.type === "welcome") {
console.info("Sanity is live with", token ? "automatic revalidation for draft content changes as well as published content" : draftModeEnabled ? "automatic revalidation for only published content. Provide a `browserToken` to `defineLive` to support draft content outside of Presentation Tool." : "automatic revalidation of published content");
setLongPollingInterval(false);
} else if (event.type === "message") revalidateSyncTags(event.tags.map((tag) => `${PUBLISHED_SYNC_TAG_PREFIX}${tag}`)).then((result) => {
if (result === "refresh") router.refresh();
});
else if (event.type === "restart" || event.type === "reconnect") router.refresh();
else if (event.type === "goaway") {
onGoAway(event, intervalOnGoAway);
setLongPollingInterval(intervalOnGoAway);
}
});
useEffect(() => {
const subscription = client.live.events({ tag: requestTag }).subscribe({
next: handleLiveEvent,
error: (err) => {
onError(err);
}
});
return () => subscription.unsubscribe();
}, [
client.live,
onError,
requestTag,
token
]);
const handleLiveDraftEvent = useEffectEvent((event) => {
if (event.type === "message") router.refresh();
});
useEffect(() => {
if (!token) return;
const subscription = client.live.events({
includeDrafts: !!token,
tag: requestTag
}).subscribe({
next: handleLiveDraftEvent,
error: (err) => {
onError(err);
}
});
return () => subscription.unsubscribe();
}, [
client.live,
onError,
requestTag,
token
]);
useEffect(() => {
if (resolvedInitialPerspective) return void 0;
if (!draftModeEnabled) {
setResolvedInitialPerspective(true);
setPerspective("unknown");
return;
}
const controller = new AbortController();
resolveDraftModePerspective().then((perspective) => {
if (controller.signal.aborted) return;
setResolvedInitialPerspective(true);
setPerspective(sanitizePerspective(perspective, "drafts"));
}).catch((err) => {
if (controller.signal.aborted) return;
console.error("Failed to resolve draft mode perspective", err);
setResolvedInitialPerspective(true);
setPerspective("unknown");
});
return () => controller.abort();
}, [
draftModeEnabled,
resolveDraftModePerspective,
resolvedInitialPerspective
]);
const [loadComlink, setLoadComlink] = useState(false);
useEffect(() => {
if (isMaybePresentation()) return;
if (draftModeEnabled && token) {
setEnvironment("live");
return;
}
if (draftModeEnabled) {
setEnvironment("static");
return;
}
setEnvironment("unknown");
}, [draftModeEnabled, token]);
useEffect(() => {
if (!isMaybePresentation()) return;
const controller = new AbortController();
const timeout = setTimeout(() => setEnvironment("live"), 3e3);
window.addEventListener("message", ({ data }) => {
if (data && typeof data === "object" && "domain" in data && data.domain === "sanity/channels" && "from" in data && data.from === "presentation") {
clearTimeout(timeout);
setEnvironment(isMaybePreviewWindow() ? "presentation-window" : "presentation-iframe");
setLoadComlink(true);
controller.abort();
}
}, { signal: controller.signal });
return () => {
clearTimeout(timeout);
controller.abort();
};
}, []);
const draftModeEnabledWarnRef = useRef(void 0);
useEffect(() => {
if (!draftModeEnabled) return;
clearTimeout(draftModeEnabledWarnRef.current);
return () => {
draftModeEnabledWarnRef.current = setTimeout(() => {
console.warn("Sanity Live: Draft mode was enabled, but is now being disabled");
});
};
}, [draftModeEnabled]);
useEffect(() => {
if (!longPollingInterval) return;
const interval = setInterval(() => router.refresh(), longPollingInterval);
return () => clearInterval(interval);
}, [longPollingInterval, router]);
return /* @__PURE__ */ jsxs(Fragment, { children: [
draftModeEnabled && loadComlink && resolvedInitialPerspective && /* @__PURE__ */ jsx(PresentationComlink, {
projectId,
dataset,
draftModeEnabled
}),
!draftModeEnabled && refreshOnMount && /* @__PURE__ */ jsx(RefreshOnMount, {}),
!draftModeEnabled && refreshOnFocus && /* @__PURE__ */ jsx(RefreshOnFocus, {}),
!draftModeEnabled && refreshOnReconnect && /* @__PURE__ */ jsx(RefreshOnReconnect, {})
] });
}
export { SanityLive as default };
//# sourceMappingURL=live.js.map