Skip to content

Commit dd7a706

Browse files
juanlou1217genedna
andauthored
feat(code review): Implement code review comment functionality (#1901)
* feat(code review): Implement code review comment functionality * feat(code review): Implement code review comment functionality * feat(code review): Implement code review comment functionality --------- Co-authored-by: Quanyi Ma <eli@patch.sh>
1 parent 26c410e commit dd7a706

21 files changed

Lines changed: 1795 additions & 175 deletions

moon/apps/web/components/CodeView/CommitsView/detail/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { useAtom } from 'jotai'
66
import { useRouter } from 'next/router'
77
import toast from 'react-hot-toast'
88

9-
import { CommitSummary, CommonPageDiffItem, CommonResultVecMuiTreeNode, DiffItem } from '@gitmono/types'
9+
import { CommitSummary, CommonPageDiffItemSchema, CommonResultVecMuiTreeNode, DiffItemSchema } from '@gitmono/types'
1010
import { LoadingSpinner } from '@gitmono/ui'
1111

1212
import { formatAssignees } from '@/components/CodeView/CommitsView'
@@ -19,7 +19,7 @@ import { usePostCommitsFilesChanged } from '@/hooks/commits/usePostCommitsFilesC
1919

2020
interface CommitDetailData {
2121
commit: CommitSummary
22-
diffs: DiffItem[]
22+
diffs: DiffItemSchema[]
2323
}
2424

2525
export const CommitsDetailView: React.FC = () => {
@@ -50,11 +50,11 @@ export const CommitsDetailView: React.FC = () => {
5050
if (!filesChangedRes?.data) return undefined
5151
return {
5252
commit: filesChangedRes.data.commit,
53-
diffs: filesChangedRes.data.page.items as DiffItem[]
53+
diffs: filesChangedRes.data.page.items as DiffItemSchema[]
5454
}
5555
}, [filesChangedRes])
5656

57-
const fileChangeData: CommonPageDiffItem | undefined = useMemo(() => {
57+
const fileChangeData: CommonPageDiffItemSchema | undefined = useMemo(() => {
5858
if (!commitsDetail?.diffs) return undefined
5959

6060
return {

moon/apps/web/components/DiffView/FileDiff.tsx

Lines changed: 128 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,22 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
22
import { type ChangeTypes, type FileDiffMetadata } from '@pierre/diffs'
33
import { FileDiff as PierreFileDiff } from '@pierre/diffs/react'
44
import { useTheme } from 'next-themes'
5+
import { useRouter } from 'next/router'
56
import { Virtuoso } from 'react-virtuoso'
67

7-
import { CommonPageDiffItem, CommonResultVecMuiTreeNode } from '@gitmono/types'
8+
import { CommonPageDiffItemSchema, CommonResultCodeReviewResponse, CommonResultVecMuiTreeNode } from '@gitmono/types'
89
import { LoadingSpinner } from '@gitmono/ui'
910
import { ExpandIcon, SparklesIcon } from '@gitmono/ui/Icons'
1011
import { cn } from '@gitmono/ui/src/utils'
1112

1213
import FileTree from '@/components/DiffView/TreeView/FileTree'
1314

15+
import { CommentForm, CommentThread, HoverButton, useComments } from './comment'
16+
import { useGetComments } from './hooks/useGetComments'
1417
import { DiffItem, generateParsedFiles, parsedDiffs } from './parsedDiffs'
1518

1619
interface FileDiffProps {
17-
fileChangeData: CommonPageDiffItem
20+
fileChangeData: CommonPageDiffItemSchema
1821
fileChangeIsLoading: boolean
1922
treeData: CommonResultVecMuiTreeNode['data']
2023
treeIsLoading: boolean
@@ -24,6 +27,120 @@ interface FileDiffProps {
2427
onLoadMore: () => void
2528
}
2629

30+
function SingleFileDiffView({
31+
filePath,
32+
fileDiffMetadata,
33+
changeType,
34+
isBinary,
35+
hasContent,
36+
clLink,
37+
commentsData
38+
}: {
39+
filePath: string
40+
fileDiffMetadata: FileDiffMetadata | null
41+
changeType: ChangeTypes | null
42+
isBinary: boolean
43+
hasContent: boolean
44+
clLink: string
45+
commentsData?: CommonResultCodeReviewResponse
46+
}) {
47+
const { resolvedTheme } = useTheme()
48+
49+
const {
50+
annotations,
51+
selectedRange,
52+
handleLineSelectionEnd,
53+
addCommentAtLine,
54+
handleSubmitComment,
55+
handleCancelComment
56+
} = useComments(filePath, commentsData?.data)
57+
58+
const getChangeTypeMessage = (changeType: ChangeTypes | null): string | null => {
59+
switch (changeType) {
60+
case 'new':
61+
return 'This file was added.'
62+
case 'deleted':
63+
return 'This file was deleted.'
64+
case 'rename-pure':
65+
return 'This file was renamed.'
66+
case 'rename-changed':
67+
return 'This file was renamed and modified.'
68+
default:
69+
return null
70+
}
71+
}
72+
73+
const message = getChangeTypeMessage(changeType)
74+
75+
if (fileDiffMetadata && hasContent) {
76+
return (
77+
<PierreFileDiff
78+
fileDiff={fileDiffMetadata}
79+
lineAnnotations={annotations}
80+
selectedLines={selectedRange}
81+
renderAnnotation={(annotation) =>
82+
annotation.metadata ? (
83+
<CommentThread thread={annotation.metadata} clLink={clLink} />
84+
) : (
85+
<CommentForm
86+
side={annotation.side}
87+
lineNumber={annotation.lineNumber}
88+
filePath={filePath}
89+
fileDiff={fileDiffMetadata}
90+
selectedRange={selectedRange}
91+
clLink={clLink}
92+
onSubmit={handleSubmitComment}
93+
onCancel={handleCancelComment}
94+
/>
95+
)
96+
}
97+
renderHoverUtility={(getHoveredLine) => (
98+
<HoverButton getHoveredLine={getHoveredLine} onAddComment={addCommentAtLine} />
99+
)}
100+
options={{
101+
theme: resolvedTheme === 'dark' ? 'min-dark' : 'min-light',
102+
diffStyle: 'unified',
103+
diffIndicators: 'classic',
104+
overflow: 'wrap',
105+
disableFileHeader: true,
106+
enableLineSelection: true,
107+
enableHoverUtility: true,
108+
onLineSelectionEnd: handleLineSelectionEnd,
109+
unsafeCSS: `
110+
::-webkit-scrollbar { display: none !important; }
111+
code { padding: 0 !important; }
112+
`
113+
}}
114+
style={{ '--diffs-font-size': '14px' } as React.CSSProperties}
115+
/>
116+
)
117+
}
118+
119+
if (isBinary) {
120+
return (
121+
<div className='p-4 text-center'>
122+
<div className='text-primary'>Binary file</div>
123+
{message && <div className='text-secondary mt-1 text-sm'>{message}</div>}
124+
</div>
125+
)
126+
}
127+
128+
if (message) {
129+
return (
130+
<div className='p-4 text-center'>
131+
<div className='text-primary'>Load Diff</div>
132+
<div className='text-secondary mt-1 text-sm'>{message}</div>
133+
</div>
134+
)
135+
}
136+
137+
if (!hasContent) {
138+
return <div className='text-tertiary p-4 text-center'>No content change</div>
139+
}
140+
141+
return null
142+
}
143+
27144
export default function FileDiff({
28145
fileChangeData,
29146
fileChangeIsLoading,
@@ -35,7 +152,11 @@ export default function FileDiff({
35152
onLoadMore
36153
}: FileDiffProps) {
37154
const virtuosoRef = useRef<any>(null)
38-
const { resolvedTheme } = useTheme()
155+
const router = useRouter()
156+
const { link } = router.query
157+
const clLink = typeof link === 'string' ? link : ''
158+
159+
const { data: commentsData } = useGetComments(clLink)
39160

40161
const [pageDataMap, setPageDataMap] = useState<Map<number, DiffItem[]>>(new Map())
41162

@@ -102,81 +223,6 @@ export default function FileDiff({
102223
setExpandedMap(Object.fromEntries(diffFiles.map((f) => [f.path, false])))
103224
}, [diffFiles])
104225

105-
const getChangeTypeMessage = (changeType: ChangeTypes | null): string | null => {
106-
switch (changeType) {
107-
case 'new':
108-
return 'This file was added.'
109-
case 'deleted':
110-
return 'This file was deleted.'
111-
case 'rename-pure':
112-
return 'This file was renamed.'
113-
case 'rename-changed':
114-
return 'This file was renamed and modified.'
115-
default:
116-
return null
117-
}
118-
}
119-
120-
const RenderDiffView = ({
121-
fileDiffMetadata,
122-
changeType,
123-
isBinary,
124-
hasContent
125-
}: {
126-
fileDiffMetadata: FileDiffMetadata | null
127-
changeType: ChangeTypes | null
128-
isBinary: boolean
129-
hasContent: boolean
130-
}) => {
131-
const message = getChangeTypeMessage(changeType)
132-
133-
if (fileDiffMetadata && hasContent) {
134-
return (
135-
<PierreFileDiff
136-
fileDiff={fileDiffMetadata}
137-
options={{
138-
theme: resolvedTheme === 'dark' ? 'min-dark' : 'min-light',
139-
diffStyle: 'unified',
140-
diffIndicators: 'classic',
141-
overflow: 'wrap',
142-
disableFileHeader: true,
143-
unsafeCSS: `
144-
:host { overflow-x: hidden !important; }
145-
* { overflow-x: hidden !important; }
146-
::-webkit-scrollbar { display: none !important; }
147-
code { padding: 0 !important; }
148-
`
149-
}}
150-
style={{ '--diffs-font-size': '14px' } as React.CSSProperties}
151-
/>
152-
)
153-
}
154-
155-
if (isBinary) {
156-
return (
157-
<div className='p-4 text-center'>
158-
<div className='text-primary'>Binary file</div>
159-
{message && <div className='text-secondary mt-1 text-sm'>{message}</div>}
160-
</div>
161-
)
162-
}
163-
164-
if (message) {
165-
return (
166-
<div className='p-4 text-center'>
167-
<div className='text-primary'>Load Diff</div>
168-
<div className='text-secondary mt-1 text-sm'>{message}</div>
169-
</div>
170-
)
171-
}
172-
173-
if (!hasContent) {
174-
return <div className='text-tertiary p-4 text-center'>No content change</div>
175-
}
176-
177-
return null
178-
}
179-
180226
const DiffItemComponent = (index: number) => {
181227
const { file, fileDiffMetadata, stats, changeType, isBinary, hasContent } = parsedFiles[index]
182228
const isExpanded = expandedMap[file.path]
@@ -211,11 +257,14 @@ export default function FileDiff({
211257

212258
<div className='copyable-text'>
213259
{isExpanded && (
214-
<RenderDiffView
260+
<SingleFileDiffView
261+
filePath={file.path}
215262
fileDiffMetadata={fileDiffMetadata}
216263
changeType={changeType}
217264
isBinary={isBinary}
218265
hasContent={hasContent}
266+
clLink={clLink}
267+
commentsData={commentsData}
219268
/>
220269
)}
221270
</div>

0 commit comments

Comments
 (0)