Skip to content

Commit

Permalink
feat: comments wip
Browse files Browse the repository at this point in the history
  • Loading branch information
hermanwikner committed Sep 4, 2023
1 parent 66afcd7 commit 12109fd
Show file tree
Hide file tree
Showing 71 changed files with 3,111 additions and 77 deletions.
22 changes: 0 additions & 22 deletions dev/test-studio/fieldActions/commentFieldAction.tsx

This file was deleted.

3 changes: 1 addition & 2 deletions dev/test-studio/sanity.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ import {vercelTheme} from './themes/vercel'
import {GoogleLogo, TailwindLogo, VercelLogo} from './components/workspaceLogos'
import {copyAction} from './fieldActions/copyAction'
import {assistFieldActionGroup} from './fieldActions/assistFieldActionGroup'
import {commentAction} from './fieldActions/commentFieldAction'
import {customInspector} from './inspectors/custom'
import {pasteAction} from './fieldActions/pasteAction'

Expand Down Expand Up @@ -70,7 +69,7 @@ const sharedSettings = definePlugin({
},
unstable_fieldActions: (prev, ctx) => {
if (['fieldActionsTest', 'stringsTest'].includes(ctx.documentType)) {
return [...prev, commentAction, assistFieldActionGroup, copyAction, pasteAction]
return [...prev, assistFieldActionGroup, copyAction, pasteAction]
}

return prev
Expand Down
1 change: 1 addition & 0 deletions packages/sanity/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@
"@sanity/logos": "^2.0.2",
"@sanity/mutator": "3.16.2",
"@sanity/portable-text-editor": "3.16.2",
"@portabletext/react": "^3.0.0",
"@sanity/schema": "3.16.2",
"@sanity/types": "3.16.2",
"@sanity/ui": "^1.7.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, {useState} from 'react'
import {Card, Container, Flex} from '@sanity/ui'
import {PortableTextBlock} from '@sanity/types'
import {useBoolean} from '@sanity/ui-workshop'
import {CommentInput} from '../components'
import {CommentMessageSerializer} from '../components/pte'
import {useCurrentUser} from '../../store'

export default function CommentsInputStory() {
const [value, setValue] = useState<PortableTextBlock[] | null>(null)
const currentUser = useCurrentUser()
const expandOnFocus = useBoolean('Expand on focus', false, 'Props')
const expanded = useBoolean('Expanded', false, 'Props')

if (!currentUser) return null

return (
<Flex height="fill">
<Card flex={1} padding={4} paddingY={8}>
<Container width={0}>
<CommentInput
currentUser={currentUser}
documentId="xyz"
documentType="xyz"
onChange={setValue}
value={value}
expandOnFocus={expandOnFocus}
isEditing={expanded}
/>
</Container>
</Card>

<Card borderLeft flex={1}>
<Card flex={1} padding={4} paddingY={8}>
<Container width={0}>
<CommentMessageSerializer blocks={value || []} />
</Container>
</Card>
</Card>
</Flex>
)
}
117 changes: 117 additions & 0 deletions packages/sanity/src/core/comments/__workshop__/CommentsListStory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import React, {useCallback, useState} from 'react'
import {Container, Flex} from '@sanity/ui'
import {CommentsList} from '../components'
import {useCurrentUser} from '../../store'
import {CommentDocument, CommentCreatePayload, CommentEditPayload} from '../types'

const BASE: CommentDocument = {
_id: '1',
_type: 'comment',
_createdAt: new Date().toISOString(),
_updatedAt: '2021-05-04T14:54:37Z',
authorId: 'p8U8TipFc',
status: 'open',
_rev: '1',

workspace: 'test',
target: {
path: {
field: JSON.stringify([]),
},
document: {
_dataset: '1',
_projectId: '1',
_ref: '1',
_type: 'crossDatasetReference',
_weak: true,
},
},
message: [
{
_type: 'block',
_key: '36a3f0d3832d',
style: 'normal',
markDefs: [],
children: [
{
_type: 'span',
_key: '89014dd684ce',
text: 'My first comment',
marks: [],
},
],
},
],
}

const PROPS = [
{
...BASE,
},
]

export default function CommentsListStory() {
const [state, setState] = useState<CommentDocument[]>(PROPS)

const currentUser = useCurrentUser()

const handleReplySubmit = useCallback(
(payload: CommentCreatePayload) => {
const comment: CommentDocument = {
...BASE,
...payload,
_createdAt: new Date().toISOString(),
_id: `${state.length + 1}`,
authorId: currentUser?.id || 'pP5s3g90N',
parentCommentId: payload.parentCommentId,
}

setState((prev) => [...prev, comment])
},
[currentUser?.id, state.length],
)

const handleEdit = useCallback(
(id: string, payload: CommentEditPayload) => {
setState((prev) =>
prev.map((item) => {
if (item._id === id) {
return {
...item,
...payload,
_updatedAt: new Date().toISOString(),
}
}

return item
}),
)
},
[setState],
)

const handleDelete = useCallback(
(id: string) => {
setState((prev) => prev.filter((item) => item._id !== id))
},
[setState],
)

if (!currentUser) return null

return (
<Flex height="fill" padding={4} sizing="border" overflow="hidden">
<Container width={1} paddingX={5} sizing="border">
<CommentsList
comments={state}
onReply={handleReplySubmit}
onDelete={handleDelete}
currentUser={currentUser}
documentId="1"
documentType="article"
onEdit={handleEdit}
/>
</Container>
</Flex>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react'
import {Card, Code} from '@sanity/ui'
import {useMentionOptions} from '../hooks'

export default function MentionOptionsHookStory() {
const {data, loading} = useMentionOptions({
documentId: '1e1744ab-43d5-4fff-8a2a-28c58bf0434a',
documentType: 'author',
})

if (loading) return <div>Loading...</div>
if (!data) return <div>No data</div>

return (
<Card height="fill" padding={4} overflow="auto">
<Code language="javascript" size={1}>
{JSON.stringify(data, null, 2)}
</Code>
</Card>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {Container, Flex} from '@sanity/ui'
import React from 'react'
import {MentionsMenu} from '../components/mentions'
import {useMentionOptions} from '../hooks'

export default function MentionsMenuStory() {
const {data, loading} = useMentionOptions({documentId: 'foo', documentType: 'bar'})

return (
<Flex height="fill" align="center">
<Container width={0}>
<MentionsMenu
options={data}
loading={loading}
onSelect={() => {
//...
}}
/>
</Container>
</Flex>
)
}
29 changes: 29 additions & 0 deletions packages/sanity/src/core/comments/__workshop__/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {defineScope} from '@sanity/ui-workshop'
import {lazy} from 'react'

export default defineScope({
name: 'core/comments',
title: 'comments',
stories: [
{
name: 'comments-input',
title: 'CommentsInput',
component: lazy(() => import('./CommentInputStory')),
},
{
name: 'mention-options-hook',
title: 'useMentionOptions',
component: lazy(() => import('./MentionOptionsHookStory')),
},
{
name: 'comments-list',
title: 'CommentsList',
component: lazy(() => import('./CommentsListStory')),
},
{
name: 'mentions-menu',
title: 'MentionsMenu',
component: lazy(() => import('./MentionsMenuStory')),
},
],
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {Dialog, Grid, Button, Stack, Text} from '@sanity/ui'
import React, {useCallback} from 'react'

const DIALOG_COPY: Record<
'thread' | 'comment',
{title: string; body: string; confirmButtonText: string}
> = {
thread: {
title: 'Delete this comment thread?',
body: 'All comments in this thread will be deleted, and once deleted cannot be recovered.',
confirmButtonText: 'Delete thread',
},
comment: {
title: 'Delete this comment?',
body: 'Once deleted, a comment cannot be recovered.',
confirmButtonText: 'Delete comment',
},
}

/**
* @beta
* @hidden
*/
export interface CommentDeleteDialogProps {
commentId: string
error: Error | null
isParent: boolean
loading: boolean
onClose: () => void
onDelete: (id: string) => void
}

/**
* @beta
* @hidden
*/
export function CommentDeleteDialog(props: CommentDeleteDialogProps) {
const {isParent, onClose, commentId, onDelete, loading, error} = props
const {title, body, confirmButtonText} = DIALOG_COPY[isParent ? 'thread' : 'comment']

const handleDelete = useCallback(() => {
onDelete(commentId)
}, [commentId, onDelete])

return (
<Dialog
header={title}
id="delete-comment-dialog"
onClose={onClose}
width={0}
footer={
<Grid columns={2} padding={2} gap={2}>
<Button text="Cancel" mode="ghost" onClick={onClose} />
<Button
loading={loading}
onClick={handleDelete}
text={confirmButtonText}
tone="critical"
/>
</Grid>
}
>
{error && <span>Something went wrong</span>}

<Stack padding={4}>
<Text>{body}</Text>
</Stack>
</Dialog>
)
}
4 changes: 4 additions & 0 deletions packages/sanity/src/core/comments/components/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import {FlexProps} from '@sanity/ui'

export const FLEX_GAP: FlexProps['gap'] = 3
export const AVATAR_SIZE = 25
27 changes: 27 additions & 0 deletions packages/sanity/src/core/comments/components/icons/MentionIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React, {forwardRef} from 'react'

export const MentionIcon = forwardRef(function Icon(
props: React.SVGProps<SVGSVGElement>,
ref: React.Ref<SVGSVGElement>,
) {
return (
<svg
data-sanity-icon="mention"
fill="none"
height="1em"
ref={ref}
viewBox="0 0 25 25"
width="1em"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M16.6633 18.9383C15.539 19.6562 14.2034 20.0723 12.7705 20.0723C8.77022 20.0723 5.52734 16.8294 5.52734 12.8291C5.52734 8.82881 8.77022 5.58594 12.7705 5.58594C16.7708 5.58594 20.0137 8.82881 20.0137 12.8291C20.0137 13.6623 19.8249 14.7093 19.6141 15.2077C19.5578 15.3408 19.479 15.4845 19.3936 15.6238C19.0955 16.1106 18.5507 16.3721 17.9807 16.4018V16.4018C16.8271 16.462 15.8588 15.5428 15.8588 14.3877V9.27302"
stroke="currentColor"
strokeWidth={1.2}
strokeLinejoin="round"
/>
<circle cx="12.5732" cy="12.8291" r="3.08691" stroke="currentColor" strokeWidth={1.2} />
</svg>
)
})
Loading

0 comments on commit 12109fd

Please sign in to comment.