next-sanity 12.0.2-canary.1 → 12.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -7
- package/src/client.ts +0 -3
- package/src/create-data-attribute.ts +0 -5
- package/src/draft-mode/define-enable-draft-mode.ts +0 -102
- package/src/draft-mode/index.ts +0 -1
- package/src/experimental/client-components/PresentationComlink.tsx +0 -73
- package/src/experimental/client-components/live.tsx +0 -326
- package/src/experimental/constants.ts +0 -2
- package/src/experimental/live.tsx +0 -458
- package/src/experimental/types.ts +0 -12
- package/src/hooks/index.ts +0 -4
- package/src/image/Image.tsx +0 -44
- package/src/image/imageLoader.ts +0 -22
- package/src/image/index.ts +0 -3
- package/src/index.ts +0 -5
- package/src/isCorsOriginError.ts +0 -8
- package/src/live/client-components/live/PresentationComlink.tsx +0 -129
- package/src/live/client-components/live/RefreshOnFocus.tsx +0 -27
- package/src/live/client-components/live/RefreshOnMount.tsx +0 -24
- package/src/live/client-components/live/RefreshOnReconnect.tsx +0 -16
- package/src/live/client-components/live/SanityLive.tsx +0 -280
- package/src/live/client-components/live/index.ts +0 -4
- package/src/live/client-components/live-stream/SanityLiveStream.tsx +0 -141
- package/src/live/client-components/live-stream/SanityLiveStreamLazy.tsx +0 -14
- package/src/live/client-components/live-stream/index.ts +0 -4
- package/src/live/defineLive.tsx +0 -420
- package/src/live/hooks/context.ts +0 -75
- package/src/live/hooks/index.ts +0 -6
- package/src/live/hooks/useDraftMode.ts +0 -51
- package/src/live/hooks/useIsLivePreview.ts +0 -25
- package/src/live/hooks/useIsPresentationTool.ts +0 -18
- package/src/live/hooks/usePresentationQuery.ts +0 -192
- package/src/live/resolveCookiePerspective.ts +0 -15
- package/src/live/server-actions/index.ts +0 -39
- package/src/live/utils.ts +0 -20
- package/src/live.server-only.ts +0 -30
- package/src/live.ts +0 -8
- package/src/studio/NextStudioLayout.tsx +0 -21
- package/src/studio/NextStudioNoScript.tsx +0 -34
- package/src/studio/NextStudioWithBridge.tsx +0 -20
- package/src/studio/client-component/NextStudio.tsx +0 -79
- package/src/studio/client-component/NextStudioLazy.tsx +0 -21
- package/src/studio/client-component/createHashHistoryForStudio.ts +0 -45
- package/src/studio/client-component/index.ts +0 -4
- package/src/studio/client-component/useIsMounted.ts +0 -12
- package/src/studio/head.tsx +0 -49
- package/src/studio/index.ts +0 -5
- package/src/visual-editing/VisualEditing.tsx +0 -45
- package/src/visual-editing/client-component/VisualEditing.tsx +0 -148
- package/src/visual-editing/client-component/VisualEditingLazy.tsx +0 -21
- package/src/visual-editing/client-component/index.ts +0 -4
- package/src/visual-editing/client-component/utils.ts +0 -128
- package/src/visual-editing/index.ts +0 -1
- package/src/visual-editing/server-actions/index.ts +0 -12
- package/src/webhook/index.ts +0 -41
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-sanity",
|
|
3
|
-
"version": "12.0.2
|
|
3
|
+
"version": "12.0.2",
|
|
4
4
|
"description": "Sanity.io toolkit for Next.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -50,12 +50,7 @@
|
|
|
50
50
|
"module": "./dist/index.js",
|
|
51
51
|
"types": "./dist/index.d.ts",
|
|
52
52
|
"files": [
|
|
53
|
-
"dist"
|
|
54
|
-
"src",
|
|
55
|
-
"!src/**/test/**",
|
|
56
|
-
"!src/**/*.test.ts",
|
|
57
|
-
"!src/**/*.test.tsx",
|
|
58
|
-
"!src/**/*.test-d.ts"
|
|
53
|
+
"dist"
|
|
59
54
|
],
|
|
60
55
|
"browserslist": "extends @sanity/browserslist-config",
|
|
61
56
|
"dependencies": {
|
package/src/client.ts
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import type {SanityClient} from '@sanity/client'
|
|
2
|
-
import {validatePreviewUrl} from '@sanity/preview-url-secret'
|
|
3
|
-
import {perspectiveCookieName} from '@sanity/preview-url-secret/constants'
|
|
4
|
-
import {cookies, draftMode} from 'next/headers'
|
|
5
|
-
import {redirect} from 'next/navigation'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @public
|
|
9
|
-
*/
|
|
10
|
-
export interface DefineEnableDraftModeOptions {
|
|
11
|
-
client: SanityClient
|
|
12
|
-
/**
|
|
13
|
-
* Force secure cookies in development mode.
|
|
14
|
-
* Enable this when using Next.js --experimental-https flag.
|
|
15
|
-
* This option has no effect in production (cookies are always secure).
|
|
16
|
-
* @defaultValue false
|
|
17
|
-
*/
|
|
18
|
-
secureDevMode?: boolean
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* @public
|
|
23
|
-
*/
|
|
24
|
-
export interface EnableDraftMode {
|
|
25
|
-
GET: (request: Request) => Promise<Response>
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Sets up an API route for enabling draft mode, can be paired with the `previewUrl.previewMode.enable` in `sanity/presentation`.
|
|
30
|
-
* Can also be used with `sanity-plugin-iframe-pane`.
|
|
31
|
-
* @example
|
|
32
|
-
* ```ts
|
|
33
|
-
* // src/app/api/draft-mode/enable/route.ts
|
|
34
|
-
*
|
|
35
|
-
* import { defineEnableDraftMode } from "next-sanity/draft-mode";
|
|
36
|
-
* import { client } from "@/sanity/lib/client";
|
|
37
|
-
*
|
|
38
|
-
* export const { GET } = defineEnableDraftMode({
|
|
39
|
-
* client: client.withConfig({ token: process.env.SANITY_API_READ_TOKEN }),
|
|
40
|
-
* });
|
|
41
|
-
* ```
|
|
42
|
-
*
|
|
43
|
-
* @public
|
|
44
|
-
*/
|
|
45
|
-
export function defineEnableDraftMode(options: DefineEnableDraftModeOptions): EnableDraftMode {
|
|
46
|
-
const {client} = options
|
|
47
|
-
return {
|
|
48
|
-
GET: async (request: Request) => {
|
|
49
|
-
// eslint-disable-next-line no-warning-comments
|
|
50
|
-
// @TODO check if already in draft mode at a much earlier stage, and skip validation
|
|
51
|
-
|
|
52
|
-
const {
|
|
53
|
-
isValid,
|
|
54
|
-
redirectTo = '/',
|
|
55
|
-
studioPreviewPerspective,
|
|
56
|
-
} = await validatePreviewUrl(client, request.url)
|
|
57
|
-
if (!isValid) {
|
|
58
|
-
return new Response('Invalid secret', {status: 401})
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const draftModeStore = await draftMode()
|
|
62
|
-
|
|
63
|
-
// Let's enable draft mode if it's not already enabled
|
|
64
|
-
if (!draftModeStore.isEnabled) {
|
|
65
|
-
draftModeStore.enable()
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const isProduction = process.env.NODE_ENV === 'production'
|
|
69
|
-
|
|
70
|
-
// We can't auto-detect HTTPS in dev due to Next.js limitations,
|
|
71
|
-
// so we need an explicit option
|
|
72
|
-
const isSecure = isProduction || (options.secureDevMode ?? false)
|
|
73
|
-
|
|
74
|
-
// Override cookie header for draft mode for usage in live-preview
|
|
75
|
-
// https://github.com/vercel/next.js/issues/49927
|
|
76
|
-
const cookieStore = await cookies()
|
|
77
|
-
const cookie = cookieStore.get('__prerender_bypass')!
|
|
78
|
-
cookieStore.set({
|
|
79
|
-
name: '__prerender_bypass',
|
|
80
|
-
value: cookie?.value,
|
|
81
|
-
httpOnly: true,
|
|
82
|
-
path: '/',
|
|
83
|
-
secure: isSecure,
|
|
84
|
-
sameSite: isSecure ? 'none' : 'lax',
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
if (studioPreviewPerspective) {
|
|
88
|
-
cookieStore.set({
|
|
89
|
-
name: perspectiveCookieName,
|
|
90
|
-
value: studioPreviewPerspective,
|
|
91
|
-
httpOnly: true,
|
|
92
|
-
path: '/',
|
|
93
|
-
secure: isSecure,
|
|
94
|
-
sameSite: isSecure ? 'none' : 'lax',
|
|
95
|
-
})
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// the `redirect` function throws, and eventually returns a Promise<Response>. TSC doesn't "see" that so we have to tell it
|
|
99
|
-
return redirect(redirectTo) as Promise<Response>
|
|
100
|
-
},
|
|
101
|
-
}
|
|
102
|
-
}
|
package/src/draft-mode/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './define-enable-draft-mode'
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import type {ClientPerspective} from '@sanity/client'
|
|
2
|
-
import {createNode, createNodeMachine} from '@sanity/comlink'
|
|
3
|
-
import {setPerspectiveCookie} from 'next-sanity/live/server-actions'
|
|
4
|
-
import {
|
|
5
|
-
createCompatibilityActors,
|
|
6
|
-
type LoaderControllerMsg,
|
|
7
|
-
type LoaderNodeMsg,
|
|
8
|
-
} from '@sanity/presentation-comlink'
|
|
9
|
-
import {useRouter} from 'next/navigation'
|
|
10
|
-
import {startTransition, useEffect, useEffectEvent} from 'react'
|
|
11
|
-
import {
|
|
12
|
-
setComlink,
|
|
13
|
-
setComlinkClientConfig,
|
|
14
|
-
setPerspective,
|
|
15
|
-
perspective,
|
|
16
|
-
} from '../../live/hooks/context'
|
|
17
|
-
import {sanitizePerspective} from '../../live/utils'
|
|
18
|
-
|
|
19
|
-
export default function PresentationComlink(props: {
|
|
20
|
-
projectId: string
|
|
21
|
-
dataset: string
|
|
22
|
-
draftModeEnabled: boolean
|
|
23
|
-
}): React.JSX.Element | null {
|
|
24
|
-
const {projectId, dataset, draftModeEnabled} = props
|
|
25
|
-
const router = useRouter()
|
|
26
|
-
|
|
27
|
-
useEffect(() => {
|
|
28
|
-
setComlinkClientConfig(projectId, dataset)
|
|
29
|
-
}, [dataset, projectId])
|
|
30
|
-
|
|
31
|
-
const handlePerspectiveChange = useEffectEvent(
|
|
32
|
-
(_perspective: ClientPerspective, signal: AbortSignal) => {
|
|
33
|
-
const nextPerspective = sanitizePerspective(_perspective, 'drafts')
|
|
34
|
-
if (draftModeEnabled && perspective.toString() !== nextPerspective.toString()) {
|
|
35
|
-
setPerspective(nextPerspective)
|
|
36
|
-
startTransition(() =>
|
|
37
|
-
setPerspectiveCookie(nextPerspective)
|
|
38
|
-
.then(() => {
|
|
39
|
-
if (signal.aborted) return
|
|
40
|
-
router.refresh()
|
|
41
|
-
})
|
|
42
|
-
.catch((reason) =>
|
|
43
|
-
console.error('Failed to set the preview perspective cookie', reason),
|
|
44
|
-
),
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
const comlink = createNode<LoaderNodeMsg, LoaderControllerMsg>(
|
|
52
|
-
{name: 'loaders', connectTo: 'presentation'},
|
|
53
|
-
createNodeMachine<LoaderNodeMsg, LoaderControllerMsg>().provide({
|
|
54
|
-
actors: createCompatibilityActors<LoaderNodeMsg>(),
|
|
55
|
-
}),
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
let controller: AbortController | undefined
|
|
59
|
-
comlink.on('loader/perspective', (data) => {
|
|
60
|
-
controller?.abort()
|
|
61
|
-
controller = new AbortController()
|
|
62
|
-
handlePerspectiveChange(data.perspective, controller.signal)
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
const stop = comlink.start()
|
|
66
|
-
setComlink(comlink)
|
|
67
|
-
return () => {
|
|
68
|
-
stop()
|
|
69
|
-
}
|
|
70
|
-
}, [])
|
|
71
|
-
|
|
72
|
-
return null
|
|
73
|
-
}
|
|
@@ -1,326 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
createClient,
|
|
5
|
-
type ClientPerspective,
|
|
6
|
-
type LiveEvent,
|
|
7
|
-
type LiveEventGoAway,
|
|
8
|
-
type SyncTag,
|
|
9
|
-
} from '@sanity/client'
|
|
10
|
-
import {isMaybePresentation, isMaybePreviewWindow} from '@sanity/presentation-comlink'
|
|
11
|
-
import dynamic from 'next/dynamic'
|
|
12
|
-
import {useRouter} from 'next/navigation'
|
|
13
|
-
import {useEffect, useMemo, useRef, useState, useEffectEvent} from 'react'
|
|
14
|
-
import {setEnvironment, setPerspective} from '../../live/hooks/context'
|
|
15
|
-
import {isCorsOriginError} from '../../isCorsOriginError'
|
|
16
|
-
import type {SanityClientConfig} from '../types'
|
|
17
|
-
import {sanitizePerspective} from '../../live/utils'
|
|
18
|
-
import {PUBLISHED_SYNC_TAG_PREFIX, type DRAFT_SYNC_TAG_PREFIX} from '../constants'
|
|
19
|
-
|
|
20
|
-
const PresentationComlink = dynamic(() => import('./PresentationComlink'), {ssr: false})
|
|
21
|
-
const RefreshOnMount = dynamic(() => import('../../live/client-components/live/RefreshOnMount'), {
|
|
22
|
-
ssr: false,
|
|
23
|
-
})
|
|
24
|
-
const RefreshOnFocus = dynamic(() => import('../../live/client-components/live/RefreshOnFocus'), {
|
|
25
|
-
ssr: false,
|
|
26
|
-
})
|
|
27
|
-
const RefreshOnReconnect = dynamic(
|
|
28
|
-
() => import('../../live/client-components/live/RefreshOnReconnect'),
|
|
29
|
-
{ssr: false},
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.
|
|
34
|
-
*/
|
|
35
|
-
export interface SanityLiveProps {
|
|
36
|
-
config: SanityClientConfig
|
|
37
|
-
draftModeEnabled: boolean
|
|
38
|
-
refreshOnMount?: boolean
|
|
39
|
-
refreshOnFocus?: boolean
|
|
40
|
-
refreshOnReconnect?: boolean
|
|
41
|
-
requestTag: string | undefined
|
|
42
|
-
/**
|
|
43
|
-
* Handle errors from the Live Events subscription.
|
|
44
|
-
* By default it's reported using `console.error`, you can override this prop to handle it in your own way.
|
|
45
|
-
*/
|
|
46
|
-
onError?: (error: unknown) => void
|
|
47
|
-
intervalOnGoAway?: number | false
|
|
48
|
-
onGoAway?: (event: LiveEventGoAway, intervalOnGoAway: number | false) => void
|
|
49
|
-
revalidateSyncTags: (
|
|
50
|
-
tags: `${typeof PUBLISHED_SYNC_TAG_PREFIX | typeof DRAFT_SYNC_TAG_PREFIX}${SyncTag}`[],
|
|
51
|
-
) => Promise<void | 'refresh'>
|
|
52
|
-
resolveDraftModePerspective: () => Promise<ClientPerspective>
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function handleError(error: unknown) {
|
|
56
|
-
/* eslint-disable no-console */
|
|
57
|
-
if (isCorsOriginError(error)) {
|
|
58
|
-
console.warn(
|
|
59
|
-
`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.`,
|
|
60
|
-
error.addOriginUrl && `Add it here:`,
|
|
61
|
-
error.addOriginUrl?.toString(),
|
|
62
|
-
)
|
|
63
|
-
} else {
|
|
64
|
-
console.error(error)
|
|
65
|
-
}
|
|
66
|
-
/* eslint-enable no-console */
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function handleOnGoAway(event: LiveEventGoAway, intervalOnGoAway: number | false) {
|
|
70
|
-
/* eslint-disable no-console */
|
|
71
|
-
if (intervalOnGoAway) {
|
|
72
|
-
console.warn(
|
|
73
|
-
'Sanity Live connection closed, switching to long polling set to a interval of',
|
|
74
|
-
intervalOnGoAway / 1000,
|
|
75
|
-
'seconds and the server gave this reason:',
|
|
76
|
-
event.reason,
|
|
77
|
-
)
|
|
78
|
-
} else {
|
|
79
|
-
console.error(
|
|
80
|
-
'Sanity Live connection closed, automatic revalidation is disabled, the server gave this reason:',
|
|
81
|
-
event.reason,
|
|
82
|
-
)
|
|
83
|
-
}
|
|
84
|
-
/* eslint-enable no-console */
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.
|
|
89
|
-
*/
|
|
90
|
-
export default function SanityLive(props: SanityLiveProps): React.JSX.Element | null {
|
|
91
|
-
const {
|
|
92
|
-
config,
|
|
93
|
-
draftModeEnabled,
|
|
94
|
-
refreshOnMount = false,
|
|
95
|
-
refreshOnFocus = draftModeEnabled
|
|
96
|
-
? false
|
|
97
|
-
: typeof window === 'undefined'
|
|
98
|
-
? true
|
|
99
|
-
: window.self === window.top,
|
|
100
|
-
refreshOnReconnect = true,
|
|
101
|
-
intervalOnGoAway = 30_000,
|
|
102
|
-
requestTag = 'next-loader.live',
|
|
103
|
-
onError = handleError,
|
|
104
|
-
onGoAway = handleOnGoAway,
|
|
105
|
-
revalidateSyncTags,
|
|
106
|
-
resolveDraftModePerspective,
|
|
107
|
-
} = props
|
|
108
|
-
const {projectId, dataset, apiHost, apiVersion, useProjectHostname, token, requestTagPrefix} =
|
|
109
|
-
config
|
|
110
|
-
|
|
111
|
-
const client = useMemo(
|
|
112
|
-
() =>
|
|
113
|
-
createClient({
|
|
114
|
-
projectId,
|
|
115
|
-
dataset,
|
|
116
|
-
apiHost,
|
|
117
|
-
apiVersion,
|
|
118
|
-
useProjectHostname,
|
|
119
|
-
ignoreBrowserTokenWarning: true,
|
|
120
|
-
token,
|
|
121
|
-
useCdn: false,
|
|
122
|
-
requestTagPrefix,
|
|
123
|
-
}),
|
|
124
|
-
[apiHost, apiVersion, dataset, projectId, requestTagPrefix, token, useProjectHostname],
|
|
125
|
-
)
|
|
126
|
-
const [longPollingInterval, setLongPollingInterval] = useState<number | false>(false)
|
|
127
|
-
const [resolvedInitialPerspective, setResolvedInitialPerspective] = useState(false)
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* 1. Handle Live Events and call revalidateTag or router.refresh when needed
|
|
131
|
-
*/
|
|
132
|
-
const router = useRouter()
|
|
133
|
-
const handleLiveEvent = useEffectEvent((event: LiveEvent) => {
|
|
134
|
-
if (process.env.NODE_ENV !== 'production' && event.type === 'welcome') {
|
|
135
|
-
// eslint-disable-next-line no-console
|
|
136
|
-
console.info(
|
|
137
|
-
'Sanity is live with',
|
|
138
|
-
token
|
|
139
|
-
? 'automatic revalidation for draft content changes as well as published content'
|
|
140
|
-
: draftModeEnabled
|
|
141
|
-
? 'automatic revalidation for only published content. Provide a `browserToken` to `defineLive` to support draft content outside of Presentation Tool.'
|
|
142
|
-
: 'automatic revalidation of published content',
|
|
143
|
-
)
|
|
144
|
-
// Disable long polling when welcome event is received, this is a no-op if long polling is already disabled
|
|
145
|
-
setLongPollingInterval(false)
|
|
146
|
-
} else if (event.type === 'message') {
|
|
147
|
-
void revalidateSyncTags(
|
|
148
|
-
event.tags.map((tag: SyncTag) => `${PUBLISHED_SYNC_TAG_PREFIX}${tag}` as const),
|
|
149
|
-
).then((result) => {
|
|
150
|
-
if (result === 'refresh') router.refresh()
|
|
151
|
-
})
|
|
152
|
-
} else if (event.type === 'restart' || event.type === 'reconnect') {
|
|
153
|
-
router.refresh()
|
|
154
|
-
} else if (event.type === 'goaway') {
|
|
155
|
-
onGoAway(event, intervalOnGoAway)
|
|
156
|
-
setLongPollingInterval(intervalOnGoAway)
|
|
157
|
-
}
|
|
158
|
-
})
|
|
159
|
-
// @TODO previous version that handle both published and draft events
|
|
160
|
-
// useEffect(() => {
|
|
161
|
-
// const subscription = client.live.events({includeDrafts: !!token, tag: requestTag}).subscribe({
|
|
162
|
-
// next: handleLiveEvent,
|
|
163
|
-
// error: (err: unknown) => {
|
|
164
|
-
// onError(err)
|
|
165
|
-
// },
|
|
166
|
-
// })
|
|
167
|
-
// return () => subscription.unsubscribe()
|
|
168
|
-
// }, [client.live, onError, requestTag, token])
|
|
169
|
-
useEffect(() => {
|
|
170
|
-
const subscription = client.live.events({tag: requestTag}).subscribe({
|
|
171
|
-
next: handleLiveEvent,
|
|
172
|
-
error: (err: unknown) => {
|
|
173
|
-
onError(err)
|
|
174
|
-
},
|
|
175
|
-
})
|
|
176
|
-
return () => subscription.unsubscribe()
|
|
177
|
-
}, [client.live, onError, requestTag, token])
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Handle live events for drafts differently, only use it to trigger refreshes, don't expire the cache
|
|
181
|
-
*/
|
|
182
|
-
const handleLiveDraftEvent = useEffectEvent((event: LiveEvent) => {
|
|
183
|
-
if (event.type === 'message') {
|
|
184
|
-
// Just refresh, due to cache bypass in draft mode it'll fetch fresh content (though we wish cache worked as in production)
|
|
185
|
-
// @TODO if draft content is published, then this extra refresh is unnecessary, it's tricky to check since `event.id` are different on the two EventSource connections
|
|
186
|
-
router.refresh()
|
|
187
|
-
}
|
|
188
|
-
})
|
|
189
|
-
useEffect(() => {
|
|
190
|
-
if (!token) return
|
|
191
|
-
const subscription = client.live.events({includeDrafts: !!token, tag: requestTag}).subscribe({
|
|
192
|
-
next: handleLiveDraftEvent,
|
|
193
|
-
error: (err: unknown) => {
|
|
194
|
-
onError(err)
|
|
195
|
-
},
|
|
196
|
-
})
|
|
197
|
-
return () => subscription.unsubscribe()
|
|
198
|
-
}, [client.live, onError, requestTag, token])
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* 2. Notify what perspective we're in, when in Draft Mode
|
|
202
|
-
*/
|
|
203
|
-
useEffect(() => {
|
|
204
|
-
if (resolvedInitialPerspective) return undefined
|
|
205
|
-
|
|
206
|
-
if (!draftModeEnabled) {
|
|
207
|
-
setResolvedInitialPerspective(true)
|
|
208
|
-
setPerspective('unknown')
|
|
209
|
-
return undefined
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const controller = new AbortController()
|
|
213
|
-
resolveDraftModePerspective()
|
|
214
|
-
.then((perspective) => {
|
|
215
|
-
if (controller.signal.aborted) return
|
|
216
|
-
setResolvedInitialPerspective(true)
|
|
217
|
-
setPerspective(sanitizePerspective(perspective, 'drafts'))
|
|
218
|
-
})
|
|
219
|
-
.catch((err) => {
|
|
220
|
-
if (controller.signal.aborted) return
|
|
221
|
-
console.error('Failed to resolve draft mode perspective', err)
|
|
222
|
-
setResolvedInitialPerspective(true)
|
|
223
|
-
setPerspective('unknown')
|
|
224
|
-
})
|
|
225
|
-
return () => controller.abort()
|
|
226
|
-
}, [draftModeEnabled, resolveDraftModePerspective, resolvedInitialPerspective])
|
|
227
|
-
|
|
228
|
-
const [loadComlink, setLoadComlink] = useState(false)
|
|
229
|
-
/**
|
|
230
|
-
* 3. Notify what environment we're in, when in Draft Mode
|
|
231
|
-
*/
|
|
232
|
-
useEffect(() => {
|
|
233
|
-
// If we might be in Presentation Tool, then skip detecting here as it's handled later
|
|
234
|
-
if (isMaybePresentation()) return
|
|
235
|
-
|
|
236
|
-
// If we're definitely not in Presentation Tool, then we can set the environment as stand-alone live preview
|
|
237
|
-
// if we have both a browser token, and draft mode is enabled
|
|
238
|
-
if (draftModeEnabled && token) {
|
|
239
|
-
setEnvironment('live')
|
|
240
|
-
return
|
|
241
|
-
}
|
|
242
|
-
// If we're in draft mode, but don't have a browser token, then we're in static mode
|
|
243
|
-
// which means that published content is still live, but draft changes likely need manual refresh
|
|
244
|
-
if (draftModeEnabled) {
|
|
245
|
-
setEnvironment('static')
|
|
246
|
-
return
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Fallback to `unknown` otherwise, as we simply don't know how it's setup
|
|
250
|
-
setEnvironment('unknown')
|
|
251
|
-
return
|
|
252
|
-
}, [draftModeEnabled, token])
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* 4. If Presentation Tool is detected, load up the comlink and integrate with it
|
|
256
|
-
*/
|
|
257
|
-
useEffect(() => {
|
|
258
|
-
if (!isMaybePresentation()) return
|
|
259
|
-
const controller = new AbortController()
|
|
260
|
-
// Wait for a while to see if Presentation Tool is detected, before assuming the env to be stand-alone live preview
|
|
261
|
-
const timeout = setTimeout(() => setEnvironment('live'), 3_000)
|
|
262
|
-
window.addEventListener(
|
|
263
|
-
'message',
|
|
264
|
-
({data}: MessageEvent<unknown>) => {
|
|
265
|
-
if (
|
|
266
|
-
data &&
|
|
267
|
-
typeof data === 'object' &&
|
|
268
|
-
'domain' in data &&
|
|
269
|
-
data.domain === 'sanity/channels' &&
|
|
270
|
-
'from' in data &&
|
|
271
|
-
data.from === 'presentation'
|
|
272
|
-
) {
|
|
273
|
-
clearTimeout(timeout)
|
|
274
|
-
setEnvironment(isMaybePreviewWindow() ? 'presentation-window' : 'presentation-iframe')
|
|
275
|
-
setLoadComlink(true)
|
|
276
|
-
controller.abort()
|
|
277
|
-
}
|
|
278
|
-
},
|
|
279
|
-
{signal: controller.signal},
|
|
280
|
-
)
|
|
281
|
-
return () => {
|
|
282
|
-
clearTimeout(timeout)
|
|
283
|
-
controller.abort()
|
|
284
|
-
}
|
|
285
|
-
}, [])
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* 5. Warn if draft mode is being disabled
|
|
289
|
-
* @TODO move logic into PresentationComlink, or maybe VisualEditing?
|
|
290
|
-
*/
|
|
291
|
-
const draftModeEnabledWarnRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
|
|
292
|
-
useEffect(() => {
|
|
293
|
-
if (!draftModeEnabled) return
|
|
294
|
-
clearTimeout(draftModeEnabledWarnRef.current)
|
|
295
|
-
return () => {
|
|
296
|
-
draftModeEnabledWarnRef.current = setTimeout(() => {
|
|
297
|
-
// eslint-disable-next-line no-console
|
|
298
|
-
console.warn('Sanity Live: Draft mode was enabled, but is now being disabled')
|
|
299
|
-
})
|
|
300
|
-
}
|
|
301
|
-
}, [draftModeEnabled])
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* 6. Handle switching to long polling when needed
|
|
305
|
-
*/
|
|
306
|
-
useEffect(() => {
|
|
307
|
-
if (!longPollingInterval) return
|
|
308
|
-
const interval = setInterval(() => router.refresh(), longPollingInterval)
|
|
309
|
-
return () => clearInterval(interval)
|
|
310
|
-
}, [longPollingInterval, router])
|
|
311
|
-
|
|
312
|
-
return (
|
|
313
|
-
<>
|
|
314
|
-
{draftModeEnabled && loadComlink && resolvedInitialPerspective && (
|
|
315
|
-
<PresentationComlink
|
|
316
|
-
projectId={projectId!}
|
|
317
|
-
dataset={dataset!}
|
|
318
|
-
draftModeEnabled={draftModeEnabled}
|
|
319
|
-
/>
|
|
320
|
-
)}
|
|
321
|
-
{!draftModeEnabled && refreshOnMount && <RefreshOnMount />}
|
|
322
|
-
{!draftModeEnabled && refreshOnFocus && <RefreshOnFocus />}
|
|
323
|
-
{!draftModeEnabled && refreshOnReconnect && <RefreshOnReconnect />}
|
|
324
|
-
</>
|
|
325
|
-
)
|
|
326
|
-
}
|