@@ -8,6 +8,7 @@ import toast from 'react-hot-toast'
88import { Button } from '@gitmono/ui/Button'
99import { Dialog } from '@gitmono/ui/Dialog'
1010
11+ import { usePostClList } from '@/hooks/CL/usePostClList'
1112import { useDiffPreview } from '@/hooks/useDiffPreview'
1213import { useGetCurrentUser } from '@/hooks/useGetCurrentUser'
1314import { useUpdateBlob } from '@/hooks/useUpdateBlob'
@@ -21,13 +22,21 @@ interface BlobEditorProps {
2122}
2223
2324type ViewMode = 'edit' | 'preview'
25+ type CLModeType = 'force_create' | 'try_reuse'
26+
27+ interface ClList {
28+ id : string
29+ link : string
30+ title : string
31+ author : string
32+ }
2433
2534export default function BlobEditor ( { fileContent, filePath, fileName, onCancel } : BlobEditorProps ) {
2635 const { data : currentUser } = useGetCurrentUser ( )
2736
2837 const updateBlobMutation = useUpdateBlob ( )
2938 const diffPreviewMutation = useDiffPreview ( )
30-
39+ const { mutate : fetchClList } = usePostClList ( )
3140 const [ content , setContent ] = useState ( fileContent )
3241
3342 const [ editedFileName , setEditedFileName ] = useState ( fileName )
@@ -36,6 +45,11 @@ export default function BlobEditor({ fileContent, filePath, fileName, onCancel }
3645
3746 const [ viewMode , setViewMode ] = useState < ViewMode > ( 'edit' )
3847
48+ const [ clOpenList , setClOpenList ] = useState < ClList [ ] > ( [ ] )
49+ const [ selectedClMode , setSelectedClMode ] = useState < CLModeType > ( 'force_create' )
50+ const [ selectedClLink , setSelectedClLink ] = useState < string | null > ( null )
51+ const [ skipBuild , setSkipBuild ] = useState ( false )
52+
3953 const [ diffResult , setDiffResult ] = useState < any > ( null )
4054 const [ fileDiffMetadata , setFileDiffMetadata ] = useState < FileDiffMetadata | null > ( null )
4155
@@ -87,7 +101,6 @@ export default function BlobEditor({ fileContent, filePath, fileName, onCancel }
87101 if ( patches . length > 0 && patches [ 0 ] . files . length > 0 ) {
88102 let metadata = patches [ 0 ] . files [ 0 ]
89103
90- // 设置文件名和语言
91104 if ( ! metadata . name ) {
92105 metadata = { ...metadata , name : editedFileName }
93106 }
@@ -107,15 +120,49 @@ export default function BlobEditor({ fileContent, filePath, fileName, onCancel }
107120 return
108121 }
109122 setIsCommitDialogOpen ( true )
110- } , [ hasChanges ] )
123+ setSelectedClMode ( 'force_create' )
124+ setSelectedClLink ( null )
125+ setSkipBuild ( false )
126+
127+ fetchClList (
128+ {
129+ data : {
130+ pagination : { page : 1 , per_page : 100 } ,
131+ additional : {
132+ asc : true ,
133+ status : 'open'
134+ }
135+ }
136+ } ,
137+ {
138+ onSuccess : ( response ) => {
139+ const data = response . data
140+ const items = data ?. items || [ ]
141+
142+ setClOpenList (
143+ items . map ( ( item : any ) => ( {
144+ id : item . id || '' ,
145+ link : item . link || '' ,
146+ title : item . title || '' ,
147+ author : item . author || ''
148+ } ) )
149+ )
150+ }
151+ }
152+ )
153+ } , [ fetchClList , hasChanges ] )
111154
112155 const handleSave = useCallback ( async ( ) => {
156+ const mode = selectedClMode === 'force_create' ? 'force_create' : { try_reuse : selectedClLink }
157+
113158 await updateBlobMutation . mutateAsync ( {
114159 path : fullEditedPath ,
115160 content : content ,
116161 commit_message : commitDescription ? `${ commitMessage } \n\n${ commitDescription } ` : commitMessage ,
117162 author_email : currentUser ?. email ,
118- author_username : currentUser ?. username
163+ author_username : currentUser ?. username ,
164+ mode : mode ,
165+ skip_build : skipBuild
119166 } )
120167
121168 setIsCommitDialogOpen ( false )
@@ -128,9 +175,22 @@ export default function BlobEditor({ fileContent, filePath, fileName, onCancel }
128175 commitMessage ,
129176 currentUser ?. email ,
130177 currentUser ?. username ,
178+ selectedClMode ,
179+ selectedClLink ,
180+ skipBuild ,
131181 onCancel
132182 ] )
133183
184+ const handleDialogClose = useCallback ( ( open : boolean ) => {
185+ setIsCommitDialogOpen ( open )
186+ if ( ! open ) {
187+ setSelectedClMode ( 'force_create' )
188+ setSelectedClLink ( null )
189+ setSkipBuild ( false )
190+ setClOpenList ( [ ] )
191+ }
192+ } , [ ] )
193+
134194 const handleTextareaChange = ( e : React . ChangeEvent < HTMLTextAreaElement > ) => {
135195 setContent ( e . target . value )
136196
@@ -286,7 +346,7 @@ export default function BlobEditor({ fileContent, filePath, fileName, onCancel }
286346 </ div >
287347 </ div >
288348
289- < Dialog . Root open = { isCommitDialogOpen } onOpenChange = { setIsCommitDialogOpen } >
349+ < Dialog . Root open = { isCommitDialogOpen } onOpenChange = { handleDialogClose } >
290350 < Dialog . Content >
291351 < Dialog . CloseButton />
292352 < Dialog . Header >
@@ -317,15 +377,68 @@ export default function BlobEditor({ fileContent, filePath, fileName, onCancel }
317377 disabled = { updateBlobMutation . isPending }
318378 />
319379 </ div >
380+
381+ < div className = 'flex flex-col gap-2' >
382+ < label className = 'text-sm font-medium text-gray-700' > Change List Mode *</ label >
383+ < select
384+ value = { selectedClMode }
385+ onChange = { ( e ) => {
386+ const mode = e . target . value as CLModeType
387+
388+ setSelectedClMode ( mode )
389+ if ( mode === 'force_create' ) {
390+ setSelectedClLink ( null )
391+ }
392+ // 切换到 try_reuse 时不自动选择,保持为 null
393+ } }
394+ className = 'w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500'
395+ disabled = { updateBlobMutation . isPending }
396+ >
397+ < option value = 'try_reuse' > Try reuse existing CL</ option >
398+ < option value = 'force_create' > Create new CL</ option >
399+ </ select >
400+ </ div >
401+
402+ { selectedClMode === 'try_reuse' && (
403+ < div className = 'flex flex-col gap-2' >
404+ < label className = 'text-sm font-medium text-gray-700' > Select Change List</ label >
405+ < select
406+ value = { selectedClLink || '' }
407+ onChange = { ( e ) => setSelectedClLink ( e . target . value || null ) }
408+ className = 'w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500'
409+ disabled = { updateBlobMutation . isPending }
410+ >
411+ < option value = '' > Auto select or create new</ option >
412+ { clOpenList . map ( ( cl ) => (
413+ < option key = { cl . link } value = { cl . link } >
414+ { cl . link } - { cl . title } ({ cl . author } )
415+ </ option >
416+ ) ) }
417+ </ select >
418+ < p className = 'text-xs text-gray-500' >
419+ If no CL is selected, the system will automatically search for an existing open CL or create a new one
420+ </ p >
421+ </ div >
422+ ) }
423+
424+ < div className = 'flex items-center gap-2' >
425+ < input
426+ type = 'checkbox'
427+ id = 'skipBuild'
428+ checked = { skipBuild }
429+ onChange = { ( e ) => setSkipBuild ( e . target . checked ) }
430+ className = 'h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500'
431+ disabled = { updateBlobMutation . isPending }
432+ />
433+ < label htmlFor = 'skipBuild' className = 'text-sm font-medium text-gray-700' >
434+ Skip automatic build after commit
435+ </ label >
436+ </ div >
320437 </ div >
321438
322439 < Dialog . Footer >
323440 < Dialog . TrailingActions >
324- < Button
325- variant = 'flat'
326- onClick = { ( ) => setIsCommitDialogOpen ( false ) }
327- disabled = { updateBlobMutation . isPending }
328- >
441+ < Button variant = 'flat' onClick = { ( ) => handleDialogClose ( false ) } disabled = { updateBlobMutation . isPending } >
329442 Cancel
330443 </ Button >
331444 < Button onClick = { handleSave } disabled = { updateBlobMutation . isPending || ! commitMessage . trim ( ) } >
0 commit comments