Skip to content

Commit

Permalink
fix: revert the storing of cross dataset references in comment documents
Browse files Browse the repository at this point in the history
  • Loading branch information
hermanwikner authored and skogsmaskin committed Oct 24, 2023
1 parent 449e53e commit e48768b
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import {
CommentInputHandle,
hasCommentMessageValue,
AddCommentIcon,
CommentIcon,
} from '../../src'
import {CurrentUser, PortableTextBlock} from 'sanity'
import {CommentIcon} from '@sanity/icons'

const TOOLTIP_DELAY: TooltipProps['delay'] = {open: 500}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ const BASE: CommentDocument = {
_dataset: '1',
_projectId: '1',
_ref: '1',
_type: 'object',
_type: 'crossDatasetReference',
_weak: true,
},
},
message: [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './SendIcon'
export * from './MentionIcon'
export * from './AddCommentIcon'
export * from './CommentIcon'
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import React, {memo, useCallback, useMemo, useState} from 'react'
import {orderBy} from 'lodash'
import {CommentStatus, CommentThreadItem} from '../../types'
import {
CommentCreatePayload,
CommentEditPayload,
CommentPostPayload,
CommentStatus,
CommentThreadItem,
} from '../../types'
import {
CommentOperationsHookOptions,
MentionHookOptions,
Expand All @@ -9,7 +15,7 @@ import {
useCommentsSetup,
useMentionOptions,
} from '../../hooks'
import {CommentsStoreOptions, useCommentsStore} from '../../store'
import {useCommentsStore} from '../../store'
import {buildCommentThreadItems} from '../../utils/buildCommentThreadItems'
import {CommentsContext} from './CommentsContext'
import {CommentsContextValue, SelectedPath} from './types'
Expand Down Expand Up @@ -99,34 +105,26 @@ const CommentsProviderInner = memo(function CommentsProviderInner(
props: Omit<CommentsProviderProps, 'enabled'>,
) {
const {children, documentId, documentType} = props
const [selectedPath, setSelectedPath] = useState<SelectedPath>(null)
const [status, setStatus] = useState<CommentStatus>('open')

const {client, runSetup, isRunningSetup} = useCommentsSetup()

const publishedId = getPublishedId(documentId)
const editState = useEditState(publishedId, documentType, 'low')

const documentValue = useMemo(() => {
return editState.draft || editState.published
}, [editState.draft, editState.published])

const [selectedPath, setSelectedPath] = useState<SelectedPath>(null)
const [status, setStatus] = useState<CommentStatus>('open')

const {
dispatch,
data = EMPTY_ARRAY,
error,
loading,
} = useCommentsStore(
useMemo(
() =>
({
documentId: publishedId,
client,
}) satisfies CommentsStoreOptions,
[client, publishedId],
),
)
} = useCommentsStore({
documentId: publishedId,
client,
})

const mentionOptions = useMentionOptions(
useMemo(() => ({documentValue}) satisfies MentionHookOptions, [documentValue]),
Expand All @@ -136,6 +134,93 @@ const CommentsProviderInner = memo(function CommentsProviderInner(
const currentUser = useCurrentUser()
const {name: workspaceName, dataset, projectId} = useWorkspace()

const handleOnCreate = useCallback(
async (payload: CommentPostPayload) => {
// If the comment we try to create already exists in the local state and has
// the 'createError' state, we know that we are retrying a comment creation.
// In that case, we want to change the state to 'createRetrying'.
const hasError = data?.find((c) => c._id === payload._id)?._state?.type === 'createError'

dispatch({
type: 'COMMENT_ADDED',
payload: {
...payload,
_state: hasError ? {type: 'createRetrying'} : undefined,
},
})

// 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 {
await runSetup(payload)
} catch (err) {
// If we fail to create the addon dataset, we update the comment state to `createError`.
// This will make the comment appear in the UI as a comment that failed to be created.
// The user can then retry the comment creation.
dispatch({
type: 'COMMENT_UPDATED',
payload: {
_id: payload._id,
_state: {
error: err,
type: 'createError',
},
},
})
}
}
},
[client, data, dispatch, runSetup],
)

const handleOnUpdate = useCallback(
(id: string, payload: Partial<CommentCreatePayload>) => {
dispatch({
type: 'COMMENT_UPDATED',
payload: {
_id: id,
...payload,
},
})
},
[dispatch],
)

const handleOnEdit = useCallback(
(id: string, payload: CommentEditPayload) => {
dispatch({
type: 'COMMENT_UPDATED',
payload: {
_id: id,
...payload,
},
})
},
[dispatch],
)

const handleOnCreateError = useCallback(
(id: string, err: Error) => {
// When an error occurs during comment creation, we update the comment state
// to `createError`. This will make the comment appear in the UI as a comment
// that failed to be created. The user can then retry the comment creation.
dispatch({
type: 'COMMENT_UPDATED',
payload: {
_id: id,
_state: {
error: err,
type: 'createError',
},
},
})
},
[dispatch],
)

const {operation} = useCommentOperations(
useMemo(
() =>
Expand All @@ -148,125 +233,53 @@ const CommentsProviderInner = memo(function CommentsProviderInner(
projectId,
schemaType,
workspace: workspaceName,

// The following callbacks runs when the comment operations 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
// the real time listener to update the comments and make the UI feel more
// responsive. The comment will be updated again when we receive an mutation
// event from the real time listener.
onCreate: async (payload) => {
// If the comment we try to create already exists in the local state and has
// the 'createError' state, we know that we are retrying a comment creation.
// In that case, we want to change the state to 'createRetrying'.
const hasError =
data?.find((c) => c._id === payload._id)?._state?.type === 'createError'

dispatch({
type: 'COMMENT_ADDED',
payload: {
...payload,
_state: hasError ? {type: 'createRetrying'} : undefined,
},
})

// 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 {
await runSetup(payload)
} catch (err) {
// If we fail to create the addon dataset, we update the comment state to `createError`.
// This will make the comment appear in the UI as a comment that failed to be created.
// The user can then retry the comment creation.
dispatch({
type: 'COMMENT_UPDATED',
payload: {
_id: payload._id,
_state: {
error: err,
type: 'createError',
},
},
})
}
}
},

// When an error occurs during comment creation, we update the comment state
// to `createError`. This will make the comment appear in the UI as a comment
// that failed to be created. The user can then retry the comment creation.
onCreateError: (id, err) => {
dispatch({
type: 'COMMENT_UPDATED',
payload: {
_id: id,
_state: {
error: err,
type: 'createError',
},
},
})
},

onEdit: (id, payload) => {
dispatch({
type: 'COMMENT_UPDATED',
payload: {
_id: id,
...payload,
},
})
},

onUpdate: (id, payload) => {
dispatch({
type: 'COMMENT_UPDATED',
payload: {
_id: id,
...payload,
},
})
},
onCreate: handleOnCreate,
onCreateError: handleOnCreateError,
onEdit: handleOnEdit,
onUpdate: handleOnUpdate,
}) satisfies CommentOperationsHookOptions,
[
client,
runSetup,
currentUser,
data,
dataset,
dispatch,
publishedId,
documentType,
projectId,
publishedId,
schemaType,
workspaceName,
handleOnCreate,
handleOnCreateError,
handleOnEdit,
handleOnUpdate,
],
),
)

const threadItemsByStatus: ThreadItemsByStatus = useMemo(() => {
if (!schemaType || !currentUser) return EMPTY_COMMENTS_DATA

// Since we only make one query to get all comments using the order `_createdAt desc` – we
// can't know for sure that the comments added through the real time listener will be in the
// correct order. In order to avoid that comments are out of order, we make an additional
// sort here. The comments can be out of order if e.g a comment creation fails and is retried
// later.
const sorted = orderBy(data, ['_createdAt'], ['desc'])

const threadItems = buildCommentThreadItems({
comments: sorted || EMPTY_ARRAY,
const items = buildCommentThreadItems({
comments: sorted,
schemaType,
currentUser,
documentValue,
})

return {
open: threadItems.filter((item) => item.parentComment.status === 'open'),
resolved: threadItems.filter((item) => item.parentComment.status === 'resolved'),
open: items.filter((item) => item.parentComment.status === 'open'),
resolved: items.filter((item) => item.parentComment.status === 'resolved'),
}
}, [currentUser, data, documentValue, schemaType])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ export function useCommentOperations(
_dataset: dataset,
_projectId: projectId,
_ref: documentId,
_type: 'object',
_type: 'crossDatasetReference',
_weak: true,
},
documentType,
},
Expand Down
3 changes: 2 additions & 1 deletion packages/sanity/src/desk/comments/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ export interface CommentDocument {
_dataset: string
_projectId: string
_ref: string
_type: 'object'
_type: 'crossDatasetReference'
_weak: boolean
}
}
}
Expand Down

0 comments on commit e48768b

Please sign in to comment.