Skip to content

Commit

Permalink
feat(comments): omit inline comments without referenced value
Browse files Browse the repository at this point in the history
  • Loading branch information
hermanwikner committed Feb 15, 2024
1 parent 26c04ea commit fafcf19
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
applyCommentsFieldAttr,
type CommentCreatePayload,
type CommentsUIMode,
isTextSelectionComment,
useComments,
useCommentsEnabled,
useCommentsScroll,
Expand Down Expand Up @@ -109,7 +110,7 @@ function CommentFieldInner(
const isInlineCommentThread = useMemo(() => {
return comments.data.open
.filter((c) => c.threadId === selectedPath?.threadId)
.some((x) => x.selection?.type === 'text')
.some((x) => isTextSelectionComment(x.parentComment))
}, [comments.data.open, selectedPath?.threadId])

// Total number of comments for the current field
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
type CommentUpdatePayload,
currentSelectionIsOverlappingWithComment,
hasCommentMessageValue,
isTextSelectionComment,
useComments,
useCommentsEnabled,
useCommentsScroll,
Expand Down Expand Up @@ -114,7 +115,7 @@ export const CommentsPortableTextInputInner = React.memo(function CommentsPortab
const textComments = useMemo(() => {
return comments.data.open
.filter((comment) => comment.fieldPath === stringFieldPath)
.filter((c) => c.selection?.type === 'text')
.filter((c) => isTextSelectionComment(c.parentComment))
}, [comments.data.open, stringFieldPath])

const getFragment = useCallback(() => {
Expand Down Expand Up @@ -217,8 +218,10 @@ export const CommentsPortableTextInputInner = React.memo(function CommentsPortab
// to finish.
const editorStateValue = PortableTextEditor.getValue(editorRef.current)

const parentComments = textComments.map((c) => c.parentComment)

const decorators = buildRangeDecorations({
comments: textComments,
comments: parentComments,
currentHoveredCommentId,
onDecoratorClick: handleDecoratorClick,
onDecoratorHoverEnd: setCurrentHoveredCommentId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
type CommentsUIMode,
CommentsUpsellPanel,
type CommentUpdatePayload,
isTextSelectionComment,
useComments,
useCommentsEnabled,
useCommentsOnboarding,
Expand Down Expand Up @@ -195,7 +196,7 @@ function CommentsInspectorInner(

const isInlineComment = comments.data.open
.filter((c) => c.threadId === nextPath?.threadId)
.some((x) => x.selection?.type === 'text')
.some((x) => isTextSelectionComment(x.parentComment))

if (isInlineComment && nextPath.threadId) {
scrollToInlineComment(nextPath.threadId)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {describe, expect, it} from '@jest/globals'

import {type CommentThreadItem} from '../types'
import {type CommentDocument} from '../types'
import {buildRangeDecorationSelectionsFromComments} from '../utils'

describe('comments: buildRangeDecorationSelectionsFromComments', () => {
Expand Down Expand Up @@ -132,45 +132,38 @@ const boldedInsideAndOutsideValue = [
},
]

const initialComments: CommentThreadItem[] = [
const initialComments: CommentDocument[] = [
{
selection: {
type: 'text',
value: [
{
_key: '6222e4072b6e',
text: 'Hello <comment>there</comment> world',
},
],
},
breadcrumbs: [],
commentsCount: 0,
fieldPath: '',
replies: [],
parentComment: {
_id: 'a14fb200-a4c4-4c44-b2c1-c17aa6d79aa8',
_type: 'comment',
_createdAt: '',
_rev: '',
authorId: '',
message: null,
threadId: '',
status: 'open',
reactions: null,
target: {
path: {
field: '',
},
documentType: '',
document: {
_dataset: '',
_projectId: '',
_ref: '',
_type: 'crossDatasetReference',
_weak: false,
_id: 'a14fb200-a4c4-4c44-b2c1-c17aa6d79aa8',
_type: 'comment',
_createdAt: '',
_rev: '',
authorId: '',
message: null,
threadId: '',
status: 'open',
reactions: null,
target: {
path: {
field: '',
selection: {
type: 'text',
value: [
{
_key: '6222e4072b6e',
text: 'Hello <comment>there</comment> world',
},
],
},
},
documentType: '',
document: {
_dataset: '',
_projectId: '',
_ref: '',
_type: 'crossDatasetReference',
_weak: false,
},
},
threadId: 'de1f947c-93a7-4f71-9b9c-5f20023f9c98',
},
]
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export default function CommentInlineHighlightDebugStory() {
const buildRangeDecorationsCallback = useCallback(
() =>
buildRangeDecorations({
comments,
comments: comments.map((c) => c.parentComment),
value,
onDecoratorHoverStart: setCurrentHoveredCommentId,
onDecoratorHoverEnd: setCurrentHoveredCommentId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {IntentLink} from 'sanity/router'
import styled, {css} from 'styled-components'

import {commentsLocaleNamespace} from '../../../i18n'
import {hasCommentMessageValue, useCommentHasChanged} from '../../helpers'
import {hasCommentMessageValue, isTextSelectionComment, useCommentHasChanged} from '../../helpers'
import {
type CommentContext,
type CommentDocument,
Expand Down Expand Up @@ -396,7 +396,7 @@ export function CommentsListItemLayout(props: CommentsListItemLayoutProps) {
)}
</Flex>

{comment.target.path.selection?.type === 'text' && Boolean(comment?.contentSnapshot) && (
{isTextSelectionComment(comment) && Boolean(comment?.contentSnapshot) && (
<Flex gap={FLEX_GAP} marginBottom={3}>
<SpacerAvatar />

Expand Down
11 changes: 11 additions & 0 deletions packages/sanity/src/structure/comments/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,14 @@ export function commentIntentIfDiffers(
}
return undefined
}

/**
* A function that checks whether a comment is a text selection comment
*/
export function isTextSelectionComment(comment: CommentDocument): boolean {
if (!comment) return false

return Boolean(
comment?.target?.path?.selection?.type === 'text' && comment?.target?.path?.selection?.value,
)
}
1 change: 0 additions & 1 deletion packages/sanity/src/structure/comments/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ export interface CommentThreadItem {
fieldPath: string
parentComment: CommentDocument
replies: CommentDocument[]
selection: CommentPathSelection | undefined
threadId: string
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import {type SanityDocument} from '@sanity/client'
import {type CurrentUser, type SchemaType} from '@sanity/types'
import {type CurrentUser, type PortableTextBlock, type SchemaType} from '@sanity/types'
import * as PathUtils from '@sanity/util/paths'
import {getValueAtPath} from 'sanity'

import {isTextSelectionComment} from '../helpers'
import {type CommentDocument, type CommentThreadItem} from '../types'
import {buildCommentBreadcrumbs} from './buildCommentBreadcrumbs'
import {buildRangeDecorationSelectionsFromComment} from './inline-comments'

const EMPTY_ARRAY: [] = []

interface BuildCommentThreadItemsProps {
comments: CommentDocument[]
Expand All @@ -21,34 +27,47 @@ export function buildCommentThreadItems(props: BuildCommentThreadItemsProps): Co
const {comments, currentUser, documentValue, schemaType} = props
const parentComments = comments?.filter((c) => !c.parentCommentId)

const items = parentComments
.map((parentComment) => {
const crumbs = buildCommentBreadcrumbs({
currentUser,
documentValue,
fieldPath: parentComment.target.path.field,
schemaType,
const items = parentComments.map((parentComment) => {
const crumbs = buildCommentBreadcrumbs({
currentUser,
documentValue,
fieldPath: parentComment.target.path.field,
schemaType,
})

const pteValue = getValueAtPath(
documentValue,
PathUtils.fromString(parentComment.target.path.field),
)

let hasInvalidSelection = false

if (isTextSelectionComment(parentComment)) {
const selection = buildRangeDecorationSelectionsFromComment({
comment: parentComment,
value: (pteValue || EMPTY_ARRAY) as PortableTextBlock[],
})

const hasInvalidBreadcrumb = crumbs.some((bc) => bc.invalid)
hasInvalidSelection = selection.length === 0
}

if (hasInvalidBreadcrumb) return undefined
const hasInvalidBreadcrumb = crumbs.some((bc) => bc.invalid)

const replies = comments?.filter((r) => r.parentCommentId === parentComment._id)
if (hasInvalidBreadcrumb || hasInvalidSelection) return undefined

const commentsCount = [parentComment, ...replies].length
const replies = comments?.filter((r) => r.parentCommentId === parentComment._id)

return {
breadcrumbs: crumbs,
commentsCount,
fieldPath: parentComment.target.path.field,
parentComment,
replies,
selection: parentComment.target.path.selection,
threadId: parentComment.threadId,
}
})
.filter(Boolean) as CommentThreadItem[]
const commentsCount = [parentComment, ...replies].length

return {
breadcrumbs: crumbs,
commentsCount,
fieldPath: parentComment.target.path.field,
parentComment,
replies,
threadId: parentComment.threadId,
}
})

return items
return items.filter(Boolean) as CommentThreadItem[]
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import {type RangeDecoration} from '@sanity/portable-text-editor'
import {
isPortableTextSpan,
isPortableTextTextBlock,
type PortableTextBlock,
type PortableTextTextBlock,
} from '@sanity/types'

import {type CommentMessage, type CommentThreadItem} from '../../types'
import {isTextSelectionComment} from '../../helpers'
import {type CommentDocument} from '../../types'

function diffText(current: string, next: string) {
const diff = makeDiff(current, next)
Expand Down Expand Up @@ -43,13 +45,13 @@ function toPlainTextWithChildSeparators(inputBlock: PortableTextTextBlock) {
const EMPTY_ARRAY: [] = []

export interface BuildCommentsRangeDecorationsProps {
value: CommentMessage | undefined
comments: CommentThreadItem[]
value: PortableTextBlock[] | undefined
comments: CommentDocument[]
}

export interface BuildCommentsRangeDecorationsResultItem {
selection: RangeDecoration['selection']
comment: CommentThreadItem
comment: CommentDocument
range: {_key: string; text: string}
}

Expand All @@ -63,13 +65,11 @@ export function buildRangeDecorationSelectionsFromComments(

if (!value || value.length === 0) return EMPTY_ARRAY

const textSelections = comments.filter(
(comment) => comment.selection?.type === 'text' && comment.selection.value,
)
const textSelections = comments.filter(isTextSelectionComment)
const decorators: BuildCommentsRangeDecorationsResultItem[] = []

textSelections.forEach((comment) => {
comment.selection?.value.forEach((selectionMember) => {
comment.target.path.selection?.value.forEach((selectionMember) => {
const matchedBlock = value.find((block) => block._key === selectionMember._key)
if (!matchedBlock || !isPortableTextTextBlock(matchedBlock)) {
return
Expand Down Expand Up @@ -135,3 +135,18 @@ export function buildRangeDecorationSelectionsFromComments(
if (decorators.length === 0) return EMPTY_ARRAY
return decorators
}

export interface BuildCommentRangeDecorationsProps {
value: PortableTextBlock[] | undefined
comment: CommentDocument
}

/**
* A function that builds range decoration selections from one single comment and its associated text
*/
export function buildRangeDecorationSelectionsFromComment(
props: BuildCommentRangeDecorationsProps,
): BuildCommentsRangeDecorationsResultItem[] {
const {value, comment} = props
return buildRangeDecorationSelectionsFromComments({comments: [comment], value})
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {type RangeDecoration} from '@sanity/portable-text-editor'
import {memo, useCallback, useEffect, useRef} from 'react'
import {type PortableTextBlock} from 'sanity'

import {CommentInlineHighlightSpan} from '../../components'
import {applyInlineCommentIdAttr} from '../../hooks'
import {type CommentMessage, type CommentThreadItem} from '../../types'
import {type CommentDocument} from '../../types'
import {buildRangeDecorationSelectionsFromComments} from './buildRangeDecorationSelectionsFromComments'

interface CommentRangeDecorationProps {
Expand Down Expand Up @@ -91,13 +92,13 @@ function isRangeInvalid() {
}

interface BuildRangeDecorationsProps {
comments: CommentThreadItem[]
comments: CommentDocument[]
currentHoveredCommentId: string | null
onDecoratorClick: (commentId: string) => void
onDecoratorHoverEnd: (commentId: null) => void
onDecoratorHoverStart: (commentId: string) => void
selectedThreadId: string | null
value: CommentMessage | undefined
value: PortableTextBlock[] | undefined
}

export function buildRangeDecorations(props: BuildRangeDecorationsProps) {
Expand All @@ -116,7 +117,7 @@ export function buildRangeDecorations(props: BuildRangeDecorationsProps) {
const decoration: RangeDecoration = {
component: ({children}) => (
<CommentRangeDecoration
commentId={comment.parentComment._id}
commentId={comment._id}
currentHoveredCommentId={currentHoveredCommentId}
onClick={onDecoratorClick}
onHoverEnd={onDecoratorHoverEnd}
Expand All @@ -130,7 +131,7 @@ export function buildRangeDecorations(props: BuildRangeDecorationsProps) {
isRangeInvalid,
selection,
payload: {
commentId: comment.parentComment._id,
commentId: comment._id,
range,
},
}
Expand Down

0 comments on commit fafcf19

Please sign in to comment.