@sanity/sdk-react 2.18.0 → 2.19.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/dist/index.d.ts CHANGED
@@ -18,6 +18,7 @@ import { DocumentEvent } from "@sanity/sdk";
18
18
  import { DocumentHandle as DocumentHandle_2 } from "@sanity/sdk";
19
19
  import { DocumentOptions } from "@sanity/sdk";
20
20
  import { DocumentPermissionsResult } from "@sanity/sdk";
21
+ import { DocumentPresence } from "@sanity/sdk";
21
22
  import { DocumentResource } from "@sanity/sdk";
22
23
  import { DocumentTypeHandle as DocumentTypeHandle_2 } from "@sanity/sdk";
23
24
  import { FallbackProps } from "react-error-boundary";
@@ -31,7 +32,9 @@ import { Organization } from "@sanity/sdk";
31
32
  import { OrganizationOptions } from "@sanity/sdk";
32
33
  import { Organizations } from "@sanity/sdk";
33
34
  import { OrganizationsOptions } from "@sanity/sdk";
35
+ import { Path } from "@sanity/types";
34
36
  import { PathChangeMessage } from "@sanity/message-protocol";
37
+ import { PresenceSelection } from "@sanity/sdk";
35
38
  import { PreviewValue } from "@sanity/sdk";
36
39
  import { Project } from "@sanity/sdk";
37
40
  import { ProjectHandle } from "@sanity/sdk";
@@ -153,10 +156,7 @@ export declare interface AuthBoundaryProps {
153
156
  * @public
154
157
  */
155
158
  export declare type ComlinkStatus =
156
- | "idle"
157
- | "handshaking"
158
- | "connected"
159
- | "disconnected";
159
+ "idle" | "handshaking" | "connected" | "disconnected";
160
160
 
161
161
  /**
162
162
  * This provider is used to provide the Comlink token refresh feature.
@@ -327,11 +327,7 @@ export declare interface FrameConnection<TFrameMessage extends FrameMessage> {
327
327
  }
328
328
 
329
329
  declare type IgnoredKey =
330
- | "_id"
331
- | "_type"
332
- | "_rev"
333
- | "_createdAt"
334
- | "_updatedAt";
330
+ "_id" | "_type" | "_rev" | "_createdAt" | "_updatedAt";
335
331
 
336
332
  /**
337
333
  * @alpha
@@ -3066,9 +3062,7 @@ export declare function useManageFavorite({
3066
3062
  declare interface UseManageFavoriteProps extends DocumentHandle_2 {
3067
3063
  resourceId?: string;
3068
3064
  resourceType:
3069
- | StudioResource["type"]
3070
- | MediaResource["type"]
3071
- | CanvasResource["type"];
3065
+ StudioResource["type"] | MediaResource["type"] | CanvasResource["type"];
3072
3066
  /**
3073
3067
  * The name of the schema collection this document belongs to.
3074
3068
  * Typically is the name of the workspace when used in the context of a studio.
@@ -3312,13 +3306,80 @@ export declare function usePerspective(
3312
3306
  ): string | string[];
3313
3307
 
3314
3308
  /**
3315
- * A hook for subscribing to presence information for the current project or Canvas.
3309
+ * Every participant in the current project and dataset, or Canvas.
3310
+ *
3311
+ * Reading presence never announces anything. Call `useReportPresence` to make the
3312
+ * current user visible to others, including to the Studio, which shares the same
3313
+ * presence room.
3314
+ *
3315
+ * This returns everyone in the whole resource and leaves the filtering to you.
3316
+ * Prefer `usePresenceForDocument` when you care about one document: it scopes and
3317
+ * flattens the result for rendering. Note that participants are counted by session,
3318
+ * so one person in two tabs appears twice.
3319
+ *
3320
+ * Presence is scoped to a single project and dataset. It is not a list of everyone
3321
+ * signed in to your organization.
3322
+ *
3316
3323
  * @public
3317
3324
  */
3318
3325
  export declare function usePresence(options?: ResourceHandle): {
3319
3326
  locations: UserPresence[];
3320
3327
  };
3321
3328
 
3329
+ /**
3330
+ * Who else is in a document, flattened to one entry per participant per location
3331
+ * so it can be rendered straight against a field.
3332
+ *
3333
+ * Reading presence never announces anything. Use `useReportPresence` to make the
3334
+ * current user visible to others.
3335
+ *
3336
+ * Prefer this over `usePresence` when you care about one document: `usePresence`
3337
+ * returns every participant in the whole project and dataset, leaving the filtering
3338
+ * to you.
3339
+ *
3340
+ * Resolves the document through its perspective exactly as `useReportPresence` does,
3341
+ * so reads match writes. Participants are counted by session, so one person in two
3342
+ * tabs appears twice.
3343
+ *
3344
+ * @example Avatars on a document
3345
+ * ```tsx
3346
+ * const {presence} = usePresenceForDocument({documentId, documentType})
3347
+ * return presence.map((p) => <Avatar key={p.sessionId} user={p.user} />)
3348
+ * ```
3349
+ *
3350
+ * @example Avatars on a single field
3351
+ * ```tsx
3352
+ * const {presence} = usePresenceForDocument({
3353
+ * documentId,
3354
+ * documentType,
3355
+ * path: ['title'],
3356
+ * excludeVersions: true,
3357
+ * })
3358
+ * ```
3359
+ *
3360
+ * @beta
3361
+ */
3362
+ export declare function usePresenceForDocument(
3363
+ options: UsePresenceForDocumentOptions,
3364
+ ): {
3365
+ presence: DocumentPresence[];
3366
+ };
3367
+
3368
+ /** @beta */
3369
+ export declare interface UsePresenceForDocumentOptions extends DocumentHandle {
3370
+ /**
3371
+ * Narrows to participants at or below this field path, which is what a field
3372
+ * indicator wants. Omit it for everyone in the document.
3373
+ */
3374
+ path?: Path;
3375
+ /**
3376
+ * By default a draft, its published version, and any release versions count as
3377
+ * the same document, which is what document lists want. Set this to compare ids
3378
+ * exactly, so that a draft and a release version do not bleed into each other.
3379
+ */
3380
+ excludeVersions?: boolean;
3381
+ }
3382
+
3322
3383
  /**
3323
3384
  * Returns metadata for a given project.
3324
3385
  *
@@ -3567,9 +3628,7 @@ export declare function useRecordDocumentHistoryEvent({
3567
3628
  */
3568
3629
  declare interface UseRecordDocumentHistoryEventProps extends DocumentHandle_2 {
3569
3630
  resourceType:
3570
- | StudioResource["type"]
3571
- | MediaResource["type"]
3572
- | CanvasResource["type"];
3631
+ StudioResource["type"] | MediaResource["type"] | CanvasResource["type"];
3573
3632
  resourceId?: string;
3574
3633
  /**
3575
3634
  * The name of the schema collection this document belongs to.
@@ -3578,6 +3637,74 @@ declare interface UseRecordDocumentHistoryEventProps extends DocumentHandle_2 {
3578
3637
  schemaName?: string;
3579
3638
  }
3580
3639
 
3640
+ /**
3641
+ * Announces that the current user is in a document, so that other clients in the
3642
+ * same project and dataset can show them.
3643
+ *
3644
+ * Writing presence is opt-in. Reading it with `usePresenceForDocument` or
3645
+ * `usePresence` never announces anything, and this hook is the only thing that
3646
+ * makes an app visible to others. That includes the Studio, which shares the same
3647
+ * presence room and will show these users in its navbar and field indicators.
3648
+ *
3649
+ * Announcements are throttled, collapsed over a short window, and then repeated
3650
+ * every 30 seconds while the user is idle. That repeat is what tells peers the
3651
+ * session is still alive, so the intended usage is to mount this hook for as long
3652
+ * as the user is in the document. On unmount the location is cleared, leaving the
3653
+ * user present in the app but not in any particular document.
3654
+ *
3655
+ * The perspective decides which specific document is reported: the draft under
3656
+ * `drafts`, the published document under `published`, a version under a release.
3657
+ * It is taken from `ResourceProvider` unless you pass one on the handle. That
3658
+ * matters for interoperability, because the Studio's field indicators compare the
3659
+ * exact id its form is on, so a mismatch shows your user at document level while
3660
+ * never lighting up a field.
3661
+ *
3662
+ * Presence is scoped to a single project and dataset. It is not a list of everyone
3663
+ * signed in to your organization.
3664
+ *
3665
+ * @example Document-level presence
3666
+ * ```tsx
3667
+ * function DocumentEditor({documentId, documentType}: DocumentHandle) {
3668
+ * useReportPresence({documentId, documentType})
3669
+ * return <Editor />
3670
+ * }
3671
+ * ```
3672
+ *
3673
+ * @example Presence in a release version
3674
+ * ```tsx
3675
+ * // The document id stays plain; the perspective selects the version.
3676
+ * useReportPresence({documentId, documentType, perspective: {releaseName: 'autumn'}})
3677
+ * ```
3678
+ *
3679
+ * @example Field-level presence
3680
+ * ```tsx
3681
+ * function TitleField({documentId, documentType}: DocumentHandle) {
3682
+ * const [focused, setFocused] = useState(false)
3683
+ * useReportPresence({documentId, documentType, path: focused ? ['title'] : undefined})
3684
+ * return <input onFocus={() => setFocused(true)} onBlur={() => setFocused(false)} />
3685
+ * }
3686
+ * ```
3687
+ *
3688
+ * @beta
3689
+ */
3690
+ export declare function useReportPresence(
3691
+ options: UseReportPresenceOptions,
3692
+ ): void;
3693
+
3694
+ /** @beta */
3695
+ export declare interface UseReportPresenceOptions extends DocumentHandle {
3696
+ /**
3697
+ * The focused field path. Omit it for document-level presence. Keyed and numeric
3698
+ * segments are supported, so array items and Portable Text spans can be
3699
+ * addressed.
3700
+ */
3701
+ path?: Path;
3702
+ /** The Portable Text caret, when the focused field is a Portable Text field. */
3703
+ selection?: PresenceSelection;
3704
+ /** Overrides the throttle interval. Mainly useful in tests. */
3705
+ throttleMs?: number;
3706
+ }
3707
+
3581
3708
  /**
3582
3709
  * Returns the currently active `DocumentResource` for the nearest resource context.
3583
3710
  *
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import { c } from "react-compiler-runtime";
3
3
  import { ClientError, CorsOriginError } from "@sanity/client";
4
- import { getAuthState, getNodeState, getIsInDashboardState, isStudioConfig, setAuthToken, AuthStateType, getLoginUrlState, observeOrganizationVerificationState, handleAuthCallback, logout, isProjectUserNotFoundClientError, getClientErrorApiDescription, getClientErrorApiBody, isImportError, getCorsErrorProjectId, getDashboardOrganizationId, getClientState, isDatasetResource, createSanityInstance, agentGenerate, agentPatch, agentPrompt, agentTransform, agentTranslate, getTokenState, getCurrentUserState, getOrCreateController, getOrCreateChannel, releaseChannel, isMediaLibraryResource, isCanvasResource, getFavoritesState, resolveFavoritesState, resolveDatasets, getDatasetsState, applyDocumentActions, createDocument, resolveDocument, getDocumentState, subscribeDocumentEvents, getPermissionsState, getDocumentSyncStatus, editDocument, getQueryKey, parseQueryKey, getQueryState, resolveQuery, createGroqSearchFilter, resolveOrganization, getOrganizationState, resolveOrganizations, getOrganizationsState, getPresence, getProjectionState, resolveProjection, transformProjectionToPreview, PREVIEW_PROJECTION, resolveProject, getProjectState, resolveProjects, getProjectsState, getActiveReleasesState, getAllReleasesState, getPerspectiveState, getUsersKey, parseUsersKey, getUsersState, resolveUsers, loadMoreUsers } from "@sanity/sdk";
4
+ import { getAuthState, getNodeState, getIsInDashboardState, isStudioConfig, setAuthToken, AuthStateType, getLoginUrlState, observeOrganizationVerificationState, handleAuthCallback, logout, isProjectUserNotFoundClientError, getClientErrorApiDescription, getClientErrorApiBody, isImportError, getCorsErrorProjectId, getDashboardOrganizationId, getClientState, isDatasetResource, createSanityInstance, agentGenerate, agentPatch, agentPrompt, agentTransform, agentTranslate, getTokenState, getCurrentUserState, getOrCreateController, getOrCreateChannel, releaseChannel, isMediaLibraryResource, isCanvasResource, getFavoritesState, resolveFavoritesState, resolveDatasets, getDatasetsState, applyDocumentActions, createDocument, resolveDocument, getDocumentState, subscribeDocumentEvents, getPermissionsState, getDocumentSyncStatus, editDocument, getQueryKey, parseQueryKey, getQueryState, resolveQuery, createGroqSearchFilter, resolveOrganization, getOrganizationState, resolveOrganizations, getOrganizationsState, getPresence, getDocumentPresence, reportPresence, getProjectionState, resolveProjection, PREVIEW_PROJECTION, transformProjectionToPreview, resolveProject, getProjectState, resolveProjects, getProjectsState, getActiveReleasesState, getAllReleasesState, getPerspectiveState, getUsersKey, parseUsersKey, getUsersState, resolveUsers, loadMoreUsers } from "@sanity/sdk";
5
5
  export * from "@sanity/sdk";
6
6
  import { createContext, useContext, useSyncExternalStore, useRef, useEffect, useState, Suspense, use, StrictMode, useCallback, useMemo, useInsertionEffect, useTransition } from "react";
7
7
  import { getErrorMessage, ErrorBoundary } from "react-error-boundary";
@@ -1798,6 +1798,137 @@ function usePresence(t0) {
1798
1798
  locations: t6
1799
1799
  }, $[12] = t6, $[13] = t7) : t7 = $[13], t7;
1800
1800
  }
1801
+ function usePresenceForDocument(options) {
1802
+ const $ = c(23);
1803
+ let excludeVersions, handle, path;
1804
+ $[0] !== options ? ({
1805
+ path,
1806
+ excludeVersions,
1807
+ ...handle
1808
+ } = options, $[0] = options, $[1] = excludeVersions, $[2] = handle, $[3] = path) : (excludeVersions = $[1], handle = $[2], path = $[3]);
1809
+ const normalizedOptions = useNormalizedResourceOptions(handle);
1810
+ if (normalizedOptions.resource && isMediaLibraryResource(normalizedOptions.resource))
1811
+ throw new Error("usePresenceForDocument() does not support media library resources. Presence tracking requires a canvas or dataset resource.");
1812
+ const sanityInstance = useSanityInstance();
1813
+ trackHookUsage(sanityInstance, "usePresenceForDocument");
1814
+ const {
1815
+ resource,
1816
+ perspective
1817
+ } = normalizedOptions, {
1818
+ documentId,
1819
+ liveEdit
1820
+ } = options;
1821
+ let t0;
1822
+ $[4] !== path ? (t0 = JSON.stringify(path ?? null), $[4] = path, $[5] = t0) : t0 = $[5];
1823
+ const pathKey = t0;
1824
+ let t1;
1825
+ $[6] !== documentId || $[7] !== excludeVersions || $[8] !== liveEdit || $[9] !== pathKey || $[10] !== perspective || $[11] !== resource || $[12] !== sanityInstance ? (t1 = getDocumentPresence(sanityInstance, {
1826
+ ...resource ? {
1827
+ resource
1828
+ } : {},
1829
+ documentId,
1830
+ ...perspective ? {
1831
+ perspective
1832
+ } : {},
1833
+ ...liveEdit ? {
1834
+ liveEdit
1835
+ } : {},
1836
+ ...pathKey === "null" ? {} : {
1837
+ path: JSON.parse(pathKey)
1838
+ },
1839
+ ...excludeVersions === void 0 ? {} : {
1840
+ excludeVersions
1841
+ }
1842
+ }), $[6] = documentId, $[7] = excludeVersions, $[8] = liveEdit, $[9] = pathKey, $[10] = perspective, $[11] = resource, $[12] = sanityInstance, $[13] = t1) : t1 = $[13];
1843
+ const source = t1;
1844
+ let t2;
1845
+ $[14] !== source ? (t2 = (callback) => source.subscribe(callback), $[14] = source, $[15] = t2) : t2 = $[15];
1846
+ const subscribe = t2;
1847
+ let t3, t4;
1848
+ $[16] !== source ? (t3 = () => source.getCurrent(), t4 = () => source.getCurrent(), $[16] = source, $[17] = t3, $[18] = t4) : (t3 = $[17], t4 = $[18]);
1849
+ const presence = useSyncExternalStore(subscribe, t3, t4);
1850
+ let t5;
1851
+ $[19] !== presence ? (t5 = presence || [], $[19] = presence, $[20] = t5) : t5 = $[20];
1852
+ let t6;
1853
+ return $[21] !== t5 ? (t6 = {
1854
+ presence: t5
1855
+ }, $[21] = t5, $[22] = t6) : t6 = $[22], t6;
1856
+ }
1857
+ const FOCUS_THROTTLE_MS = 1e3, SELECTION_THROTTLE_MS = 250;
1858
+ function useReportPresence(options) {
1859
+ const $ = c(37);
1860
+ let handle, path, selection, throttleMs;
1861
+ $[0] !== options ? ({
1862
+ path,
1863
+ selection,
1864
+ throttleMs,
1865
+ ...handle
1866
+ } = options, $[0] = options, $[1] = handle, $[2] = path, $[3] = selection, $[4] = throttleMs) : (handle = $[1], path = $[2], selection = $[3], throttleMs = $[4]);
1867
+ const normalizedOptions = useNormalizedResourceOptions(handle);
1868
+ if (normalizedOptions.resource && isMediaLibraryResource(normalizedOptions.resource))
1869
+ throw new Error("useReportPresence() does not support media library resources. Presence tracking requires a canvas or dataset resource.");
1870
+ const sanityInstance = useSanityInstance();
1871
+ trackHookUsage(sanityInstance, "useReportPresence");
1872
+ const {
1873
+ resource,
1874
+ perspective
1875
+ } = normalizedOptions, {
1876
+ documentId,
1877
+ liveEdit
1878
+ } = options, interval = throttleMs ?? (selection ? SELECTION_THROTTLE_MS : FOCUS_THROTTLE_MS);
1879
+ let t0;
1880
+ $[5] !== documentId || $[6] !== liveEdit || $[7] !== path || $[8] !== perspective || $[9] !== selection ? (t0 = JSON.stringify([documentId, path ?? [], selection ?? null, perspective ?? null, liveEdit]), $[5] = documentId, $[6] = liveEdit, $[7] = path, $[8] = perspective, $[9] = selection, $[10] = t0) : t0 = $[10];
1881
+ const locationKey = t0;
1882
+ let t1;
1883
+ $[11] !== locationKey ? (t1 = JSON.parse(locationKey), $[11] = locationKey, $[12] = t1) : t1 = $[12];
1884
+ const [id, parsedPath, parsedSelection, parsedPerspective, parsedLiveEdit] = t1;
1885
+ let t2;
1886
+ $[13] !== parsedPerspective ? (t2 = parsedPerspective ? {
1887
+ perspective: parsedPerspective
1888
+ } : {}, $[13] = parsedPerspective, $[14] = t2) : t2 = $[14];
1889
+ let t3;
1890
+ $[15] !== parsedLiveEdit ? (t3 = parsedLiveEdit ? {
1891
+ liveEdit: parsedLiveEdit
1892
+ } : {}, $[15] = parsedLiveEdit, $[16] = t3) : t3 = $[16];
1893
+ let t4;
1894
+ $[17] !== parsedPath ? (t4 = parsedPath.length > 0 ? {
1895
+ path: parsedPath
1896
+ } : {}, $[17] = parsedPath, $[18] = t4) : t4 = $[18];
1897
+ let t5;
1898
+ $[19] !== parsedSelection ? (t5 = parsedSelection ? {
1899
+ selection: parsedSelection
1900
+ } : {}, $[19] = parsedSelection, $[20] = t5) : t5 = $[20];
1901
+ let t6;
1902
+ $[21] !== id || $[22] !== t2 || $[23] !== t3 || $[24] !== t4 || $[25] !== t5 ? (t6 = {
1903
+ documentId: id,
1904
+ ...t2,
1905
+ ...t3,
1906
+ ...t4,
1907
+ ...t5
1908
+ }, $[21] = id, $[22] = t2, $[23] = t3, $[24] = t4, $[25] = t5, $[26] = t6) : t6 = $[26];
1909
+ const location2 = t6, lastSentAt = useRef(0), pending = useRef(void 0);
1910
+ let t7, t8;
1911
+ $[27] !== interval || $[28] !== location2 || $[29] !== resource || $[30] !== sanityInstance ? (t7 = () => {
1912
+ const send = () => {
1913
+ lastSentAt.current = Date.now(), reportPresence(sanityInstance, {
1914
+ ...resource ? {
1915
+ resource
1916
+ } : {},
1917
+ locations: [location2]
1918
+ });
1919
+ }, elapsed = Date.now() - lastSentAt.current;
1920
+ return elapsed >= interval ? send() : (clearTimeout(pending.current), pending.current = setTimeout(send, interval - elapsed)), () => clearTimeout(pending.current);
1921
+ }, t8 = [location2, interval, sanityInstance, resource], $[27] = interval, $[28] = location2, $[29] = resource, $[30] = sanityInstance, $[31] = t7, $[32] = t8) : (t7 = $[31], t8 = $[32]), useEffect(t7, t8);
1922
+ let t10, t9;
1923
+ $[33] !== resource || $[34] !== sanityInstance ? (t9 = () => () => {
1924
+ reportPresence(sanityInstance, {
1925
+ ...resource ? {
1926
+ resource
1927
+ } : {},
1928
+ locations: []
1929
+ });
1930
+ }, t10 = [sanityInstance, resource], $[33] = resource, $[34] = sanityInstance, $[35] = t10, $[36] = t9) : (t10 = $[35], t9 = $[36]), useEffect(t9, t10);
1931
+ }
1801
1932
  function useDocumentProjection(t0) {
1802
1933
  const $ = c(13);
1803
1934
  let docHandle, projection, ref;
@@ -2015,7 +2146,7 @@ function useUsers(options) {
2015
2146
  loadMore
2016
2147
  }, $[20] = data, $[21] = hasMore, $[22] = isPending, $[23] = loadMore, $[24] = t8) : t8 = $[24], t8;
2017
2148
  }
2018
- var version = "2.18.0";
2149
+ var version = "2.19.0";
2019
2150
  function getEnv(key) {
2020
2151
  if (typeof import.meta < "u" && import.meta.env)
2021
2152
  return import.meta.env[key];
@@ -2073,10 +2204,12 @@ export {
2073
2204
  usePaginatedDocuments,
2074
2205
  usePerspective,
2075
2206
  usePresence,
2207
+ usePresenceForDocument,
2076
2208
  useProject,
2077
2209
  useProjects,
2078
2210
  useQuery,
2079
2211
  useRecordDocumentHistoryEvent,
2212
+ useReportPresence,
2080
2213
  useResource,
2081
2214
  useSanityInstance,
2082
2215
  useStudioWorkspacesByProjectIdDataset,