55
66import React , { useCallback , useContext , useEffect , useRef , useState } from 'react' ;
77import { ContextDropdown } from './contextDropdown' ;
8- import { copyIcon , editIcon , quoteIcon , trashIcon } from './icon' ;
8+ import { copyIcon , editIcon , quoteIcon , sparkleIcon , stopCircleIcon , trashIcon } from './icon' ;
99import { nbsp , Spaced } from './space' ;
1010import { Timestamp } from './timestamp' ;
1111import { AuthorLink , Avatar } from './user' ;
@@ -56,6 +56,7 @@ export function CommentView(commentProps: Props) {
5656 id = { id }
5757 key = { `editComment${ id } ` }
5858 body = { currentDraft || bodyMd }
59+ isPRDescription = { isPRDescription }
5960 onCancel = { ( ) => {
6061 if ( pr ?. pendingCommentDrafts ) {
6162 delete pr . pendingCommentDrafts [ id ] ;
@@ -208,14 +209,17 @@ type FormInputSet = {
208209type EditCommentProps = {
209210 id : number ;
210211 body : string ;
212+ isPRDescription ?: boolean ;
211213 onCancel : ( ) => void ;
212214 onSave : ( body : string ) => Promise < any > ;
213215} ;
214216
215- function EditComment ( { id, body, onCancel, onSave } : EditCommentProps ) {
216- const { updateDraft } = useContext ( PullRequestContext ) ;
217+ function EditComment ( { id, body, isPRDescription , onCancel, onSave } : EditCommentProps ) {
218+ const { updateDraft, pr , generateDescription , cancelGenerateDescription } = useContext ( PullRequestContext ) ;
217219 const draftComment = useRef < { body : string ; dirty : boolean } > ( { body, dirty : false } ) ;
218220 const form = useRef < HTMLFormElement > ( ) ;
221+ const [ isGenerating , setIsGenerating ] = useState ( false ) ;
222+ const [ canGenerate , setCanGenerate ] = useState ( false ) ;
219223
220224 useEffect ( ( ) => {
221225 const interval = setInterval ( ( ) => {
@@ -227,6 +231,13 @@ function EditComment({ id, body, onCancel, onSave }: EditCommentProps) {
227231 return ( ) => clearInterval ( interval ) ;
228232 } , [ draftComment ] ) ;
229233
234+ useEffect ( ( ) => {
235+ // Check if description generation is available
236+ if ( isPRDescription && pr ?. generateDescriptionTitle ) {
237+ setCanGenerate ( true ) ;
238+ }
239+ } , [ isPRDescription , pr ?. generateDescriptionTitle ] ) ;
240+
230241 const submit = useCallback ( async ( ) => {
231242 const { markdown, submitButton } : FormInputSet = form . current ! ;
232243 submitButton . disabled = true ;
@@ -263,9 +274,57 @@ function EditComment({ id, body, onCancel, onSave }: EditCommentProps) {
263274 [ draftComment ] ,
264275 ) ;
265276
277+ const handleGenerateDescription = useCallback ( async ( ) => {
278+ if ( ! generateDescription ) {
279+ return ;
280+ }
281+ setIsGenerating ( true ) ;
282+ try {
283+ const generated = await generateDescription ( ) ;
284+ if ( generated ?. description && form . current ) {
285+ const textarea = form . current . markdown as HTMLTextAreaElement ;
286+ textarea . value = generated . description ;
287+ draftComment . current . body = generated . description ;
288+ draftComment . current . dirty = true ;
289+ }
290+ } finally {
291+ setIsGenerating ( false ) ;
292+ }
293+ } , [ generateDescription ] ) ;
294+
295+ const handleCancelGenerate = useCallback ( ( ) => {
296+ if ( cancelGenerateDescription ) {
297+ cancelGenerateDescription ( ) ;
298+ }
299+ setIsGenerating ( false ) ;
300+ } , [ cancelGenerateDescription ] ) ;
301+
266302 return (
267303 < form ref = { form as React . MutableRefObject < HTMLFormElement > } onSubmit = { onSubmit } >
268- < textarea name = "markdown" defaultValue = { body } onKeyDown = { onKeyDown } onInput = { onInput } />
304+ < div className = "textarea-wrapper" >
305+ < textarea name = "markdown" defaultValue = { body } onKeyDown = { onKeyDown } onInput = { onInput } disabled = { isGenerating } />
306+ { canGenerate && isPRDescription ? (
307+ isGenerating ? (
308+ < button
309+ type = "button"
310+ title = "Cancel"
311+ className = "title-action icon-button"
312+ onClick = { handleCancelGenerate }
313+ >
314+ { stopCircleIcon }
315+ </ button >
316+ ) : (
317+ < button
318+ type = "button"
319+ title = { pr ?. generateDescriptionTitle || 'Generate description' }
320+ className = "title-action icon-button"
321+ onClick = { handleGenerateDescription }
322+ >
323+ { sparkleIcon }
324+ </ button >
325+ )
326+ ) : null }
327+ </ div >
269328 < div className = "form-actions" >
270329 < button className = "secondary" onClick = { onCancel } >
271330 Cancel
0 commit comments