Skip to content

Commit

Permalink
refactor(sanity): move addon dataset handling to resource cache
Browse files Browse the repository at this point in the history
  • Loading branch information
juice49 committed Jul 16, 2024
1 parent 9dbb9f6 commit 88c0e0b
Show file tree
Hide file tree
Showing 23 changed files with 374 additions and 385 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -24,19 +23,17 @@ export default function CommentsProviderStory() {
const _mode = useSelect('_mode', MODES) || ('default' as keyof typeof MODES)

return (
<AddonDatasetProvider>
<CommentsEnabledProvider documentType={_type} documentId={_id}>
<CommentsProvider documentType={_type} documentId={_id} type="field" sortOrder="desc">
<ConditionalWrapper
condition={_mode === 'upsell'}
// eslint-disable-next-line react/jsx-no-bind
wrapper={(children) => <CommentsUpsellProvider>{children}</CommentsUpsellProvider>}
>
<Inner mode={_mode} />
</ConditionalWrapper>
</CommentsProvider>
</CommentsEnabledProvider>
</AddonDatasetProvider>
<CommentsEnabledProvider documentType={_type} documentId={_id}>
<CommentsProvider documentType={_type} documentId={_id} type="field" sortOrder="desc">
<ConditionalWrapper
condition={_mode === 'upsell'}
// eslint-disable-next-line react/jsx-no-bind
wrapper={(children) => <CommentsUpsellProvider>{children}</CommentsUpsellProvider>}
>
<Inner mode={_mode} />
</ConditionalWrapper>
</CommentsProvider>
</CommentsEnabledProvider>
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -81,11 +82,12 @@ export const CommentsProvider = memo(function CommentsProvider(props: CommentsPr
} = props
const commentsEnabled = useCommentsEnabled()
const [status, setStatus] = useState<CommentStatus>('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()

Expand Down Expand Up @@ -226,7 +228,6 @@ export const CommentsProvider = memo(function CommentsProvider(props: CommentsPr
const {operation} = useCommentOperations(
useMemo(
(): CommentOperationsHookOptions => ({
client,
currentUser,
dataset,
documentId: publishedId,
Expand All @@ -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
Expand All @@ -254,7 +251,6 @@ export const CommentsProvider = memo(function CommentsProvider(props: CommentsPr
getCommentLink,
}),
[
client,
currentUser,
dataset,
publishedId,
Expand All @@ -265,7 +261,6 @@ export const CommentsProvider = memo(function CommentsProvider(props: CommentsPr
projectId,
schemaType,
workspaceName,
createAddonDataset,
handleOnCreate,
handleOnCreateError,
handleOnUpdate,
Expand All @@ -280,7 +275,6 @@ export const CommentsProvider = memo(function CommentsProvider(props: CommentsPr
documentId,
documentType,

isCreatingDataset,
status,
setStatus: handleSetStatus,
getComment,
Expand All @@ -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: {
Expand All @@ -312,7 +306,6 @@ export const CommentsProvider = memo(function CommentsProvider(props: CommentsPr
[
documentId,
documentType,
isCreatingDataset,
status,
handleSetStatus,
getComment,
Expand Down
2 changes: 0 additions & 2 deletions packages/sanity/src/core/comments/context/comments/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ export interface CommentsContextValue {
selectedCommentId?: string | undefined
onClearSelectedComment?: () => void

isCreatingDataset: boolean

isCommentsOpen?: boolean
onCommentsOpen?: () => void

Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -14,7 +15,6 @@ import {weakenReferencesInContentSnapshot} from '../../utils'

interface CreateOperationProps {
activeTool: Tool | undefined
client: SanityClient | null
comment: CommentCreatePayload
currentUser: CurrentUser
dataset: string
Expand All @@ -28,14 +28,13 @@ interface CreateOperationProps {
onCreate?: (comment: CommentPostPayload) => void
onCreateError: (id: string, error: Error) => void
projectId: string
createAddonDataset: () => Promise<SanityClient | null>
workspace: string
addonDatasetStore: AddonDatasetStore
}

export async function createOperation(props: CreateOperationProps): Promise<void> {
const {
activeTool,
client,
comment,
currentUser,
dataset,
Expand All @@ -48,8 +47,8 @@ export async function createOperation(props: CreateOperationProps): Promise<void
onCreate,
onCreateError,
projectId,
createAddonDataset,
workspace,
addonDatasetStore,
} = props

// The comment payload might already have an id if, for example, the comment was created
Expand Down Expand Up @@ -162,28 +161,13 @@ export async function createOperation(props: CreateOperationProps): Promise<void

onCreate?.(nextComment)

// If we don't have a client, that means that the dataset doesn't have an addon dataset.
// Therefore, when the first comment is created, we need to create the addon dataset and create
// a client for it and then post the comment. We do this here, since we know that we have a
// comment to create.
if (!client) {
try {
const newAddonClient = await createAddonDataset()
if (!newAddonClient) {
throw new Error('Failed to create addon dataset client')
}
await newAddonClient.create(nextComment)
} catch (err) {
onCreateError?.(nextComment._id, err)
throw err
}
return
}

try {
await client.create(nextComment)
} catch (err) {
onCreateError?.(nextComment._id, err)
throw err
}
addonDatasetStore.client$
.pipe(
filter((clientStore) => clientStore.state === 'ready'),
switchMap(({client}) => client.observable.create(nextComment)),
tap({
error: (error) => onCreateError?.(nextComment._id, error),
}),
)
.subscribe()
}
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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<void> {
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)
Expand Down Expand Up @@ -55,10 +56,17 @@ export async function reactOperation(props: ReactOperationProps): Promise<void>
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
}
Expand All @@ -83,10 +91,17 @@ export async function reactOperation(props: ReactOperationProps): Promise<void>
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(),
),
),
)
}
}
Original file line number Diff line number Diff line change
@@ -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<void> {
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),
),
),
),
)
}
Original file line number Diff line number Diff line change
@@ -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'

Expand Down Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 88c0e0b

Please sign in to comment.