Skip to content

Commit

Permalink
refactor: scroll to thread, feature enable check, build breadcrumbs
Browse files Browse the repository at this point in the history
  • Loading branch information
hermanwikner committed Sep 25, 2023
1 parent ac6be5f commit 74b0c4e
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ interface CommentThreadLayoutProps {
canCreateNewThread: boolean
children: React.ReactNode
currentUser: CurrentUser
threadId: string
mentionOptions: MentionOptionsHookValue
onNewThreadCreate: (payload: CommentCreatePayload) => void
path: Path
Expand All @@ -33,7 +32,6 @@ export function CommentThreadLayout(props: CommentThreadLayoutProps) {
canCreateNewThread,
children,
currentUser,
threadId,
mentionOptions,
onNewThreadCreate,
path,
Expand Down Expand Up @@ -77,7 +75,7 @@ export function CommentThreadLayout(props: CommentThreadLayoutProps) {
)

return (
<Stack space={2} data-thread-id={threadId}>
<Stack space={2}>
<BreadcrumbsFlex align="center" gap={2} paddingX={1} sizing="border">
<Stack flex={1}>
<Breadcrumbs maxLength={3}>
Expand Down
107 changes: 61 additions & 46 deletions packages/sanity/src/core/comments/components/list/CommentsList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable max-nested-callbacks */
import React, {forwardRef, useCallback, useImperativeHandle, useMemo, useState} from 'react'
import {BoundaryElementProvider, Container, Flex, Spinner, Stack, Text} from '@sanity/ui'
import {CurrentUser, Path} from '@sanity/types'
Expand All @@ -15,6 +14,12 @@ import {CommentsListItem} from './CommentsListItem'
import {CommentThreadLayout} from './CommentThreadLayout'
import {EMPTY_STATE_MESSAGES} from './constants'

const SCROLL_INTO_VIEW_OPTIONS: ScrollIntoViewOptions = {
behavior: 'smooth',
block: 'center',
inline: 'center',
}

interface GroupedComments {
[field: string]: CommentDocument[]
}
Expand All @@ -33,6 +38,17 @@ function groupComments(comments: CommentDocument[]) {
}, {} as GroupedComments)
}

function getReplies(parentCommentId: string, group: CommentDocument[]) {
const replies = group.filter((c) => c.parentCommentId === parentCommentId)

// The default sort order is by date, descending (newest first).
// However, inside a thread, we want the order to be ascending (oldest first).
// So we reverse the array here.
const orderedReplies = [...replies].reverse()

return orderedReplies
}

/**
* @beta
* @hidden
Expand Down Expand Up @@ -89,12 +105,7 @@ export const CommentsList = forwardRef<CommentsListHandle, CommentsListProps>(fu
const scrollToComment = useCallback(
(id: string) => {
const commentElement = boundaryElement?.querySelector(`[data-comment-id="${id}"]`)

commentElement?.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'center',
})
commentElement?.scrollIntoView(SCROLL_INTO_VIEW_OPTIONS)
},
[boundaryElement],
)
Expand All @@ -114,9 +125,8 @@ export const CommentsList = forwardRef<CommentsListHandle, CommentsListProps>(fu
// 1. Get all comments that are not replies and are in the current view (open or resolved)
.filter((c) => !c.parentCommentId)
// 2. Get all replies to each parent comment and add them to the array
.map((c) => {
return [c, ...comments.filter((c2) => c2.parentCommentId === c._id)]
})
// eslint-disable-next-line max-nested-callbacks
.map((c) => [c, ...comments.filter((c2) => c2.parentCommentId === c._id)])
.flat()

return Object.entries(groupComments(filteredComments))
Expand Down Expand Up @@ -186,45 +196,50 @@ export const CommentsList = forwardRef<CommentsListHandle, CommentsListProps>(fu
{groupedComments.map(([fieldPath, group]) => {
const parentComments = group.filter((c) => !c.parentCommentId)

const breadcrumbs = buildCommentBreadcrumbs?.(fieldPath)
// The threadId is used to identify the thread in the DOM, so we can scroll to it.
// todo: validate this approach
const threadId = group[0].threadId

const breadcrumbs = buildCommentBreadcrumbs?.(fieldPath)
const hasInvalidBreadcrumb = breadcrumbs?.some((b) => b.invalid === true)

// If the breadcrumb is invalid, the field might have been remove from the
// the schema, or an array item might have been removed. In that case, we don't
// want to render any button to open the field.
const _onPathFocus = hasInvalidBreadcrumb ? undefined : onPathFocus

return (
<CommentThreadLayout
breadcrumbs={breadcrumbs}
canCreateNewThread={status === 'open'}
currentUser={currentUser}
key={fieldPath}
mentionOptions={mentionOptions}
onNewThreadCreate={onNewThreadCreate}
path={PathUtils.fromString(fieldPath)}
threadId={threadId}
>
{parentComments.map((comment) => {
const replies = group.filter((c) => c.parentCommentId === comment._id)

// The default sort order is by date, descending (newest first).
// However, inside a thread, we want the order to be ascending (oldest first).
// So we reverse the array here.
const orderedReplies = [...replies].reverse()

return (
<CommentsListItem
canReply={status === 'open'}
currentUser={currentUser}
key={comment?._id}
mentionOptions={mentionOptions}
onDelete={onDelete}
onEdit={onEdit}
onPathFocus={onPathFocus}
onReply={onReply}
onStatusChange={onStatusChange}
parentComment={comment}
replies={orderedReplies}
/>
)
})}
</CommentThreadLayout>
<Stack data-thread-id={threadId} key={fieldPath}>
<CommentThreadLayout
breadcrumbs={breadcrumbs}
canCreateNewThread={status === 'open'}
currentUser={currentUser}
key={fieldPath}
mentionOptions={mentionOptions}
onNewThreadCreate={onNewThreadCreate}
path={PathUtils.fromString(fieldPath)}
>
{parentComments.map((comment) => {
const replies = getReplies(comment._id, group)

return (
<CommentsListItem
canReply={status === 'open'}
currentUser={currentUser}
key={comment?._id}
mentionOptions={mentionOptions}
onDelete={onDelete}
onEdit={onEdit}
onPathFocus={_onPathFocus}
onReply={onReply}
onStatusChange={onStatusChange}
parentComment={comment}
replies={replies}
/>
)
})}
</CommentThreadLayout>
</Stack>
)
})}
</BoundaryElementProvider>
Expand Down
24 changes: 18 additions & 6 deletions packages/sanity/src/core/comments/hooks/useCommentsEnabled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,22 @@ import {useFeatureEnabled} from '../../hooks'
import {useSource} from '../../studio'
import {getPublishedId} from '../../util'

interface Enabled {
interface Disabled {
isEnabled: false
reason: 'disabled-by-config' | 'plan-upgrade-required'
reason: 'disabled-by-config' | 'plan-upgrade-required' | 'loading'
}

interface Disabled {
interface Enabled {
isEnabled: true
reason: null
}

type CommentsEnabled = Enabled | Disabled
interface Loading {
isEnabled: false
reason: 'loading'
}

type CommentsEnabled = Enabled | Disabled | Loading

interface CommentsEnabledHookOptions {
documentId: string
Expand All @@ -34,14 +39,21 @@ export function useCommentsEnabled(opts: CommentsEnabledHookOptions): CommentsEn
const enabledFromConfig = enabled({documentType, documentId: getPublishedId(documentId)})

const commentsEnabled = useMemo(() => {
if (!featureEnabled && !isLoading) {
if (isLoading) {
return {
isEnabled: false,
reason: 'loading',
} satisfies CommentsEnabled
}

if (!featureEnabled) {
return {
isEnabled: false,
reason: 'plan-upgrade-required',
} satisfies CommentsEnabled
}

if (!enabledFromConfig || isLoading) {
if (!enabledFromConfig) {
return {
isEnabled: false,
reason: 'disabled-by-config',
Expand Down
32 changes: 19 additions & 13 deletions packages/sanity/src/core/comments/utils/buildCommentBreadcrumbs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ function getSchemaField(
return field
}
}

return undefined
}

Expand All @@ -36,7 +37,7 @@ function findArrayItemIndex(array: unknown[], pathSegment: any): number | false
return index === -1 ? false : index
}

interface BuildCommentBreadcrumbs {
interface BuildCommentBreadcrumbsProps {
documentValue: unknown
fieldPath: string
schemaType: SchemaType
Expand All @@ -46,7 +47,7 @@ interface BuildCommentBreadcrumbs {
* @beta
* @hidden
*/
export function buildCommentBreadcrumbs(props: BuildCommentBreadcrumbs): CommentBreadcrumbs {
export function buildCommentBreadcrumbs(props: BuildCommentBreadcrumbsProps): CommentBreadcrumbs {
const {schemaType, fieldPath, documentValue} = props
const paths = PathUtils.fromString(fieldPath)
const fieldPaths: CommentBreadcrumbs = []
Expand All @@ -56,24 +57,16 @@ export function buildCommentBreadcrumbs(props: BuildCommentBreadcrumbs): Comment
const isArraySegment = seg.hasOwnProperty('_key')
const field = getSchemaField(schemaType, currentPath)

if (!field && !isArraySegment) {
fieldPaths.push({
invalid: true,
isArrayItem: false,
title: startCase(seg.toString()),
})

return
}

if (field && !isArraySegment) {
if (field) {
const title = getSchemaTypeTitle(field?.type)

fieldPaths.push({
invalid: false,
isArrayItem: false,
title,
})

return
}

if (isArraySegment) {
Expand All @@ -86,6 +79,19 @@ export function buildCommentBreadcrumbs(props: BuildCommentBreadcrumbs): Comment
isArrayItem: true,
title: `#${Number(arrayItemIndex) + 1}`,
})

return
}

// This is usually the schema definition of an array item (ie inside of: []).
// todo: fix so that we get the defined titled instead of the field name.
// We need to add support for it in `getSchemaField`.
if (!isArraySegment) {
fieldPaths.push({
invalid: false,
isArrayItem: false,
title: startCase(seg.toString()),
})
}
})

Expand Down
6 changes: 4 additions & 2 deletions packages/sanity/src/desk/comments/field/CommentField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
useFieldCommentsCount,
} from 'sanity'

const SCROLL_INTO_VIEW_OPTIONS: ScrollIntoViewOptions = {behavior: 'smooth', block: 'start'}

export function CommentField(props: FieldProps) {
const {documentId, documentType} = useDocumentPane()

Expand Down Expand Up @@ -52,7 +54,7 @@ function CommentFieldInner(props: FieldProps) {
useEffect(() => {
if (status === 'open' && inspector?.name === COMMENTS_INSPECTOR_NAME && shouldScrollToThread) {
const threadId = comments.data.open.find(
(comment) => comment.target.path.field === props.path.toString(),
(comment) => comment.target?.path?.field === props.path.toString(),
)?.threadId

// Find the node in the DOM
Expand All @@ -61,7 +63,7 @@ function CommentFieldInner(props: FieldProps) {
// Scroll to the node
if (node) {
requestAnimationFrame(() => {
node.scrollIntoView({behavior: 'smooth', block: 'start'})
node.scrollIntoView(SCROLL_INTO_VIEW_OPTIONS)
})
}

Expand Down

0 comments on commit 74b0c4e

Please sign in to comment.