Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion packages/core/src/projection/getProjectionState.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {NEVER} from 'rxjs'
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'

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

describe('getProjectionState', () => {
let instance: SanityInstance
const docHandle = {documentId: 'exampleId', documentType: 'exampleType'}
const docHandle = {
documentId: 'exampleId',
documentType: 'exampleType',
source: sourceFor({projectId: 'exampleProject', dataset: 'exampleDataset'}),
}
const projection1 = '{exampleProjection1}'
const hash1 = hashString(projection1)
const projection2 = '{exampleProjection2}'
Expand Down
18 changes: 8 additions & 10 deletions packages/core/src/projection/getProjectionState.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {type SanityProjectionResult} from 'groq'
import {omit} from 'lodash-es'

import {type DocumentHandle} from '../config/sanityConfig'
import {type DocumentSource} from '../config/sanityConfig'
import {bindActionByDataset} from '../store/createActionBinder'
import {type SanityInstance} from '../store/createSanityInstance'
import {
Expand All @@ -15,13 +15,11 @@ import {projectionStore} from './projectionStore'
import {type ProjectionStoreState, type ProjectionValuePending} from './types'
import {PROJECTION_STATE_CLEAR_DELAY, STABLE_EMPTY_PROJECTION, validateProjection} from './util'

export interface ProjectionOptions<
TProjection extends string = string,
TDocumentType extends string = string,
TDataset extends string = string,
TProjectId extends string = string,
> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {
export interface ProjectionOptions<TProjection extends string = string> {
projection: TProjection
documentId: string
documentType: string
source: DocumentSource
}

/**
Expand All @@ -34,7 +32,7 @@ export function getProjectionState<
TProjectId extends string = string,
>(
instance: SanityInstance,
options: ProjectionOptions<TProjection, TDocumentType, TDataset, TProjectId>,
options: ProjectionOptions<TProjection>,
): StateSource<
| ProjectionValuePending<
SanityProjectionResult<TProjection, TDocumentType, `${TProjectId}.${TDataset}`>
Expand Down Expand Up @@ -75,13 +73,13 @@ export const _getProjectionState = bindActionByDataset(
createStateSourceAction({
selector: (
{state}: SelectorContext<ProjectionStoreState>,
options: ProjectionOptions<string, string, string, string>,
options: ProjectionOptions<string>,
): ProjectionValuePending<object> | undefined => {
const documentId = getPublishedId(options.documentId)
const projectionHash = hashString(options.projection)
return state.values[documentId]?.[projectionHash] ?? STABLE_EMPTY_PROJECTION
},
onSubscribe: ({state}, options: ProjectionOptions<string, string, string, string>) => {
onSubscribe: ({state}, options: ProjectionOptions<string>) => {
const {projection, ...docHandle} = options
const subscriptionId = insecureRandomId()
const documentId = getPublishedId(docHandle.documentId)
Expand Down
7 changes: 4 additions & 3 deletions packages/core/src/projection/resolveProjection.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {of} from 'rxjs'
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'

import {createDocumentHandle} from '../config/handles'
import {sourceFor} from '../config/sanityConfig'
import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
import {type StateSource} from '../store/createStateSourceAction'
import {getProjectionState} from './getProjectionState'
Expand Down Expand Up @@ -31,10 +31,11 @@ describe('resolveProjection', () => {
})

it('resolves a projection and returns the first emitted value with results', async () => {
const docHandle = createDocumentHandle({
const docHandle = {
documentId: 'doc123',
documentType: 'movie',
})
source: sourceFor({projectId: 'p', dataset: 'd'}),
}
const projection = '{title}'

const result = await resolveProjection(instance, {...docHandle, projection})
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/projection/resolveProjection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function resolveProjection<
TProjectId extends string = string,
>(
instance: SanityInstance,
options: ProjectionOptions<TProjection, TDocumentType, TDataset, TProjectId>,
options: ProjectionOptions<TProjection>,
): Promise<
ProjectionValuePending<
SanityProjectionResult<TProjection, TDocumentType, `${TProjectId}.${TDataset}`>
Expand Down
28 changes: 15 additions & 13 deletions packages/react/src/hooks/projection/useDocumentProjection.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import {type DocumentHandle, getProjectionState, resolveProjection} from '@sanity/sdk'
import {getProjectionState, resolveProjection} from '@sanity/sdk'
import {type SanityProjectionResult} from 'groq'
import {useCallback, useSyncExternalStore} from 'react'
import {distinctUntilChanged, EMPTY, Observable, startWith, switchMap} from 'rxjs'

import {useSanityInstance} from '../context/useSanityInstance'
import {type SourceOptions} from '../../type'
import {useSanityInstanceAndSource} from '../context/useSanityInstance'

/**
* @public
* @category Types
*/
export interface useDocumentProjectionOptions<
TProjection extends string = string,
TDocumentType extends string = string,
TDataset extends string = string,
TProjectId extends string = string,
> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {
export interface useDocumentProjectionOptions<TProjection extends string = string>
extends SourceOptions {
documentId: string
documentType: string
/** The GROQ projection string */
projection: TProjection
/** Optional parameters for the projection query */
Expand Down Expand Up @@ -115,7 +114,7 @@ export function useDocumentProjection<
TDataset extends string = string,
TProjectId extends string = string,
>(
options: useDocumentProjectionOptions<TProjection, TDocumentType, TDataset, TProjectId>,
options: useDocumentProjectionOptions<TProjection>,
): useDocumentProjectionResults<
SanityProjectionResult<TProjection, TDocumentType, `${TProjectId}.${TDataset}`>
>
Expand Down Expand Up @@ -173,14 +172,17 @@ export function useDocumentProjection<TData extends object>(
// Implementation (no JSDoc needed here as it's covered by overloads)
export function useDocumentProjection<TData extends object>({
ref,
projection,
projectId,
dataset,
source,
...docHandle
}: useDocumentProjectionOptions): useDocumentProjectionResults<TData> {
const instance = useSanityInstance(docHandle)
const stateSource = getProjectionState<TData>(instance, {...docHandle, projection})
const [instance, actualSource] = useSanityInstanceAndSource({projectId, dataset, source})
const options = {...docHandle, source: actualSource}
const stateSource = getProjectionState<TData>(instance, options)

if (stateSource.getCurrent()?.data === null) {
throw resolveProjection(instance, {...docHandle, projection})
throw resolveProjection(instance, options)
}

// Create subscribe function for useSyncExternalStore
Expand Down
Loading