@sanity/sdk-react 0.0.0-rc.0 → 0.0.0-rc.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.
Files changed (49) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.ts +398 -228
  3. package/dist/index.js +275 -166
  4. package/dist/index.js.map +1 -1
  5. package/package.json +11 -11
  6. package/src/_exports/index.ts +20 -12
  7. package/src/components/Login/LoginLinks.test.tsx +2 -2
  8. package/src/components/Login/LoginLinks.tsx +2 -2
  9. package/src/components/auth/AuthBoundary.test.tsx +2 -2
  10. package/src/components/auth/LoginCallback.test.tsx +3 -3
  11. package/src/components/auth/LoginCallback.tsx +4 -4
  12. package/src/hooks/auth/useCurrentUser.tsx +1 -0
  13. package/src/hooks/auth/useDashboardOrganizationId.test.tsx +42 -0
  14. package/src/hooks/auth/useDashboardOrganizationId.tsx +29 -0
  15. package/src/hooks/auth/useHandleAuthCallback.test.tsx +16 -0
  16. package/src/hooks/auth/{useHandleCallback.tsx → useHandleAuthCallback.tsx} +6 -6
  17. package/src/hooks/auth/useLogOut.test.tsx +2 -2
  18. package/src/hooks/client/useClient.ts +1 -0
  19. package/src/hooks/comlink/useFrameConnection.test.tsx +20 -10
  20. package/src/hooks/comlink/useFrameConnection.ts +33 -56
  21. package/src/hooks/comlink/useManageFavorite.test.ts +9 -4
  22. package/src/hooks/comlink/useManageFavorite.ts +46 -14
  23. package/src/hooks/comlink/useRecordDocumentHistoryEvent.test.ts +7 -3
  24. package/src/hooks/comlink/useRecordDocumentHistoryEvent.ts +45 -14
  25. package/src/hooks/comlink/useWindowConnection.test.ts +20 -10
  26. package/src/hooks/comlink/useWindowConnection.ts +69 -41
  27. package/src/hooks/dashboard/useNavigateToStudioDocument.test.ts +178 -0
  28. package/src/hooks/dashboard/useNavigateToStudioDocument.ts +123 -0
  29. package/src/hooks/dashboard/useStudioWorkspacesByResourceId.test.tsx +278 -0
  30. package/src/hooks/dashboard/useStudioWorkspacesByResourceId.ts +92 -0
  31. package/src/hooks/datasets/useDatasets.ts +6 -3
  32. package/src/hooks/document/useApplyDocumentActions.test.ts +25 -0
  33. package/src/hooks/document/{useApplyActions.ts → useApplyDocumentActions.ts} +13 -12
  34. package/src/hooks/document/{usePermissions.ts → useDocumentPermissions.ts} +12 -10
  35. package/src/hooks/document/useDocumentSyncStatus.ts +4 -1
  36. package/src/hooks/document/useEditDocument.test.ts +8 -8
  37. package/src/hooks/document/useEditDocument.ts +2 -2
  38. package/src/hooks/{infiniteList/useInfiniteList.test.tsx → documents/useDocuments.test.tsx} +9 -9
  39. package/src/hooks/{infiniteList/useInfiniteList.ts → documents/useDocuments.ts} +10 -10
  40. package/src/hooks/{paginatedList/usePaginatedList.test.tsx → paginatedDocuments/usePaginatedDocuments.test.tsx} +14 -14
  41. package/src/hooks/{paginatedList/usePaginatedList.ts → paginatedDocuments/usePaginatedDocuments.ts} +7 -7
  42. package/src/hooks/preview/usePreview.test.tsx +6 -6
  43. package/src/hooks/preview/usePreview.tsx +5 -5
  44. package/src/hooks/projection/useProjection.test.tsx +9 -9
  45. package/src/hooks/projection/useProjection.ts +27 -15
  46. package/src/hooks/projects/useProject.ts +4 -1
  47. package/src/hooks/projects/useProjects.ts +7 -3
  48. package/src/hooks/auth/useHandleCallback.test.tsx +0 -16
  49. package/src/hooks/document/useApplyActions.test.ts +0 -25
@@ -0,0 +1,123 @@
1
+ import {type Status} from '@sanity/comlink'
2
+ import {SDK_CHANNEL_NAME, SDK_NODE_NAME} from '@sanity/message-protocol'
3
+ import {type DocumentHandle} from '@sanity/sdk'
4
+ import {useCallback, useState} from 'react'
5
+
6
+ import {useWindowConnection} from '../comlink/useWindowConnection'
7
+ import {useStudioWorkspacesByResourceId} from './useStudioWorkspacesByResourceId'
8
+
9
+ interface NavigateToResourceMessage {
10
+ type: 'dashboard/v1/bridge/navigate-to-resource'
11
+ data: {
12
+ /**
13
+ * Resource ID
14
+ */
15
+ resourceId: string
16
+ /**
17
+ * Resource type
18
+ * @example 'application' | 'studio'
19
+ */
20
+ resourceType: string
21
+ /**
22
+ * Path within the resource to navigate to.
23
+ */
24
+ path?: string
25
+ }
26
+ }
27
+
28
+ interface NavigateToStudioResult {
29
+ navigateToStudioDocument: () => void
30
+ isConnected: boolean
31
+ }
32
+
33
+ /**
34
+ * @public
35
+ * Hook that provides a function to navigate to a studio document.
36
+ * Currently, requires a document handle with a resourceId.
37
+ * That resourceId is currently formatted like: `document:projectId.dataset:documentId`
38
+ * If the hook you used to retrieve the document handle doesn't provide a resourceId like this,
39
+ * you can construct it according to the above format with the document handle's _id.
40
+ *
41
+ * This will only work if you have deployed a studio with a workspace
42
+ * with this projectId / dataset combination.
43
+ * It may be able to take a custom URL in the future.
44
+ *
45
+ * This will likely change in the future.
46
+ * @param documentHandle - The document handle containing document ID, type, and resource ID
47
+ * @returns An object containing:
48
+ * - navigateToStudioDocument - Function that when called will navigate to the studio document
49
+ * - isConnected - Boolean indicating if connection to Dashboard is established
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * import {navigateToStudioDocument, type DocumentHandle} from '@sanity/sdk'
54
+ *
55
+ * function MyComponent({documentHandle}: {documentHandle: DocumentHandle}) {
56
+ * const {navigateToStudioDocument, isConnected} = useNavigateToStudioDocument(documentHandle)
57
+ *
58
+ * return (
59
+ * <button onClick={navigateToStudioDocument} disabled={!isConnected}>
60
+ * Navigate to Studio Document
61
+ * </button>
62
+ * )
63
+ * }
64
+ * ```
65
+ */
66
+ export function useNavigateToStudioDocument(
67
+ documentHandle: DocumentHandle,
68
+ ): NavigateToStudioResult {
69
+ const {workspacesByResourceId, isConnected: workspacesConnected} =
70
+ useStudioWorkspacesByResourceId()
71
+ const [status, setStatus] = useState<Status>('idle')
72
+ const {sendMessage} = useWindowConnection<NavigateToResourceMessage, never>({
73
+ name: SDK_NODE_NAME,
74
+ connectTo: SDK_CHANNEL_NAME,
75
+ onStatus: setStatus,
76
+ })
77
+
78
+ const navigateToStudioDocument = useCallback(() => {
79
+ if (!workspacesConnected || status !== 'connected' || !documentHandle.resourceId) {
80
+ return
81
+ }
82
+
83
+ // Extract projectId and dataset from the resourceId (current format: document:projectId.dataset:documentId)
84
+ const [, projectAndDataset] = documentHandle.resourceId.split(':')
85
+ const [projectId, dataset] = projectAndDataset.split('.')
86
+ if (!projectId || !dataset) {
87
+ return
88
+ }
89
+
90
+ // Find the workspace for this document
91
+ const workspaces = workspacesByResourceId[`${projectId}:${dataset}`]
92
+ if (!workspaces?.length) {
93
+ // eslint-disable-next-line no-console
94
+ console.warn('No workspace found for document', documentHandle.resourceId)
95
+ return
96
+ }
97
+
98
+ if (workspaces.length > 1) {
99
+ // eslint-disable-next-line no-console
100
+ console.warn('Multiple workspaces found for document', documentHandle.resourceId)
101
+ // eslint-disable-next-line no-console
102
+ console.warn('Using the first one', workspaces[0])
103
+ }
104
+
105
+ const workspace = workspaces[0]
106
+
107
+ const message: NavigateToResourceMessage = {
108
+ type: 'dashboard/v1/bridge/navigate-to-resource',
109
+ data: {
110
+ resourceId: workspace._ref,
111
+ resourceType: 'studio',
112
+ path: `/intent/edit/id=${documentHandle._id};type=${documentHandle._type}`,
113
+ },
114
+ }
115
+
116
+ sendMessage(message.type, message.data)
117
+ }, [documentHandle, workspacesConnected, status, sendMessage, workspacesByResourceId])
118
+
119
+ return {
120
+ navigateToStudioDocument,
121
+ isConnected: workspacesConnected && status === 'connected',
122
+ }
123
+ }
@@ -0,0 +1,278 @@
1
+ import {type Message, type Status} from '@sanity/comlink'
2
+ import {renderHook, waitFor} from '@testing-library/react'
3
+ import {describe, expect, it, vi} from 'vitest'
4
+
5
+ import {useWindowConnection, type WindowConnection} from '../comlink/useWindowConnection'
6
+ import {useStudioWorkspacesByResourceId} from './useStudioWorkspacesByResourceId'
7
+
8
+ vi.mock('../comlink/useWindowConnection', () => ({
9
+ useWindowConnection: vi.fn(),
10
+ }))
11
+
12
+ const mockWorkspaceData = {
13
+ context: {
14
+ availableResources: [
15
+ {
16
+ projectId: 'project1',
17
+ workspaces: [
18
+ {
19
+ name: 'workspace1',
20
+ title: 'Workspace 1',
21
+ basePath: '/workspace1',
22
+ dataset: 'dataset1',
23
+ userApplicationId: 'user1',
24
+ url: 'https://test.sanity.studio',
25
+ _ref: 'user1-workspace1',
26
+ },
27
+ {
28
+ name: 'workspace2',
29
+ title: 'Workspace 2',
30
+ basePath: '/workspace2',
31
+ dataset: 'dataset1',
32
+ userApplicationId: 'user1',
33
+ url: 'https://test.sanity.studio',
34
+ _ref: 'user1-workspace2',
35
+ },
36
+ ],
37
+ },
38
+ {
39
+ projectId: 'project2',
40
+ workspaces: [
41
+ {
42
+ name: 'workspace3',
43
+ title: 'Workspace 3',
44
+ basePath: '/workspace3',
45
+ dataset: 'dataset2',
46
+ userApplicationId: 'user2',
47
+ url: 'https://test.sanity.studio',
48
+ _ref: 'user2-workspace3',
49
+ },
50
+ ],
51
+ },
52
+ {
53
+ // Project without workspaces
54
+ projectId: 'project3',
55
+ workspaces: [],
56
+ },
57
+ ],
58
+ },
59
+ }
60
+
61
+ describe('useStudioWorkspacesByResourceId', () => {
62
+ it('should return empty workspaces and connected=false when not connected', async () => {
63
+ // Create a mock that captures the onStatus callback
64
+ let capturedOnStatus: ((status: Status) => void) | undefined
65
+
66
+ vi.mocked(useWindowConnection).mockImplementation(({onStatus}) => {
67
+ capturedOnStatus = onStatus
68
+
69
+ return {
70
+ fetch: undefined,
71
+ sendMessage: vi.fn(),
72
+ } as unknown as WindowConnection<Message>
73
+ })
74
+
75
+ const {result} = renderHook(() => useStudioWorkspacesByResourceId())
76
+
77
+ // Call onStatus with 'idle' to simulate not connected
78
+ if (capturedOnStatus) capturedOnStatus('idle')
79
+
80
+ expect(result.current).toEqual({
81
+ workspacesByResourceId: {},
82
+ error: null,
83
+ isConnected: false,
84
+ })
85
+ })
86
+
87
+ it('should process workspaces into lookup by projectId:dataset', async () => {
88
+ const mockFetch = vi.fn().mockResolvedValue(mockWorkspaceData)
89
+ let capturedOnStatus: ((status: Status) => void) | undefined
90
+
91
+ vi.mocked(useWindowConnection).mockImplementation(({onStatus}) => {
92
+ capturedOnStatus = onStatus
93
+
94
+ return {
95
+ fetch: mockFetch,
96
+ sendMessage: vi.fn(),
97
+ } as unknown as WindowConnection<Message>
98
+ })
99
+
100
+ const {result} = renderHook(() => useStudioWorkspacesByResourceId())
101
+
102
+ // Call onStatus with 'connected' to simulate connected state
103
+ if (capturedOnStatus) capturedOnStatus('connected')
104
+
105
+ await waitFor(() => {
106
+ expect(result.current.workspacesByResourceId).toEqual({
107
+ 'project1:dataset1': [
108
+ {
109
+ name: 'workspace1',
110
+ title: 'Workspace 1',
111
+ basePath: '/workspace1',
112
+ dataset: 'dataset1',
113
+ userApplicationId: 'user1',
114
+ url: 'https://test.sanity.studio',
115
+ _ref: 'user1-workspace1',
116
+ },
117
+ {
118
+ name: 'workspace2',
119
+ title: 'Workspace 2',
120
+ basePath: '/workspace2',
121
+ dataset: 'dataset1',
122
+ userApplicationId: 'user1',
123
+ url: 'https://test.sanity.studio',
124
+ _ref: 'user1-workspace2',
125
+ },
126
+ ],
127
+ 'project2:dataset2': [
128
+ {
129
+ name: 'workspace3',
130
+ title: 'Workspace 3',
131
+ basePath: '/workspace3',
132
+ dataset: 'dataset2',
133
+ userApplicationId: 'user2',
134
+ url: 'https://test.sanity.studio',
135
+ _ref: 'user2-workspace3',
136
+ },
137
+ ],
138
+ })
139
+ expect(result.current.error).toBeNull()
140
+ expect(result.current.isConnected).toBe(true)
141
+ })
142
+
143
+ expect(mockFetch).toHaveBeenCalledWith(
144
+ 'dashboard/v1/bridge/context',
145
+ undefined,
146
+ expect.any(Object),
147
+ )
148
+ })
149
+
150
+ it('should handle fetch errors', async () => {
151
+ const mockFetch = vi.fn().mockRejectedValue(new Error('Failed to fetch'))
152
+ let capturedOnStatus: ((status: Status) => void) | undefined
153
+
154
+ vi.mocked(useWindowConnection).mockImplementation(({onStatus}) => {
155
+ capturedOnStatus = onStatus
156
+
157
+ return {
158
+ fetch: mockFetch,
159
+ sendMessage: vi.fn(),
160
+ } as unknown as WindowConnection<Message>
161
+ })
162
+
163
+ const {result} = renderHook(() => useStudioWorkspacesByResourceId())
164
+
165
+ // Call onStatus with 'connected' to simulate connected state
166
+ if (capturedOnStatus) capturedOnStatus('connected')
167
+
168
+ await waitFor(() => {
169
+ expect(result.current.workspacesByResourceId).toEqual({})
170
+ expect(result.current.error).toBe('Failed to fetch workspaces')
171
+ expect(result.current.isConnected).toBe(true)
172
+ })
173
+ })
174
+
175
+ it('should handle AbortError silently', async () => {
176
+ const abortError = new Error('Aborted')
177
+ abortError.name = 'AbortError'
178
+ const mockFetch = vi.fn().mockRejectedValue(abortError)
179
+ let capturedOnStatus: ((status: Status) => void) | undefined
180
+
181
+ vi.mocked(useWindowConnection).mockImplementation(({onStatus}) => {
182
+ capturedOnStatus = onStatus
183
+
184
+ return {
185
+ fetch: mockFetch,
186
+ sendMessage: vi.fn(),
187
+ } as unknown as WindowConnection<Message>
188
+ })
189
+
190
+ const {result} = renderHook(() => useStudioWorkspacesByResourceId())
191
+
192
+ // Call onStatus with 'connected' to simulate connected state
193
+ if (capturedOnStatus) capturedOnStatus('connected')
194
+
195
+ await waitFor(() => {
196
+ expect(result.current.workspacesByResourceId).toEqual({})
197
+ expect(result.current.error).toBeNull()
198
+ expect(result.current.isConnected).toBe(true)
199
+ })
200
+ })
201
+
202
+ it('should handle projects without workspaces', async () => {
203
+ const mockFetch = vi.fn().mockResolvedValue({
204
+ context: {
205
+ availableResources: [
206
+ {
207
+ projectId: 'project1',
208
+ workspaces: [],
209
+ },
210
+ ],
211
+ },
212
+ })
213
+ let capturedOnStatus: ((status: Status) => void) | undefined
214
+
215
+ vi.mocked(useWindowConnection).mockImplementation(({onStatus}) => {
216
+ capturedOnStatus = onStatus
217
+
218
+ return {
219
+ fetch: mockFetch,
220
+ sendMessage: vi.fn(),
221
+ } as unknown as WindowConnection<Message>
222
+ })
223
+
224
+ const {result} = renderHook(() => useStudioWorkspacesByResourceId())
225
+
226
+ // Call onStatus with 'connected' to simulate connected state
227
+ if (capturedOnStatus) capturedOnStatus('connected')
228
+
229
+ await waitFor(() => {
230
+ expect(result.current.workspacesByResourceId).toEqual({})
231
+ expect(result.current.error).toBeNull()
232
+ expect(result.current.isConnected).toBe(true)
233
+ })
234
+ })
235
+
236
+ it('should handle projects without projectId', async () => {
237
+ const mockFetch = vi.fn().mockResolvedValue({
238
+ context: {
239
+ availableResources: [
240
+ {
241
+ workspaces: [
242
+ {
243
+ name: 'workspace1',
244
+ title: 'Workspace 1',
245
+ basePath: '/workspace1',
246
+ dataset: 'dataset1',
247
+ userApplicationId: 'user1',
248
+ url: 'https://test.sanity.studio',
249
+ _ref: 'user1-workspace1',
250
+ },
251
+ ],
252
+ },
253
+ ],
254
+ },
255
+ })
256
+ let capturedOnStatus: ((status: Status) => void) | undefined
257
+
258
+ vi.mocked(useWindowConnection).mockImplementation(({onStatus}) => {
259
+ capturedOnStatus = onStatus
260
+
261
+ return {
262
+ fetch: mockFetch,
263
+ sendMessage: vi.fn(),
264
+ } as unknown as WindowConnection<Message>
265
+ })
266
+
267
+ const {result} = renderHook(() => useStudioWorkspacesByResourceId())
268
+
269
+ // Call onStatus with 'connected' to simulate connected state
270
+ if (capturedOnStatus) capturedOnStatus('connected')
271
+
272
+ await waitFor(() => {
273
+ expect(result.current.workspacesByResourceId).toEqual({})
274
+ expect(result.current.error).toBeNull()
275
+ expect(result.current.isConnected).toBe(true)
276
+ })
277
+ })
278
+ })
@@ -0,0 +1,92 @@
1
+ import {type Status} from '@sanity/comlink'
2
+ import {SDK_CHANNEL_NAME, SDK_NODE_NAME} from '@sanity/message-protocol'
3
+ import {useEffect, useState} from 'react'
4
+
5
+ import {useWindowConnection} from '../comlink/useWindowConnection'
6
+
7
+ interface Workspace {
8
+ name: string
9
+ title: string
10
+ basePath: string
11
+ dataset: string
12
+ userApplicationId: string
13
+ url: string
14
+ _ref: string
15
+ }
16
+
17
+ interface WorkspacesByResourceId {
18
+ [key: string]: Workspace[] // key format: `${projectId}:${dataset}`
19
+ }
20
+
21
+ interface StudioWorkspacesResult {
22
+ workspacesByResourceId: WorkspacesByResourceId
23
+ error: string | null
24
+ isConnected: boolean
25
+ }
26
+
27
+ /**
28
+ * Hook that fetches studio workspaces and organizes them by projectId:dataset
29
+ * @internal
30
+ */
31
+ export function useStudioWorkspacesByResourceId(): StudioWorkspacesResult {
32
+ const [workspacesByResourceId, setWorkspacesByResourceId] = useState<WorkspacesByResourceId>({})
33
+ const [status, setStatus] = useState<Status>('idle')
34
+ const [error, setError] = useState<string | null>(null)
35
+
36
+ const {fetch} = useWindowConnection({
37
+ name: SDK_NODE_NAME,
38
+ connectTo: SDK_CHANNEL_NAME,
39
+ onStatus: setStatus,
40
+ })
41
+
42
+ // Once computed, this should probably be in a store and poll for changes
43
+ // However, our stores are currently being refactored
44
+ useEffect(() => {
45
+ if (!fetch || status !== 'connected') return
46
+
47
+ async function fetchWorkspaces(signal: AbortSignal) {
48
+ try {
49
+ const data = await fetch<{
50
+ context: {availableResources: Array<{projectId: string; workspaces: Workspace[]}>}
51
+ }>('dashboard/v1/bridge/context', undefined, {signal})
52
+
53
+ const workspaceMap: WorkspacesByResourceId = {}
54
+
55
+ data.context.availableResources.forEach((resource) => {
56
+ if (!resource.projectId || !resource.workspaces?.length) return
57
+
58
+ resource.workspaces.forEach((workspace) => {
59
+ const key = `${resource.projectId}:${workspace.dataset}`
60
+ if (!workspaceMap[key]) {
61
+ workspaceMap[key] = []
62
+ }
63
+ workspaceMap[key].push(workspace)
64
+ })
65
+ })
66
+
67
+ setWorkspacesByResourceId(workspaceMap)
68
+ setError(null)
69
+ } catch (err: unknown) {
70
+ if (err instanceof Error) {
71
+ if (err.name === 'AbortError') {
72
+ return
73
+ }
74
+ setError('Failed to fetch workspaces')
75
+ }
76
+ }
77
+ }
78
+
79
+ const controller = new AbortController()
80
+ fetchWorkspaces(controller.signal)
81
+
82
+ return () => {
83
+ controller.abort()
84
+ }
85
+ }, [fetch, status])
86
+
87
+ return {
88
+ workspacesByResourceId,
89
+ error,
90
+ isConnected: status === 'connected',
91
+ }
92
+ }
@@ -6,10 +6,10 @@ import {createStateSourceHook} from '../helpers/createStateSourceHook'
6
6
  type UseDatasets = {
7
7
  /**
8
8
  *
9
- * Returns metadata for each dataset in your organization.
9
+ * Returns metadata for each dataset the current user has access to.
10
10
  *
11
11
  * @category Datasets
12
- * @returns The metadata for your organization's datasets
12
+ * @returns The metadata for your the datasets
13
13
  *
14
14
  * @example
15
15
  * ```tsx
@@ -28,7 +28,10 @@ type UseDatasets = {
28
28
  (): DatasetsResponse
29
29
  }
30
30
 
31
- /** @public */
31
+ /**
32
+ * @public
33
+ * @function
34
+ */
32
35
  export const useDatasets: UseDatasets = createStateSourceHook({
33
36
  // remove `undefined` since we're suspending when that is the case
34
37
  getState: getDatasetsState as (instance: SanityInstance) => StateSource<DatasetsResponse>,
@@ -0,0 +1,25 @@
1
+ import {applyDocumentActions, createDocument, type ResourceId} from '@sanity/sdk'
2
+ import {describe, it} from 'vitest'
3
+
4
+ import {createCallbackHook} from '../helpers/createCallbackHook'
5
+
6
+ vi.mock('../helpers/createCallbackHook', () => ({
7
+ createCallbackHook: vi.fn((cb) => () => cb),
8
+ }))
9
+ vi.mock('@sanity/sdk', async (importOriginal) => {
10
+ const original = await importOriginal<typeof import('@sanity/sdk')>()
11
+ return {...original, applyDocumentActions: vi.fn()}
12
+ })
13
+
14
+ describe('useApplyDocumentActions', () => {
15
+ it('calls `createCallbackHook` with `applyDocumentActions`', async () => {
16
+ const {useApplyDocumentActions} = await import('./useApplyDocumentActions')
17
+ const resourceId: ResourceId = 'project1.dataset1'
18
+ expect(createCallbackHook).not.toHaveBeenCalled()
19
+
20
+ expect(applyDocumentActions).not.toHaveBeenCalled()
21
+ const apply = useApplyDocumentActions(resourceId)
22
+ apply(createDocument({_type: 'author'}))
23
+ expect(applyDocumentActions).toHaveBeenCalledWith(createDocument({_type: 'author'}))
24
+ })
25
+ })
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  type ActionsResult,
3
- applyActions,
4
- type ApplyActionsOptions,
3
+ applyDocumentActions,
4
+ type ApplyDocumentActionsOptions,
5
5
  type DocumentAction,
6
6
  type ResourceId,
7
7
  } from '@sanity/sdk'
@@ -21,9 +21,9 @@ import {createCallbackHook} from '../helpers/createCallbackHook'
21
21
  * @example Publish or unpublish a document
22
22
  * ```
23
23
  * import { publishDocument, unpublishDocument } from '@sanity/sdk'
24
- * import { useApplyActions } from '@sanity/sdk-react'
24
+ * import { useApplyDocumentActions } from '@sanity/sdk-react'
25
25
  *
26
- * const apply = useApplyActions()
26
+ * const apply = useApplyDocumentActions()
27
27
  * const myDocument = { _id: 'my-document-id', _type: 'my-document-type' }
28
28
  *
29
29
  * return (
@@ -35,9 +35,9 @@ import {createCallbackHook} from '../helpers/createCallbackHook'
35
35
  * @example Create and publish a new document
36
36
  * ```
37
37
  * import { createDocument, publishDocument } from '@sanity/sdk'
38
- * import { useApplyActions } from '@sanity/sdk-react'
38
+ * import { useApplyDocumentActions } from '@sanity/sdk-react'
39
39
  *
40
- * const apply = useApplyActions()
40
+ * const apply = useApplyDocumentActions()
41
41
  *
42
42
  * const handleCreateAndPublish = () => {
43
43
  * const handle = { _id: window.crypto.randomUUID(), _type: 'my-document-type' }
@@ -54,21 +54,22 @@ import {createCallbackHook} from '../helpers/createCallbackHook'
54
54
  * )
55
55
  * ```
56
56
  */
57
- export function useApplyActions(
57
+ export function useApplyDocumentActions(
58
58
  resourceId?: ResourceId,
59
59
  ): <TDocument extends SanityDocument>(
60
60
  action: DocumentAction<TDocument> | DocumentAction<TDocument>[],
61
- options?: ApplyActionsOptions,
61
+ options?: ApplyDocumentActionsOptions,
62
62
  ) => Promise<ActionsResult<TDocument>>
63
63
 
64
64
  /** @beta */
65
- export function useApplyActions(
65
+ export function useApplyDocumentActions(
66
66
  resourceId?: ResourceId,
67
67
  ): (
68
68
  action: DocumentAction | DocumentAction[],
69
- options?: ApplyActionsOptions,
69
+ options?: ApplyDocumentActionsOptions,
70
70
  ) => Promise<ActionsResult> {
71
- return _useApplyActions(resourceId)()
71
+ return _useApplyDocumentActions(resourceId)()
72
72
  }
73
73
 
74
- const _useApplyActions = (resourceId?: ResourceId) => createCallbackHook(applyActions, resourceId)
74
+ const _useApplyDocumentActions = (resourceId?: ResourceId) =>
75
+ createCallbackHook(applyDocumentActions, resourceId)
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  type DocumentAction,
3
+ type DocumentPermissionsResult,
3
4
  getPermissionsState,
4
5
  getResourceId,
5
- type PermissionsResult,
6
6
  } from '@sanity/sdk'
7
7
  import {useCallback, useMemo, useSyncExternalStore} from 'react'
8
8
  import {filter, firstValueFrom} from 'rxjs'
@@ -21,25 +21,25 @@ import {useSanityInstance} from '../context/useSanityInstance'
21
21
  *
22
22
  * @example Checking for permission to publish a document
23
23
  * ```ts
24
- * import {usePermissions, useApplyActions} from '@sanity/sdk-react'
24
+ * import {useDocumentPermissions, useApplyDocumentActions} from '@sanity/sdk-react'
25
25
  * import {publishDocument} from '@sanity/sdk'
26
26
  *
27
27
  * export function PublishButton({doc}: {doc: DocumentHandle}) {
28
- * const canPublish = usePermissions(publishDocument(doc))
29
- * const applyAction = useApplyActions()
28
+ * const publishPermissions = useDocumentPermissions(publishDocument(doc))
29
+ * const applyAction = useApplyDocumentActions()
30
30
  *
31
31
  * return (
32
32
  * <>
33
33
  * <button
34
- * disabled={!canPublish.allowed}
34
+ * disabled={!publishPermissions.allowed}
35
35
  * onClick={() => applyAction(publishDocument(doc))}
36
- * popoverTarget={`${canPublish.allowed ? undefined : 'publishButtonPopover'}`}
36
+ * popoverTarget={`${publishPermissions.allowed ? undefined : 'publishButtonPopover'}`}
37
37
  * >
38
38
  * Publish
39
39
  * </button>
40
- * {!canPublish.allowed && (
40
+ * {!publishPermissions.allowed && (
41
41
  * <div popover id="publishButtonPopover">
42
- * {canPublish.message}
42
+ * {publishPermissions.message}
43
43
  * </div>
44
44
  * )}
45
45
  * </>
@@ -47,7 +47,9 @@ import {useSanityInstance} from '../context/useSanityInstance'
47
47
  * }
48
48
  * ```
49
49
  */
50
- export function usePermissions(actions: DocumentAction | DocumentAction[]): PermissionsResult {
50
+ export function useDocumentPermissions(
51
+ actions: DocumentAction | DocumentAction[],
52
+ ): DocumentPermissionsResult {
51
53
  // if actions is an array, we need to check each action to see if the resourceId is the same
52
54
  if (Array.isArray(actions)) {
53
55
  const resourceIds = actions.map((action) => action.resourceId)
@@ -78,5 +80,5 @@ export function usePermissions(actions: DocumentAction | DocumentAction[]): Perm
78
80
  [actions, instance],
79
81
  )
80
82
 
81
- return useSyncExternalStore(subscribe, getCurrent) as PermissionsResult
83
+ return useSyncExternalStore(subscribe, getCurrent) as DocumentPermissionsResult
82
84
  }
@@ -25,6 +25,9 @@ type UseDocumentSyncStatus = {
25
25
  (doc: DocumentHandle): boolean | undefined
26
26
  }
27
27
 
28
- /** @beta */
28
+ /**
29
+ * @beta
30
+ * @function
31
+ */
29
32
  export const useDocumentSyncStatus: UseDocumentSyncStatus =
30
33
  createStateSourceHook(getDocumentSyncStatus)