Skip to content

Commit bd8a282

Browse files
committed
feat: add support for source in projection store
This adds support for `source` parameter in `useDocumentProjection` in a backwards compatible manner. In the core package we've now requiring `source` as a parameter.
1 parent e3650f5 commit bd8a282

File tree

5 files changed

+34
-28
lines changed

5 files changed

+34
-28
lines changed

packages/core/src/projection/getProjectionState.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {NEVER} from 'rxjs'
22
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
33

4+
import {sourceFor} from '../config/sanityConfig'
45
import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
56
import {type StoreState} from '../store/createStoreState'
67
import {hashString} from '../utils/hashString'
@@ -30,7 +31,11 @@ vi.mock('./subscribeToStateAndFetchBatches.ts')
3031

3132
describe('getProjectionState', () => {
3233
let instance: SanityInstance
33-
const docHandle = {documentId: 'exampleId', documentType: 'exampleType'}
34+
const docHandle = {
35+
documentId: 'exampleId',
36+
documentType: 'exampleType',
37+
source: sourceFor({projectId: 'exampleProject', dataset: 'exampleDataset'}),
38+
}
3439
const projection1 = '{exampleProjection1}'
3540
const hash1 = hashString(projection1)
3641
const projection2 = '{exampleProjection2}'

packages/core/src/projection/getProjectionState.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {type SanityProjectionResult} from 'groq'
22
import {omit} from 'lodash-es'
33

4-
import {type DocumentHandle} from '../config/sanityConfig'
4+
import {type DocumentSource} from '../config/sanityConfig'
55
import {bindActionByDataset} from '../store/createActionBinder'
66
import {type SanityInstance} from '../store/createSanityInstance'
77
import {
@@ -15,13 +15,11 @@ import {projectionStore} from './projectionStore'
1515
import {type ProjectionStoreState, type ProjectionValuePending} from './types'
1616
import {PROJECTION_STATE_CLEAR_DELAY, STABLE_EMPTY_PROJECTION, validateProjection} from './util'
1717

18-
export interface ProjectionOptions<
19-
TProjection extends string = string,
20-
TDocumentType extends string = string,
21-
TDataset extends string = string,
22-
TProjectId extends string = string,
23-
> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {
18+
export interface ProjectionOptions<TProjection extends string = string> {
2419
projection: TProjection
20+
documentId: string
21+
documentType: string
22+
source: DocumentSource
2523
}
2624

2725
/**
@@ -34,7 +32,7 @@ export function getProjectionState<
3432
TProjectId extends string = string,
3533
>(
3634
instance: SanityInstance,
37-
options: ProjectionOptions<TProjection, TDocumentType, TDataset, TProjectId>,
35+
options: ProjectionOptions<TProjection>,
3836
): StateSource<
3937
| ProjectionValuePending<
4038
SanityProjectionResult<TProjection, TDocumentType, `${TProjectId}.${TDataset}`>
@@ -75,13 +73,13 @@ export const _getProjectionState = bindActionByDataset(
7573
createStateSourceAction({
7674
selector: (
7775
{state}: SelectorContext<ProjectionStoreState>,
78-
options: ProjectionOptions<string, string, string, string>,
76+
options: ProjectionOptions<string>,
7977
): ProjectionValuePending<object> | undefined => {
8078
const documentId = getPublishedId(options.documentId)
8179
const projectionHash = hashString(options.projection)
8280
return state.values[documentId]?.[projectionHash] ?? STABLE_EMPTY_PROJECTION
8381
},
84-
onSubscribe: ({state}, options: ProjectionOptions<string, string, string, string>) => {
82+
onSubscribe: ({state}, options: ProjectionOptions<string>) => {
8583
const {projection, ...docHandle} = options
8684
const subscriptionId = insecureRandomId()
8785
const documentId = getPublishedId(docHandle.documentId)

packages/core/src/projection/resolveProjection.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {of} from 'rxjs'
22
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
33

4-
import {createDocumentHandle} from '../config/handles'
4+
import {sourceFor} from '../config/sanityConfig'
55
import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
66
import {type StateSource} from '../store/createStateSourceAction'
77
import {getProjectionState} from './getProjectionState'
@@ -31,10 +31,11 @@ describe('resolveProjection', () => {
3131
})
3232

3333
it('resolves a projection and returns the first emitted value with results', async () => {
34-
const docHandle = createDocumentHandle({
34+
const docHandle = {
3535
documentId: 'doc123',
3636
documentType: 'movie',
37-
})
37+
source: sourceFor({projectId: 'p', dataset: 'd'}),
38+
}
3839
const projection = '{title}'
3940

4041
const result = await resolveProjection(instance, {...docHandle, projection})

packages/core/src/projection/resolveProjection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export function resolveProjection<
1515
TProjectId extends string = string,
1616
>(
1717
instance: SanityInstance,
18-
options: ProjectionOptions<TProjection, TDocumentType, TDataset, TProjectId>,
18+
options: ProjectionOptions<TProjection>,
1919
): Promise<
2020
ProjectionValuePending<
2121
SanityProjectionResult<TProjection, TDocumentType, `${TProjectId}.${TDataset}`>

packages/react/src/hooks/projection/useDocumentProjection.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
import {type DocumentHandle, getProjectionState, resolveProjection} from '@sanity/sdk'
1+
import {getProjectionState, resolveProjection} from '@sanity/sdk'
22
import {type SanityProjectionResult} from 'groq'
33
import {useCallback, useSyncExternalStore} from 'react'
44
import {distinctUntilChanged, EMPTY, Observable, startWith, switchMap} from 'rxjs'
55

6-
import {useSanityInstance} from '../context/useSanityInstance'
6+
import {type SourceOptions} from '../../type'
7+
import {useSanityInstanceAndSource} from '../context/useSanityInstance'
78

89
/**
910
* @public
1011
* @category Types
1112
*/
12-
export interface useDocumentProjectionOptions<
13-
TProjection extends string = string,
14-
TDocumentType extends string = string,
15-
TDataset extends string = string,
16-
TProjectId extends string = string,
17-
> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {
13+
export interface useDocumentProjectionOptions<TProjection extends string = string>
14+
extends SourceOptions {
15+
documentId: string
16+
documentType: string
1817
/** The GROQ projection string */
1918
projection: TProjection
2019
/** Optional parameters for the projection query */
@@ -115,7 +114,7 @@ export function useDocumentProjection<
115114
TDataset extends string = string,
116115
TProjectId extends string = string,
117116
>(
118-
options: useDocumentProjectionOptions<TProjection, TDocumentType, TDataset, TProjectId>,
117+
options: useDocumentProjectionOptions<TProjection>,
119118
): useDocumentProjectionResults<
120119
SanityProjectionResult<TProjection, TDocumentType, `${TProjectId}.${TDataset}`>
121120
>
@@ -173,14 +172,17 @@ export function useDocumentProjection<TData extends object>(
173172
// Implementation (no JSDoc needed here as it's covered by overloads)
174173
export function useDocumentProjection<TData extends object>({
175174
ref,
176-
projection,
175+
projectId,
176+
dataset,
177+
source,
177178
...docHandle
178179
}: useDocumentProjectionOptions): useDocumentProjectionResults<TData> {
179-
const instance = useSanityInstance(docHandle)
180-
const stateSource = getProjectionState<TData>(instance, {...docHandle, projection})
180+
const [instance, actualSource] = useSanityInstanceAndSource({projectId, dataset, source})
181+
const options = {...docHandle, source: actualSource}
182+
const stateSource = getProjectionState<TData>(instance, options)
181183

182184
if (stateSource.getCurrent()?.data === null) {
183-
throw resolveProjection(instance, {...docHandle, projection})
185+
throw resolveProjection(instance, options)
184186
}
185187

186188
// Create subscribe function for useSyncExternalStore

0 commit comments

Comments
 (0)