sanity 3.83.1-canary.23 → 3.84.1-next.0
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/lib/_chunks-cjs/PostMessageSchema.js.map +1 -1
- package/lib/_chunks-cjs/PostMessageTelemetry.js +1 -1
- package/lib/_chunks-cjs/PostMessageTelemetry.js.map +1 -1
- package/lib/_chunks-cjs/PresentationToolGrantsCheck.js +429 -1417
- package/lib/_chunks-cjs/PresentationToolGrantsCheck.js.map +1 -1
- package/lib/_chunks-cjs/presentation.js +70 -22
- package/lib/_chunks-cjs/presentation.js.map +1 -1
- package/lib/_chunks-cjs/resources6.js +0 -8
- package/lib/_chunks-cjs/resources6.js.map +1 -1
- package/lib/_chunks-cjs/version.js +1 -1
- package/lib/_chunks-es/PostMessageSchema.mjs.map +1 -1
- package/lib/_chunks-es/PostMessageTelemetry.mjs +1 -1
- package/lib/_chunks-es/PostMessageTelemetry.mjs.map +1 -1
- package/lib/_chunks-es/PresentationToolGrantsCheck.mjs +437 -1406
- package/lib/_chunks-es/PresentationToolGrantsCheck.mjs.map +1 -1
- package/lib/_chunks-es/presentation.mjs +73 -24
- package/lib/_chunks-es/presentation.mjs.map +1 -1
- package/lib/_chunks-es/resources6.mjs +0 -8
- package/lib/_chunks-es/resources6.mjs.map +1 -1
- package/lib/_chunks-es/version.mjs +1 -1
- package/lib/_singletons.d.mts +61 -2889
- package/lib/_singletons.d.ts +61 -2889
- package/lib/presentation.d.mts +57 -2887
- package/lib/presentation.d.ts +57 -2887
- package/lib/presentation.js +4 -0
- package/lib/presentation.js.map +1 -1
- package/lib/presentation.mjs +5 -1
- package/package.json +15 -16
- package/src/presentation/PostMessageTelemetry.tsx +1 -1
- package/src/presentation/PresentationTool.tsx +76 -85
- package/src/presentation/PresentationToolGrantsCheck.tsx +75 -39
- package/src/presentation/document/LocationsBanner.tsx +13 -52
- package/src/presentation/i18n/resources.ts +0 -10
- package/src/presentation/index.ts +13 -0
- package/src/presentation/{util → lib}/parse.ts +1 -2
- package/src/presentation/overlays/schema/PostMessageSchema.tsx +0 -1
- package/src/presentation/panels/usePanelsStorage.ts +1 -1
- package/src/presentation/preview/IFrame.tsx +35 -83
- package/src/presentation/preview/Preview.tsx +56 -172
- package/src/presentation/preview/PreviewHeader.tsx +10 -43
- package/src/presentation/preview/PreviewLocationInput.tsx +24 -48
- package/src/presentation/preview/SharePreviewMenu.tsx +2 -2
- package/src/presentation/reducers/presentationReducer.ts +134 -0
- package/src/presentation/types.ts +7 -139
- package/src/presentation/useMainDocument.ts +12 -4
- package/src/presentation/useParams.ts +3 -2
- package/src/presentation/usePreviewUrl.ts +133 -0
- package/src/presentation/actors/create-preview-secret.ts +0 -19
- package/src/presentation/actors/read-shared-secret.ts +0 -18
- package/src/presentation/actors/resolve-allow-patterns.ts +0 -55
- package/src/presentation/actors/resolve-initial-url.ts +0 -65
- package/src/presentation/actors/resolve-preview-mode-url.ts +0 -72
- package/src/presentation/actors/resolve-preview-mode.ts +0 -66
- package/src/presentation/actors/resolve-url-from-preview-search-param.ts +0 -29
- package/src/presentation/machines/presentation-machine.ts +0 -101
- package/src/presentation/machines/preview-url.ts +0 -568
- package/src/presentation/useAllowPatterns.ts +0 -12
- package/src/presentation/useId.ts +0 -7
- package/src/presentation/usePresentationPerspective.ts +0 -14
- package/src/presentation/usePreviewUrlActorRef.ts +0 -96
- package/src/presentation/useReportInvalidPreviewSearchParam.tsx +0 -43
- package/src/presentation/useTargetOrigin.ts +0 -11
- /package/src/presentation/{util → lib}/debounce.ts +0 -0
package/lib/presentation.js
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: !0 });
|
3
3
|
var presentation = require("./_chunks-cjs/presentation.js");
|
4
|
+
exports.ACTION_IFRAME_LOADED = presentation.ACTION_IFRAME_LOADED;
|
5
|
+
exports.ACTION_IFRAME_REFRESH = presentation.ACTION_IFRAME_REFRESH;
|
6
|
+
exports.ACTION_IFRAME_RELOAD = presentation.ACTION_IFRAME_RELOAD;
|
7
|
+
exports.ACTION_VISUAL_EDITING_OVERLAYS_TOGGLE = presentation.ACTION_VISUAL_EDITING_OVERLAYS_TOGGLE;
|
4
8
|
exports.defineDocuments = presentation.defineDocuments;
|
5
9
|
exports.defineLocations = presentation.defineLocations;
|
6
10
|
exports.presentationTool = presentation.presentationTool;
|
package/lib/presentation.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"presentation.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"presentation.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;"}
|
package/lib/presentation.mjs
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
-
import { defineDocuments, defineLocations, presentationTool, usePresentationNavigate, usePresentationParams, useSharedState } from "./_chunks-es/presentation.mjs";
|
1
|
+
import { ACTION_IFRAME_LOADED, ACTION_IFRAME_REFRESH, ACTION_IFRAME_RELOAD, ACTION_VISUAL_EDITING_OVERLAYS_TOGGLE, defineDocuments, defineLocations, presentationTool, usePresentationNavigate, usePresentationParams, useSharedState } from "./_chunks-es/presentation.mjs";
|
2
2
|
export {
|
3
|
+
ACTION_IFRAME_LOADED,
|
4
|
+
ACTION_IFRAME_REFRESH,
|
5
|
+
ACTION_IFRAME_RELOAD,
|
6
|
+
ACTION_VISUAL_EDITING_OVERLAYS_TOGGLE,
|
3
7
|
defineDocuments,
|
4
8
|
defineLocations,
|
5
9
|
presentationTool,
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "sanity",
|
3
|
-
"version": "3.
|
3
|
+
"version": "3.84.1-next.0",
|
4
4
|
"description": "Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches",
|
5
5
|
"keywords": [
|
6
6
|
"sanity",
|
@@ -156,11 +156,11 @@
|
|
156
156
|
"@rexxars/react-json-inspector": "^9.0.1",
|
157
157
|
"@sanity/asset-utils": "^2.0.6",
|
158
158
|
"@sanity/bifur-client": "^0.4.1",
|
159
|
-
"@sanity/cli": "3.
|
159
|
+
"@sanity/cli": "3.84.1-next.0",
|
160
160
|
"@sanity/client": "^6.28.4",
|
161
161
|
"@sanity/color": "^3.0.0",
|
162
162
|
"@sanity/comlink": "^3.0.1",
|
163
|
-
"@sanity/diff": "3.
|
163
|
+
"@sanity/diff": "3.84.1-next.0",
|
164
164
|
"@sanity/diff-match-patch": "^3.1.1",
|
165
165
|
"@sanity/diff-patch": "^5.0.0",
|
166
166
|
"@sanity/eventsource": "^5.0.0",
|
@@ -170,15 +170,15 @@
|
|
170
170
|
"@sanity/import": "^3.38.2",
|
171
171
|
"@sanity/insert-menu": "^1.1.9",
|
172
172
|
"@sanity/logos": "^2.1.13",
|
173
|
-
"@sanity/migrate": "3.
|
174
|
-
"@sanity/mutator": "3.
|
173
|
+
"@sanity/migrate": "3.84.1-next.0",
|
174
|
+
"@sanity/mutator": "3.84.1-next.0",
|
175
175
|
"@sanity/presentation-comlink": "^1.0.15",
|
176
176
|
"@sanity/preview-url-secret": "^2.1.7",
|
177
|
-
"@sanity/schema": "3.
|
177
|
+
"@sanity/schema": "3.84.1-next.0",
|
178
178
|
"@sanity/telemetry": "^0.8.0",
|
179
|
-
"@sanity/types": "3.
|
179
|
+
"@sanity/types": "3.84.1-next.0",
|
180
180
|
"@sanity/ui": "^2.15.13",
|
181
|
-
"@sanity/util": "3.
|
181
|
+
"@sanity/util": "3.84.1-next.0",
|
182
182
|
"@sanity/uuid": "^3.0.2",
|
183
183
|
"@sentry/react": "^8.33.0",
|
184
184
|
"@tanstack/react-table": "^8.16.0",
|
@@ -189,7 +189,6 @@
|
|
189
189
|
"@types/tar-stream": "^3.1.3",
|
190
190
|
"@types/use-sync-external-store": "^1.5.0",
|
191
191
|
"@vitejs/plugin-react": "^4.3.4",
|
192
|
-
"@xstate/react": "^5.0.3",
|
193
192
|
"archiver": "^7.0.0",
|
194
193
|
"arrify": "^2.0.1",
|
195
194
|
"async-mutex": "^0.4.1",
|
@@ -260,25 +259,25 @@
|
|
260
259
|
"semver": "^7.3.5",
|
261
260
|
"shallow-equals": "^1.0.0",
|
262
261
|
"speakingurl": "^14.0.1",
|
262
|
+
"suspend-react": "0.1.3",
|
263
263
|
"tar-fs": "^2.1.1",
|
264
264
|
"tar-stream": "^3.1.7",
|
265
|
-
"urlpattern-polyfill": "10.0.0",
|
266
265
|
"use-device-pixel-ratio": "^1.1.0",
|
267
266
|
"use-effect-event": "^1.0.2",
|
268
267
|
"use-hot-module-reload": "^2.0.0",
|
269
268
|
"use-sync-external-store": "^1.5.0",
|
270
269
|
"uuid": "^11.0.5",
|
270
|
+
"valibot": "0.31.1",
|
271
271
|
"vite": "^6.0.11",
|
272
|
-
"xstate": "^5.19.2",
|
273
272
|
"yargs": "^17.3.0"
|
274
273
|
},
|
275
274
|
"devDependencies": {
|
276
275
|
"@playwright/experimental-ct-react": "1.51.1",
|
277
276
|
"@playwright/test": "1.51.1",
|
278
|
-
"@repo/dev-aliases": "3.
|
279
|
-
"@repo/package.config": "3.
|
280
|
-
"@repo/test-config": "3.
|
281
|
-
"@sanity/codegen": "3.
|
277
|
+
"@repo/dev-aliases": "3.84.1-next.0",
|
278
|
+
"@repo/package.config": "3.84.1-next.0",
|
279
|
+
"@repo/test-config": "3.84.1-next.0",
|
280
|
+
"@sanity/codegen": "3.84.1-next.0",
|
282
281
|
"@sanity/generate-help-url": "^3.0.0",
|
283
282
|
"@sanity/pkg-utils": "6.13.4",
|
284
283
|
"@sanity/tsdoc": "1.0.169",
|
@@ -325,5 +324,5 @@
|
|
325
324
|
"engines": {
|
326
325
|
"node": ">=18"
|
327
326
|
},
|
328
|
-
"gitHead": "
|
327
|
+
"gitHead": "09b868ccb5b76181daf2495b124005cb36072796"
|
329
328
|
}
|
@@ -13,7 +13,7 @@ const PostMessageTelemetry: FC<PostMessageTelemetryProps> = (props) => {
|
|
13
13
|
const telemetry = useTelemetry()
|
14
14
|
|
15
15
|
useEffect(() => {
|
16
|
-
return comlink.on('visual-editing/telemetry-log', (message) => {
|
16
|
+
return comlink.on('visual-editing/telemetry-log', async (message) => {
|
17
17
|
const {event, data} = message
|
18
18
|
|
19
19
|
// SANITY_STUDIO_DEBUG_TELEMETRY ensures noop/in-browser logging for telemetry events
|
@@ -17,8 +17,7 @@ import {
|
|
17
17
|
urlSearchParamVercelSetBypassCookie,
|
18
18
|
} from '@sanity/preview-url-secret/constants'
|
19
19
|
import {BoundaryElementProvider, Flex} from '@sanity/ui'
|
20
|
-
import {
|
21
|
-
import {lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
20
|
+
import {lazy, Suspense, useCallback, useEffect, useMemo, useReducer, useRef, useState} from 'react'
|
22
21
|
import {
|
23
22
|
type CommentIntentGetter,
|
24
23
|
COMMENTS_INSPECTOR_NAME,
|
@@ -26,6 +25,7 @@ import {
|
|
26
25
|
type SanityDocument,
|
27
26
|
type Tool,
|
28
27
|
useDataset,
|
28
|
+
usePerspective,
|
29
29
|
useProjectId,
|
30
30
|
useUnique,
|
31
31
|
useWorkspace,
|
@@ -36,8 +36,7 @@ import {useEffectEvent} from 'use-effect-event'
|
|
36
36
|
|
37
37
|
import {DEFAULT_TOOL_NAME, EDIT_INTENT_MODE} from './constants'
|
38
38
|
import PostMessageFeatures from './features/PostMessageFeatures'
|
39
|
-
import {
|
40
|
-
import {type PreviewUrlRef} from './machines/preview-url'
|
39
|
+
import {debounce} from './lib/debounce'
|
41
40
|
import {SharedStateProvider} from './overlays/SharedStateProvider'
|
42
41
|
import {Panel} from './panels/Panel'
|
43
42
|
import {Panels} from './panels/Panels'
|
@@ -47,24 +46,29 @@ import {usePresentationNavigator} from './PresentationNavigator'
|
|
47
46
|
import {PresentationParamsProvider} from './PresentationParamsProvider'
|
48
47
|
import {PresentationProvider} from './PresentationProvider'
|
49
48
|
import {Preview} from './preview/Preview'
|
49
|
+
import {
|
50
|
+
ACTION_IFRAME_LOADED,
|
51
|
+
ACTION_IFRAME_REFRESH,
|
52
|
+
ACTION_VISUAL_EDITING_OVERLAYS_TOGGLE,
|
53
|
+
presentationReducer,
|
54
|
+
presentationReducerInit,
|
55
|
+
} from './reducers/presentationReducer'
|
50
56
|
import {
|
51
57
|
type FrameState,
|
52
58
|
type PresentationNavigate,
|
59
|
+
type PresentationPerspective,
|
53
60
|
type PresentationPluginOptions,
|
54
61
|
type PresentationStateParams,
|
55
62
|
type PresentationViewport,
|
56
63
|
type StructureDocumentPaneParams,
|
57
64
|
type VisualEditingConnection,
|
58
65
|
} from './types'
|
59
|
-
import {useAllowPatterns} from './useAllowPatterns'
|
60
66
|
import {useDocumentsOnPage} from './useDocumentsOnPage'
|
61
67
|
import {useMainDocument} from './useMainDocument'
|
62
68
|
import {useParams} from './useParams'
|
63
69
|
import {usePopups} from './usePopups'
|
64
|
-
import {
|
70
|
+
import {usePreviewUrl} from './usePreviewUrl'
|
65
71
|
import {useStatus} from './useStatus'
|
66
|
-
import {useTargetOrigin} from './useTargetOrigin'
|
67
|
-
import {debounce} from './util/debounce'
|
68
72
|
|
69
73
|
const LiveQueries = lazy(() => import('./loader/LiveQueries'))
|
70
74
|
const PostMessageDocuments = lazy(() => import('./overlays/PostMessageDocuments'))
|
@@ -80,25 +84,20 @@ const Container = styled(Flex)`
|
|
80
84
|
|
81
85
|
export default function PresentationTool(props: {
|
82
86
|
tool: Tool<PresentationPluginOptions>
|
87
|
+
canCreateUrlPreviewSecrets: boolean
|
83
88
|
canToggleSharePreviewAccess: boolean
|
84
89
|
canUseSharedPreviewAccess: boolean
|
85
90
|
vercelProtectionBypass: string | null
|
86
|
-
initialPreviewUrl: URL
|
87
|
-
previewUrlRef: PreviewUrlRef
|
88
91
|
}): React.JSX.Element {
|
89
92
|
const {
|
93
|
+
canCreateUrlPreviewSecrets,
|
90
94
|
canToggleSharePreviewAccess,
|
91
95
|
canUseSharedPreviewAccess,
|
92
96
|
tool,
|
93
97
|
vercelProtectionBypass,
|
94
|
-
initialPreviewUrl,
|
95
|
-
previewUrlRef,
|
96
98
|
} = props
|
97
|
-
|
98
|
-
const allowOrigins = useAllowPatterns(previewUrlRef)
|
99
|
-
const targetOrigin = useTargetOrigin(previewUrlRef)
|
100
|
-
|
101
99
|
const components = tool.options?.components
|
100
|
+
const _previewUrl = tool.options?.previewUrl
|
102
101
|
const name = tool.name || DEFAULT_TOOL_NAME
|
103
102
|
const {unstable_navigator, unstable_header} = components || {}
|
104
103
|
|
@@ -106,12 +105,39 @@ export default function PresentationTool(props: {
|
|
106
105
|
state: PresentationStateParams
|
107
106
|
}
|
108
107
|
const routerSearchParams = useUnique(Object.fromEntries(routerState._searchParams || []))
|
109
|
-
const
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
108
|
+
const {perspectiveStack, selectedPerspectiveName = 'drafts', selectedReleaseId} = usePerspective()
|
109
|
+
const perspective = (
|
110
|
+
selectedReleaseId ? perspectiveStack : selectedPerspectiveName
|
111
|
+
) as PresentationPerspective
|
112
|
+
|
113
|
+
const initialPreviewUrl = usePreviewUrl(
|
114
|
+
_previewUrl || '/',
|
115
|
+
name,
|
116
|
+
perspective,
|
117
|
+
routerSearchParams.preview || null,
|
118
|
+
canCreateUrlPreviewSecrets,
|
114
119
|
)
|
120
|
+
const canSharePreviewAccess = useMemo<boolean>(() => {
|
121
|
+
if (
|
122
|
+
_previewUrl &&
|
123
|
+
typeof _previewUrl === 'object' &&
|
124
|
+
'draftMode' in _previewUrl &&
|
125
|
+
_previewUrl.draftMode
|
126
|
+
) {
|
127
|
+
// eslint-disable-next-line no-console
|
128
|
+
console.warn('previewUrl.draftMode is deprecated, use previewUrl.previewMode instead')
|
129
|
+
return _previewUrl.draftMode.shareAccess !== false
|
130
|
+
}
|
131
|
+
if (
|
132
|
+
_previewUrl &&
|
133
|
+
typeof _previewUrl === 'object' &&
|
134
|
+
'previewMode' in _previewUrl &&
|
135
|
+
_previewUrl.previewMode
|
136
|
+
) {
|
137
|
+
return _previewUrl.previewMode.shareAccess !== false
|
138
|
+
}
|
139
|
+
return false
|
140
|
+
}, [_previewUrl])
|
115
141
|
|
116
142
|
const [devMode] = useState(() => {
|
117
143
|
const option = tool.options?.devMode
|
@@ -122,6 +148,10 @@ export default function PresentationTool(props: {
|
|
122
148
|
return typeof window !== 'undefined' && window.location.hostname === 'localhost'
|
123
149
|
})
|
124
150
|
|
151
|
+
const targetOrigin = useMemo(() => {
|
152
|
+
return initialPreviewUrl.origin
|
153
|
+
}, [initialPreviewUrl.origin])
|
154
|
+
|
125
155
|
const iframeRef = useRef<HTMLIFrameElement>(null)
|
126
156
|
|
127
157
|
const [controller, setController] = useState<Controller>()
|
@@ -151,7 +181,7 @@ export default function PresentationTool(props: {
|
|
151
181
|
// Most navigation events should be debounced, so use this unless explicitly needed
|
152
182
|
const navigate = useMemo(() => debounce<PresentationNavigate>(_navigate, 50), [_navigate])
|
153
183
|
|
154
|
-
const
|
184
|
+
const [state, dispatch] = useReducer(presentationReducer, {}, presentationReducerInit)
|
155
185
|
|
156
186
|
const viewport = useMemo(() => (params.viewport ? 'mobile' : 'desktop'), [params.viewport])
|
157
187
|
|
@@ -165,7 +195,7 @@ export default function PresentationTool(props: {
|
|
165
195
|
navigate: _navigate,
|
166
196
|
navigationHistory,
|
167
197
|
path: params.preview,
|
168
|
-
|
198
|
+
previewUrl: tool.options?.previewUrl,
|
169
199
|
resolvers: tool.options?.resolve?.mainDocuments,
|
170
200
|
})
|
171
201
|
|
@@ -175,7 +205,7 @@ export default function PresentationTool(props: {
|
|
175
205
|
|
176
206
|
const {open: handleOpenPopup} = usePopups(controller)
|
177
207
|
|
178
|
-
const isLoading =
|
208
|
+
const isLoading = state.iframe.status === 'loading'
|
179
209
|
|
180
210
|
useEffect(() => {
|
181
211
|
const target = iframeRef.current?.contentWindow
|
@@ -220,19 +250,7 @@ export default function PresentationTool(props: {
|
|
220
250
|
})
|
221
251
|
|
222
252
|
comlink.on('visual-editing/navigate', (data) => {
|
223
|
-
const {title} = data
|
224
|
-
let url = data.url
|
225
|
-
/**
|
226
|
-
* The URL is relative, we need to resolve it to an absolute URL
|
227
|
-
*/
|
228
|
-
if (!url.startsWith('http')) {
|
229
|
-
try {
|
230
|
-
url = new URL(url, targetOrigin).toString()
|
231
|
-
} catch {
|
232
|
-
// ignore
|
233
|
-
}
|
234
|
-
}
|
235
|
-
|
253
|
+
const {title, url} = data
|
236
254
|
if (frameStateRef.current.url !== url) {
|
237
255
|
try {
|
238
256
|
// Handle bypass params being forwarded to the final URL
|
@@ -256,7 +274,10 @@ export default function PresentationTool(props: {
|
|
256
274
|
})
|
257
275
|
|
258
276
|
comlink.on('visual-editing/toggle', (data) => {
|
259
|
-
|
277
|
+
dispatch({
|
278
|
+
type: ACTION_VISUAL_EDITING_OVERLAYS_TOGGLE,
|
279
|
+
enabled: data.enabled,
|
280
|
+
})
|
260
281
|
})
|
261
282
|
|
262
283
|
comlink.on('visual-editing/documents', (data) => {
|
@@ -273,12 +294,12 @@ export default function PresentationTool(props: {
|
|
273
294
|
if (data.source === 'manual') {
|
274
295
|
clearTimeout(refreshRef.current)
|
275
296
|
} else if (data.source === 'mutation') {
|
276
|
-
|
297
|
+
dispatch({type: ACTION_IFRAME_REFRESH})
|
277
298
|
}
|
278
299
|
})
|
279
300
|
|
280
301
|
comlink.on('visual-editing/refreshed', () => {
|
281
|
-
|
302
|
+
dispatch({type: ACTION_IFRAME_LOADED})
|
282
303
|
})
|
283
304
|
|
284
305
|
comlink.onStatus(setOverlaysConnection)
|
@@ -289,7 +310,7 @@ export default function PresentationTool(props: {
|
|
289
310
|
stop()
|
290
311
|
setVisualEditingComlink(null)
|
291
312
|
}
|
292
|
-
}, [controller,
|
313
|
+
}, [controller, setDocumentsOnPage, setOverlaysConnection, targetOrigin])
|
293
314
|
|
294
315
|
useEffect(() => {
|
295
316
|
if (!controller) return undefined
|
@@ -322,7 +343,7 @@ export default function PresentationTool(props: {
|
|
322
343
|
|
323
344
|
const handleFocusPath = useCallback(
|
324
345
|
(nextPath: Path) => {
|
325
|
-
// Don
|
346
|
+
// Don’t need to explicitly set the id here because it was either already set via postMessage or is the same if navigating in the document pane
|
326
347
|
navigate({path: studioPath.toString(nextPath)}, {}, true)
|
327
348
|
},
|
328
349
|
[navigate],
|
@@ -330,20 +351,13 @@ export default function PresentationTool(props: {
|
|
330
351
|
|
331
352
|
const handlePreviewPath = useCallback(
|
332
353
|
(nextPath: string) => {
|
333
|
-
const url = new URL(nextPath,
|
334
|
-
const preview = url.
|
335
|
-
if (
|
336
|
-
return
|
337
|
-
}
|
338
|
-
if (Array.isArray(allowOrigins)) {
|
339
|
-
if (allowOrigins.some((pattern) => pattern.test(preview))) {
|
340
|
-
navigate({}, {preview})
|
341
|
-
}
|
342
|
-
} else if (url.origin === targetOrigin) {
|
354
|
+
const url = new URL(nextPath, initialPreviewUrl.origin)
|
355
|
+
const preview = url.pathname + url.search
|
356
|
+
if (url.origin === initialPreviewUrl.origin && preview !== params.preview) {
|
343
357
|
navigate({}, {preview})
|
344
358
|
}
|
345
359
|
},
|
346
|
-
[
|
360
|
+
[initialPreviewUrl, params, navigate],
|
347
361
|
)
|
348
362
|
|
349
363
|
const handleStructureParams = useCallback(
|
@@ -369,32 +383,12 @@ export default function PresentationTool(props: {
|
|
369
383
|
params.preview &&
|
370
384
|
frameStateRef.current.url !== params.preview
|
371
385
|
) {
|
372
|
-
try {
|
373
|
-
const frameOrigin = new URL(frameStateRef.current.url, targetOrigin).origin
|
374
|
-
const previewOrigin = new URL(params.preview, targetOrigin).origin
|
375
|
-
if (frameOrigin !== previewOrigin) {
|
376
|
-
return
|
377
|
-
}
|
378
|
-
} catch {
|
379
|
-
// ignore
|
380
|
-
}
|
381
|
-
|
382
386
|
frameStateRef.current.url = params.preview
|
383
|
-
if (overlaysConnection
|
384
|
-
|
385
|
-
|
386
|
-
*/
|
387
|
-
let url = params.preview
|
388
|
-
if (url.startsWith('http')) {
|
389
|
-
try {
|
390
|
-
const newUrl = new URL(params.preview, targetOrigin)
|
391
|
-
url = newUrl.pathname + newUrl.search + newUrl.hash
|
392
|
-
} catch {
|
393
|
-
// ignore
|
394
|
-
}
|
395
|
-
}
|
387
|
+
if (overlaysConnection !== 'connected' && iframeRef.current) {
|
388
|
+
iframeRef.current.src = `${targetOrigin}${params.preview}`
|
389
|
+
} else {
|
396
390
|
visualEditingComlink?.post('presentation/navigate', {
|
397
|
-
url,
|
391
|
+
url: params.preview,
|
398
392
|
type: 'replace',
|
399
393
|
})
|
400
394
|
}
|
@@ -442,7 +436,7 @@ export default function PresentationTool(props: {
|
|
442
436
|
const refreshRef = useRef<number>(undefined)
|
443
437
|
const handleRefresh = useCallback(
|
444
438
|
(fallback: () => void) => {
|
445
|
-
|
439
|
+
dispatch({type: ACTION_IFRAME_REFRESH})
|
446
440
|
if (visualEditingComlink) {
|
447
441
|
// We only wait 300ms for the iframe to ack the refresh request before running the fallback logic
|
448
442
|
refreshRef.current = window.setTimeout(fallback, 300)
|
@@ -455,7 +449,7 @@ export default function PresentationTool(props: {
|
|
455
449
|
}
|
456
450
|
fallback()
|
457
451
|
},
|
458
|
-
[loadersConnection,
|
452
|
+
[loadersConnection, previewKitConnection, visualEditingComlink],
|
459
453
|
)
|
460
454
|
|
461
455
|
const workspace = useWorkspace()
|
@@ -517,13 +511,14 @@ export default function PresentationTool(props: {
|
|
517
511
|
<Flex direction="column" flex={1} height="fill" ref={setBoundaryElement}>
|
518
512
|
<BoundaryElementProvider element={boundaryElement}>
|
519
513
|
<Preview
|
520
|
-
// @TODO move closer to the <iframe> element itself to allow for more precise handling of when to reload the iframe and when to reconnect when the target origin changes
|
521
514
|
// Make sure the iframe is unmounted if the targetOrigin has changed
|
522
515
|
key={targetOrigin}
|
523
516
|
canSharePreviewAccess={canSharePreviewAccess}
|
524
517
|
canToggleSharePreviewAccess={canToggleSharePreviewAccess}
|
525
518
|
canUseSharedPreviewAccess={canUseSharedPreviewAccess}
|
519
|
+
dispatch={dispatch}
|
526
520
|
header={unstable_header}
|
521
|
+
iframe={state.iframe}
|
527
522
|
initialUrl={initialPreviewUrl}
|
528
523
|
loadersConnection={loadersConnection}
|
529
524
|
navigatorEnabled={navigatorEnabled}
|
@@ -539,9 +534,8 @@ export default function PresentationTool(props: {
|
|
539
534
|
toggleNavigator={toggleNavigator}
|
540
535
|
toggleOverlay={toggleOverlay}
|
541
536
|
viewport={viewport}
|
537
|
+
visualEditing={state.visualEditing}
|
542
538
|
vercelProtectionBypass={vercelProtectionBypass}
|
543
|
-
presentationRef={presentationRef}
|
544
|
-
previewUrlRef={previewUrlRef}
|
545
539
|
/>
|
546
540
|
</BoundaryElementProvider>
|
547
541
|
</Flex>
|
@@ -606,12 +600,10 @@ export default function PresentationTool(props: {
|
|
606
600
|
)
|
607
601
|
}
|
608
602
|
|
609
|
-
// @TODO reconcile with core utils
|
610
603
|
function isAltKey(event: KeyboardEvent): boolean {
|
611
604
|
return event.key === 'Alt'
|
612
605
|
}
|
613
606
|
|
614
|
-
// @TODO reconcile with core utils
|
615
607
|
const IS_MAC =
|
616
608
|
typeof window != 'undefined' && /Mac|iPod|iPhone|iPad/.test(window.navigator.platform)
|
617
609
|
const MODIFIERS: Record<string, 'altKey' | 'ctrlKey' | 'metaKey' | 'shiftKey'> = {
|
@@ -620,7 +612,6 @@ const MODIFIERS: Record<string, 'altKey' | 'ctrlKey' | 'metaKey' | 'shiftKey'> =
|
|
620
612
|
mod: IS_MAC ? 'metaKey' : 'ctrlKey',
|
621
613
|
shift: 'shiftKey',
|
622
614
|
}
|
623
|
-
// @TODO reconcile with core utils
|
624
615
|
function isHotkey(keys: string[], event: KeyboardEvent): boolean {
|
625
616
|
return keys.every((key) => {
|
626
617
|
if (MODIFIERS[key]) {
|
@@ -1,67 +1,103 @@
|
|
1
|
-
import {
|
2
|
-
|
1
|
+
import {
|
2
|
+
schemaIdSingleton,
|
3
|
+
schemaType,
|
4
|
+
schemaTypeSingleton,
|
5
|
+
} from '@sanity/preview-url-secret/constants'
|
6
|
+
import {useToast} from '@sanity/ui'
|
7
|
+
import {uuid} from '@sanity/uuid'
|
8
|
+
import {useEffect, useState} from 'react'
|
9
|
+
import {type PermissionCheckResult, type Tool, useGrantsStore, useTranslation} from 'sanity'
|
3
10
|
|
11
|
+
import {presentationLocaleNamespace} from './i18n'
|
4
12
|
import {PresentationSpinner} from './PresentationSpinner'
|
5
13
|
import PresentationTool from './PresentationTool'
|
6
14
|
import {type PresentationPluginOptions} from './types'
|
7
|
-
import {usePreviewUrlActorRef} from './usePreviewUrlActorRef'
|
8
|
-
import {useReportInvalidPreviewSearchParam} from './useReportInvalidPreviewSearchParam'
|
9
15
|
import {useVercelBypassSecret} from './useVercelBypassSecret'
|
10
16
|
|
11
|
-
export default function PresentationToolGrantsCheck({
|
12
|
-
tool,
|
13
|
-
}: {
|
17
|
+
export default function PresentationToolGrantsCheck(props: {
|
14
18
|
tool: Tool<PresentationPluginOptions>
|
15
19
|
}): React.JSX.Element {
|
16
|
-
const
|
17
|
-
|
20
|
+
const {t} = useTranslation(presentationLocaleNamespace)
|
21
|
+
const {previewUrl} = props.tool.options ?? {}
|
22
|
+
const {push: pushToast} = useToast()
|
23
|
+
const willGeneratePreviewUrlSecret =
|
24
|
+
typeof previewUrl === 'object' || typeof previewUrl === 'function'
|
25
|
+
const grantsStore = useGrantsStore()
|
26
|
+
const [previewAccessSharingCreatePermission, setCreateAccessSharingPermission] =
|
27
|
+
useState<PermissionCheckResult | null>(null)
|
28
|
+
const [previewAccessSharingUpdatePermission, setUpdateAccessSharingPermission] =
|
29
|
+
useState<PermissionCheckResult | null>(null)
|
30
|
+
const [previewAccessSharingReadPermission, setReadAccessSharingPermission] =
|
31
|
+
useState<PermissionCheckResult | null>(null)
|
32
|
+
const [previewUrlSecretPermission, setPreviewUrlSecretPermission] =
|
33
|
+
useState<PermissionCheckResult | null>(null)
|
34
|
+
|
35
|
+
useEffect(() => {
|
36
|
+
if (!willGeneratePreviewUrlSecret) return undefined
|
37
|
+
|
38
|
+
const previewCreateAccessSharingPermissionSubscription = grantsStore
|
39
|
+
.checkDocumentPermission('create', {_id: schemaIdSingleton, _type: schemaTypeSingleton})
|
40
|
+
.subscribe(setCreateAccessSharingPermission)
|
41
|
+
const previewUpdateAccessSharingPermissionSubscription = grantsStore
|
42
|
+
.checkDocumentPermission('update', {_id: schemaIdSingleton, _type: schemaTypeSingleton})
|
43
|
+
.subscribe(setUpdateAccessSharingPermission)
|
44
|
+
const previewReadAccessSharingPermissionSubscription = grantsStore
|
45
|
+
.checkDocumentPermission('read', {_id: schemaIdSingleton, _type: schemaTypeSingleton})
|
46
|
+
.subscribe(setReadAccessSharingPermission)
|
47
|
+
const previewUrlSecretPermissionSubscription = grantsStore
|
48
|
+
.checkDocumentPermission('create', {_id: `drafts.${uuid()}`, _type: schemaType})
|
49
|
+
.subscribe(setPreviewUrlSecretPermission)
|
50
|
+
|
51
|
+
return () => {
|
52
|
+
previewCreateAccessSharingPermissionSubscription.unsubscribe()
|
53
|
+
previewUpdateAccessSharingPermissionSubscription.unsubscribe()
|
54
|
+
previewReadAccessSharingPermissionSubscription.unsubscribe()
|
55
|
+
previewUrlSecretPermissionSubscription.unsubscribe()
|
56
|
+
}
|
57
|
+
}, [grantsStore, willGeneratePreviewUrlSecret])
|
58
|
+
|
59
|
+
const canCreateUrlPreviewSecrets = previewUrlSecretPermission?.granted
|
60
|
+
|
61
|
+
useEffect(() => {
|
62
|
+
if (!willGeneratePreviewUrlSecret || canCreateUrlPreviewSecrets !== false) return undefined
|
63
|
+
const raf = requestAnimationFrame(() =>
|
64
|
+
pushToast({
|
65
|
+
closable: true,
|
66
|
+
status: 'error',
|
67
|
+
duration: 30_000,
|
68
|
+
title: t('preview-url-secret.missing-grants'),
|
69
|
+
}),
|
70
|
+
)
|
71
|
+
return () => cancelAnimationFrame(raf)
|
72
|
+
}, [canCreateUrlPreviewSecrets, pushToast, t, willGeneratePreviewUrlSecret])
|
18
73
|
|
19
|
-
const previewAccessSharingCreatePermission = useSelector(
|
20
|
-
previewUrlRef,
|
21
|
-
(state) => state.context.previewAccessSharingCreatePermission,
|
22
|
-
)
|
23
|
-
const previewAccessSharingUpdatePermission = useSelector(
|
24
|
-
previewUrlRef,
|
25
|
-
(state) => state.context.previewAccessSharingUpdatePermission,
|
26
|
-
)
|
27
|
-
const previewAccessSharingReadPermission = useSelector(
|
28
|
-
previewUrlRef,
|
29
|
-
(state) => state.context.previewAccessSharingReadPermission,
|
30
|
-
)
|
31
|
-
const previewUrlSecretPermission = useSelector(
|
32
|
-
previewUrlRef,
|
33
|
-
(state) => state.context.previewUrlSecretPermission,
|
34
|
-
)
|
35
|
-
const url = useSelector(previewUrlRef, (state) => state.context.previewUrl)
|
36
|
-
// @TODO the vercel protection bypass can be moved to the iframe level
|
37
74
|
const [vercelProtectionBypass, vercelProtectionBypassReadyState] = useVercelBypassSecret()
|
38
75
|
|
39
76
|
if (
|
40
|
-
!url ||
|
41
77
|
vercelProtectionBypassReadyState === 'loading' ||
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
78
|
+
(willGeneratePreviewUrlSecret &&
|
79
|
+
(!previewAccessSharingCreatePermission ||
|
80
|
+
typeof previewAccessSharingCreatePermission.granted === 'undefined' ||
|
81
|
+
!previewAccessSharingUpdatePermission ||
|
82
|
+
typeof previewAccessSharingUpdatePermission.granted === 'undefined' ||
|
83
|
+
!previewUrlSecretPermission ||
|
84
|
+
!previewAccessSharingReadPermission ||
|
85
|
+
typeof previewAccessSharingReadPermission.granted === 'undefined' ||
|
86
|
+
typeof previewUrlSecretPermission.granted === 'undefined'))
|
50
87
|
) {
|
51
88
|
return <PresentationSpinner />
|
52
89
|
}
|
53
90
|
|
54
91
|
return (
|
55
92
|
<PresentationTool
|
56
|
-
|
57
|
-
initialPreviewUrl={url}
|
93
|
+
{...props}
|
58
94
|
vercelProtectionBypass={vercelProtectionBypass}
|
95
|
+
canCreateUrlPreviewSecrets={canCreateUrlPreviewSecrets === true}
|
59
96
|
canToggleSharePreviewAccess={
|
60
97
|
previewAccessSharingCreatePermission?.granted === true &&
|
61
98
|
previewAccessSharingUpdatePermission?.granted === true
|
62
99
|
}
|
63
100
|
canUseSharedPreviewAccess={previewAccessSharingReadPermission?.granted === true}
|
64
|
-
previewUrlRef={previewUrlRef}
|
65
101
|
/>
|
66
102
|
)
|
67
103
|
}
|