@@ -2,19 +2,22 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
22import { type ChangeTypes , type FileDiffMetadata } from '@pierre/diffs'
33import { FileDiff as PierreFileDiff } from '@pierre/diffs/react'
44import { useTheme } from 'next-themes'
5+ import { useRouter } from 'next/router'
56import { Virtuoso } from 'react-virtuoso'
67
7- import { CommonPageDiffItem , CommonResultVecMuiTreeNode } from '@gitmono/types'
8+ import { CommonPageDiffItemSchema , CommonResultCodeReviewResponse , CommonResultVecMuiTreeNode } from '@gitmono/types'
89import { LoadingSpinner } from '@gitmono/ui'
910import { ExpandIcon , SparklesIcon } from '@gitmono/ui/Icons'
1011import { cn } from '@gitmono/ui/src/utils'
1112
1213import FileTree from '@/components/DiffView/TreeView/FileTree'
1314
15+ import { CommentForm , CommentThread , HoverButton , useComments } from './comment'
16+ import { useGetComments } from './hooks/useGetComments'
1417import { DiffItem , generateParsedFiles , parsedDiffs } from './parsedDiffs'
1518
1619interface 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+
27144export 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