diff --git a/packages/sanity/src/core/comments/__workshop__/CommentsProviderStory.tsx b/packages/sanity/src/core/comments/__workshop__/CommentsProviderStory.tsx index 12645005a09..2c04aebd263 100644 --- a/packages/sanity/src/core/comments/__workshop__/CommentsProviderStory.tsx +++ b/packages/sanity/src/core/comments/__workshop__/CommentsProviderStory.tsx @@ -4,7 +4,6 @@ import {useMemo} from 'react' import {ConditionalWrapper} from '../../../ui-components' import {useCurrentUser} from '../../store' -import {AddonDatasetProvider} from '../../studio' import {CommentsList, CommentsUpsellPanel} from '../components' import {CommentsEnabledProvider, CommentsProvider, CommentsUpsellProvider} from '../context' import {useComments, useCommentsUpsell} from '../hooks' @@ -24,19 +23,17 @@ export default function CommentsProviderStory() { const _mode = useSelect('_mode', MODES) || ('default' as keyof typeof MODES) return ( - - - - {children}} - > - - - - - + + + {children}} + > + + + + ) } diff --git a/packages/sanity/src/core/comments/context/comments/CommentsProvider.tsx b/packages/sanity/src/core/comments/context/comments/CommentsProvider.tsx index e15c5a4eb0d..9c99ab8ba68 100644 --- a/packages/sanity/src/core/comments/context/comments/CommentsProvider.tsx +++ b/packages/sanity/src/core/comments/context/comments/CommentsProvider.tsx @@ -1,11 +1,12 @@ import {type Path} from '@sanity/types' import {orderBy} from 'lodash' import {memo, type ReactNode, useCallback, useMemo, useState} from 'react' +import {useObservable} from 'react-rx' import {CommentsContext} from 'sanity/_singletons' import {useEditState, useSchema, useUserListWithPermissions} from '../../../hooks' -import {useCurrentUser} from '../../../store' -import {useAddonDataset, useWorkspace} from '../../../studio' +import {useAddonDatasetStore, useCurrentUser} from '../../../store' +import {useWorkspace} from '../../../studio' import {getPublishedId} from '../../../util' import { type CommentOperationsHookOptions, @@ -81,11 +82,12 @@ export const CommentsProvider = memo(function CommentsProvider(props: CommentsPr } = props const commentsEnabled = useCommentsEnabled() const [status, setStatus] = useState('open') - const {client, createAddonDataset, isCreatingDataset} = useAddonDataset() + const {lazyClient$} = useAddonDatasetStore() const publishedId = getPublishedId(documentId) const editState = useEditState(publishedId, documentType, 'low') const schemaType = useSchema().get(documentType) const currentUser = useCurrentUser() + const {client} = useObservable(lazyClient$)! const {name: workspaceName, dataset, projectId} = useWorkspace() @@ -226,7 +228,6 @@ export const CommentsProvider = memo(function CommentsProvider(props: CommentsPr const {operation} = useCommentOperations( useMemo( (): CommentOperationsHookOptions => ({ - client, currentUser, dataset, documentId: publishedId, @@ -237,10 +238,6 @@ export const CommentsProvider = memo(function CommentsProvider(props: CommentsPr projectId, schemaType, workspace: workspaceName, - // This function runs when the first comment creation is executed. - // It is used to create the addon dataset and configure a client for - // the addon dataset. - createAddonDataset, // The following callbacks runs when the comment operation are executed. // They are used to update the local state of the comments immediately after // a comment operation has been executed. This is done to avoid waiting for @@ -254,7 +251,6 @@ export const CommentsProvider = memo(function CommentsProvider(props: CommentsPr getCommentLink, }), [ - client, currentUser, dataset, publishedId, @@ -265,7 +261,6 @@ export const CommentsProvider = memo(function CommentsProvider(props: CommentsPr projectId, schemaType, workspaceName, - createAddonDataset, handleOnCreate, handleOnCreateError, handleOnUpdate, @@ -280,7 +275,6 @@ export const CommentsProvider = memo(function CommentsProvider(props: CommentsPr documentId, documentType, - isCreatingDataset, status, setStatus: handleSetStatus, getComment, @@ -298,7 +292,7 @@ export const CommentsProvider = memo(function CommentsProvider(props: CommentsPr comments: { data: threadItemsByStatus, error, - loading: loading || isCreatingDataset || isConnecting || false, + loading: loading || isConnecting || false, }, operation: { @@ -312,7 +306,6 @@ export const CommentsProvider = memo(function CommentsProvider(props: CommentsPr [ documentId, documentType, - isCreatingDataset, status, handleSetStatus, getComment, diff --git a/packages/sanity/src/core/comments/context/comments/types.ts b/packages/sanity/src/core/comments/context/comments/types.ts index 1b4c79e8ca6..c926796d887 100644 --- a/packages/sanity/src/core/comments/context/comments/types.ts +++ b/packages/sanity/src/core/comments/context/comments/types.ts @@ -21,8 +21,6 @@ export interface CommentsContextValue { selectedCommentId?: string | undefined onClearSelectedComment?: () => void - isCreatingDataset: boolean - isCommentsOpen?: boolean onCommentsOpen?: () => void diff --git a/packages/sanity/src/core/comments/hooks/use-comment-operations/createOperation.ts b/packages/sanity/src/core/comments/hooks/use-comment-operations/createOperation.ts index efc0114b216..f0c3bb81927 100644 --- a/packages/sanity/src/core/comments/hooks/use-comment-operations/createOperation.ts +++ b/packages/sanity/src/core/comments/hooks/use-comment-operations/createOperation.ts @@ -1,8 +1,9 @@ -import {type SanityClient} from '@sanity/client' import {type CurrentUser} from '@sanity/types' import {uuid} from '@sanity/uuid' +import {filter, switchMap, tap} from 'rxjs' import {type Tool} from '../../../config' +import {type AddonDatasetStore} from '../../../studio' import { type CommentContext, type CommentCreatePayload, @@ -14,7 +15,6 @@ import {weakenReferencesInContentSnapshot} from '../../utils' interface CreateOperationProps { activeTool: Tool | undefined - client: SanityClient | null comment: CommentCreatePayload currentUser: CurrentUser dataset: string @@ -28,14 +28,13 @@ interface CreateOperationProps { onCreate?: (comment: CommentPostPayload) => void onCreateError: (id: string, error: Error) => void projectId: string - createAddonDataset: () => Promise workspace: string + addonDatasetStore: AddonDatasetStore } export async function createOperation(props: CreateOperationProps): Promise { const { activeTool, - client, comment, currentUser, dataset, @@ -48,8 +47,8 @@ export async function createOperation(props: CreateOperationProps): Promise clientStore.state === 'ready'), + switchMap(({client}) => client.observable.create(nextComment)), + tap({ + error: (error) => onCreateError?.(nextComment._id, error), + }), + ) + .subscribe() } diff --git a/packages/sanity/src/core/comments/hooks/use-comment-operations/reactOperation.ts b/packages/sanity/src/core/comments/hooks/use-comment-operations/reactOperation.ts index 22584550941..2d9ed4cbae3 100644 --- a/packages/sanity/src/core/comments/hooks/use-comment-operations/reactOperation.ts +++ b/packages/sanity/src/core/comments/hooks/use-comment-operations/reactOperation.ts @@ -1,6 +1,7 @@ -import {type SanityClient} from '@sanity/client' import {type CurrentUser} from '@sanity/types' +import {filter, firstValueFrom, switchMap} from 'rxjs' +import {type AddonDatasetStore} from '../../../studio' import { type CommentDocument, type CommentReactionItem, @@ -18,16 +19,16 @@ function createReactionKey(userId: string, shortName: CommentReactionShortNames) } interface ReactOperationProps { - client: SanityClient currentUser: CurrentUser id: string reaction: CommentReactionOption getComment?: (id: string) => CommentDocument | undefined onUpdate?: (id: string, comment: CommentUpdatePayload) => void + addonDatasetStore: AddonDatasetStore } export async function reactOperation(props: ReactOperationProps): Promise { - const {client, currentUser, id, reaction, getComment, onUpdate} = props + const {currentUser, id, reaction, getComment, onUpdate, addonDatasetStore} = props const reactions = getComment?.(id)?.reactions || [] const currentUserReactions = reactions.filter((r) => r.userId === currentUser.id) @@ -55,10 +56,17 @@ export async function reactOperation(props: ReactOperationProps): Promise onUpdate?.(id, {reactions: next}) // Unset the reaction - await client - .patch(id) - .unset([`reactions[_key=="${_key}"]`]) - .commit() + await firstValueFrom( + addonDatasetStore.client$.pipe( + filter((clientStore) => clientStore.state === 'ready'), + switchMap(({client}) => + client.observable + .patch(id) + .unset([`reactions[_key=="${_key}"]`]) + .commit(), + ), + ), + ) return } @@ -83,10 +91,17 @@ export async function reactOperation(props: ReactOperationProps): Promise onUpdate?.(id, {reactions: next}) // Append the new reaction to the comment - await client - .patch(id) - .setIfMissing({reactions: []}) - .append('reactions', [reactionItem]) - .commit() + await firstValueFrom( + addonDatasetStore.client$.pipe( + filter((clientStore) => clientStore.state === 'ready'), + switchMap(({client}) => + client + .patch(id) + .setIfMissing({reactions: []}) + .append('reactions', [reactionItem]) + .commit(), + ), + ), + ) } } diff --git a/packages/sanity/src/core/comments/hooks/use-comment-operations/removeOperation.ts b/packages/sanity/src/core/comments/hooks/use-comment-operations/removeOperation.ts index b31857b14d3..d1e2e4b6567 100644 --- a/packages/sanity/src/core/comments/hooks/use-comment-operations/removeOperation.ts +++ b/packages/sanity/src/core/comments/hooks/use-comment-operations/removeOperation.ts @@ -1,17 +1,26 @@ -import {type SanityClient} from '@sanity/client' +import {filter, firstValueFrom, switchMap, zip} from 'rxjs' + +import {type AddonDatasetStore} from '../../../studio' interface RemoveOperationProps { - client: SanityClient id: string onRemove?: (id: string) => void + addonDatasetStore: AddonDatasetStore } export async function removeOperation(props: RemoveOperationProps): Promise { - const {client, id, onRemove} = props + const {id, onRemove, addonDatasetStore} = props onRemove?.(id) - await Promise.all([ - client.delete({query: `*[_type == "comment" && parentCommentId == "${id}"]`}), - client.delete(id), - ]) + await firstValueFrom( + addonDatasetStore.client$.pipe( + filter((clientStore) => clientStore.state === 'ready'), + switchMap(({client}) => + zip( + client.observable.delete({query: `*[_type == "comment" && parentCommentId == "${id}"]`}), + client.observable.delete(id), + ), + ), + ), + ) } diff --git a/packages/sanity/src/core/comments/hooks/use-comment-operations/updateOperation.ts b/packages/sanity/src/core/comments/hooks/use-comment-operations/updateOperation.ts index d50501ea254..e70dca744f0 100644 --- a/packages/sanity/src/core/comments/hooks/use-comment-operations/updateOperation.ts +++ b/packages/sanity/src/core/comments/hooks/use-comment-operations/updateOperation.ts @@ -1,6 +1,7 @@ -import {type SanityClient} from '@sanity/client' import {uuid} from '@sanity/uuid' -import {throttle, type ThrottleSettings} from 'lodash' +import {type ThrottleSettings} from 'lodash' +import {filter, firstValueFrom, map, throttle} from 'rxjs' +import {type AddonDatasetStore} from 'sanity' import {type CommentUpdatePayload} from '../../types' @@ -32,20 +33,27 @@ function getThrottledFunction(id: string) { } interface UpdateOperationProps { - client: SanityClient comment: CommentUpdatePayload throttled: boolean | undefined id: string onUpdate?: (id: string, comment: CommentUpdatePayload) => void transactionId: string | undefined + addonDatasetStore: AddonDatasetStore } async function postCommentUpdate(props: UpdateOperationProps) { - const {client, id, comment, transactionId: transactionIdProp, onUpdate} = props + const {id, comment, transactionId: transactionIdProp, onUpdate, addonDatasetStore} = props + + const client = await firstValueFrom( + addonDatasetStore.client$.pipe( + filter((clientStore) => clientStore.state === 'ready'), + map((clientStore) => clientStore.client), + ), + ) // Fall back to generating a new transaction id if none is provided const transactionId = transactionIdProp || uuid() - const patch = client?.patch(id).set(comment) + const patch = client.patch(id).set(comment) const transaction = client.transaction().transactionId(transactionId).patch(patch) onUpdate?.(id, comment) diff --git a/packages/sanity/src/core/comments/hooks/use-comment-operations/useCommentOperations.ts b/packages/sanity/src/core/comments/hooks/use-comment-operations/useCommentOperations.ts index b2202caca1d..c16378fe88e 100644 --- a/packages/sanity/src/core/comments/hooks/use-comment-operations/useCommentOperations.ts +++ b/packages/sanity/src/core/comments/hooks/use-comment-operations/useCommentOperations.ts @@ -1,7 +1,7 @@ -import {type SanityClient} from '@sanity/client' import {type CurrentUser, type SchemaType} from '@sanity/types' import {uuid} from '@sanity/uuid' import {useCallback, useMemo} from 'react' +import {useAddonDatasetStore} from 'sanity' import {useRouterState} from 'sanity/router' import {useTools} from '../../../hooks' @@ -26,7 +26,6 @@ export interface CommentOperationsHookValue { } export interface CommentOperationsHookOptions { - client: SanityClient | null currentUser: CurrentUser | null dataset: string documentId: string @@ -40,7 +39,6 @@ export interface CommentOperationsHookOptions { onTransactionStart: (commentDocumentId: string, transactionId: string) => void onUpdate?: (id: string, comment: CommentUpdatePayload) => void projectId: string - createAddonDataset: () => Promise schemaType: SchemaType | undefined workspace: string getCommentLink?: (commentId: string) => string @@ -50,7 +48,6 @@ export function useCommentOperations( opts: CommentOperationsHookOptions, ): CommentOperationsHookValue { const { - client, currentUser, dataset, documentId, @@ -64,7 +61,6 @@ export function useCommentOperations( onTransactionStart, onUpdate, projectId, - createAddonDataset, workspace, getCommentLink, } = opts @@ -85,17 +81,14 @@ export function useCommentOperations( ) const {getNotificationValue} = useNotificationTarget({documentId, documentType, getCommentLink}) + const addonDatasetStore = useAddonDatasetStore() + const handleCreate = useCallback( async (comment: CommentCreatePayload) => { - // Unlike the other operations, we want to proceed with create operation even - // though there is no client available. This is because if there is no client for the - // comments addon dataset, it will be created in the `createOperation`, and the - // comment will be created in that dataset when the client is eventually created. if (!currentUser?.id) return await createOperation({ activeTool, - client, comment, currentUser, dataset, @@ -108,13 +101,12 @@ export function useCommentOperations( onCreate, onCreateError, projectId, - createAddonDataset, workspace, + addonDatasetStore, }) }, [ activeTool, - client, currentUser, dataset, documentId, @@ -126,22 +118,20 @@ export function useCommentOperations( onCreate, onCreateError, projectId, - createAddonDataset, workspace, + addonDatasetStore, ], ) const handleRemove = useCallback( async (id: string) => { - if (!client) return - await removeOperation({ - client, id, onRemove, + addonDatasetStore, }) }, - [client, onRemove], + [addonDatasetStore, onRemove], ) const handleUpdate = useCallback( @@ -150,7 +140,6 @@ export function useCommentOperations( comment: CommentUpdatePayload, updateOpts?: CommentUpdateOperationOptions, ) => { - if (!client) return const {throttled} = updateOpts || {} // Generate a new transaction ID to use for the update operation transaction @@ -166,31 +155,31 @@ export function useCommentOperations( onTransactionStart(id, nextTransactionId) await updateOperation({ - client, comment, throttled, id, onUpdate, transactionId: nextTransactionId, + addonDatasetStore, }) }, - [client, onTransactionStart, onUpdate], + [addonDatasetStore, onTransactionStart, onUpdate], ) const handleReact = useCallback( async (id: string, reaction: CommentReactionOption) => { - if (!client || !currentUser?.id) return + if (!currentUser?.id) return await reactOperation({ - client, currentUser, id, reaction, getComment, onUpdate, + addonDatasetStore, }) }, - [client, currentUser, getComment, onUpdate], + [addonDatasetStore, currentUser, getComment, onUpdate], ) return useMemo( diff --git a/packages/sanity/src/core/comments/plugin/field/CommentsField.tsx b/packages/sanity/src/core/comments/plugin/field/CommentsField.tsx index 1dd1fb8145d..73d52fd1912 100644 --- a/packages/sanity/src/core/comments/plugin/field/CommentsField.tsx +++ b/packages/sanity/src/core/comments/plugin/field/CommentsField.tsx @@ -90,16 +90,8 @@ function CommentFieldInner( const rootRef = useRef(null) - const { - comments, - isCommentsOpen, - isCreatingDataset, - mentionOptions, - onCommentsOpen, - operation, - setStatus, - status, - } = useComments() + const {comments, isCommentsOpen, mentionOptions, onCommentsOpen, operation, setStatus, status} = + useComments() const {upsellData, handleOpenDialog} = useCommentsUpsell() const {selectedPath, setSelectedPath} = useCommentsSelectedPath() const {authoringPath, setAuthoringPath} = useCommentsAuthoringPath() @@ -285,7 +277,6 @@ function CommentFieldInner( count={Number(count)} currentUser={currentUser} fieldTitle={fieldTitle} - isCreatingDataset={isCreatingDataset} mentionOptions={mentionOptions} onChange={handleOnChange} onClick={handleClick} @@ -303,7 +294,6 @@ function CommentFieldInner( currentUser, count, fieldTitle, - isCreatingDataset, mentionOptions, handleOnChange, handleClick, diff --git a/packages/sanity/src/core/comments/plugin/field/CommentsFieldButton.tsx b/packages/sanity/src/core/comments/plugin/field/CommentsFieldButton.tsx index 83803e69396..b95b9b7fce2 100644 --- a/packages/sanity/src/core/comments/plugin/field/CommentsFieldButton.tsx +++ b/packages/sanity/src/core/comments/plugin/field/CommentsFieldButton.tsx @@ -27,7 +27,6 @@ interface CommentsFieldButtonProps { count: number currentUser: CurrentUser fieldTitle: string - isCreatingDataset: boolean mentionOptions: UserListWithPermissionsHookValue onChange: (value: PortableTextBlock[]) => void onClick?: () => void @@ -44,7 +43,6 @@ export function CommentsFieldButton(props: CommentsFieldButtonProps) { count, currentUser, fieldTitle, - isCreatingDataset, mentionOptions, onChange, onClick, @@ -149,7 +147,6 @@ export function CommentsFieldButton(props: CommentsFieldButtonProps) { onKeyDown={handleInputKeyDown} onSubmit={handleSubmit} placeholder={placeholder} - readOnly={isCreatingDataset} ref={commentInputHandle} value={value} /> @@ -170,7 +167,6 @@ export function CommentsFieldButton(props: CommentsFieldButtonProps) {