11"use client"
22
3- import { useState , MouseEvent } from "react"
3+ import { useState , MouseEvent , Dispatch , SetStateAction , useRef } from "react"
44import { useDropzone } from "react-dropzone"
55import { Dialog , DialogContent , DialogHeader , DialogTitle } from "./ui/dialog"
66import { Button } from "./ui/button"
77import { Upload , X } from "lucide-react"
88import JSZip from "jszip" ;
99import { useChat } from "@/components/chat-provider" ;
1010import { toast } from "sonner" ;
11+ import { Checkbox } from "@/components/ui/checkbox" ;
12+ import path from "node:path" ;
1113
1214interface UploadDialogProps {
1315 open : boolean
1416 onOpenChange : ( open : boolean ) => void
17+ setMessage : Dispatch < SetStateAction < string > >
1518}
1619
17- export function UploadDialog ( { open, onOpenChange } : UploadDialogProps ) {
20+ export function UploadDialog ( { open, onOpenChange, setMessage } : UploadDialogProps ) {
1821 const [ uploadPath , setUploadPath ] = useState ( "" )
19-
2022 const { uploadFiles} = useChat ( ) ;
21-
2223 const [ filesToUpload , setFilesToUpload ] = useState < File [ ] > ( [ ] ) ;
24+ const filePathsToAppend = useRef < Set < string > > ( new Set ( [ ] ) ) ;
25+ const [ disableUploadPath , setDisableUploadPath ] = useState ( false ) ;
2326
2427
2528 const { getRootProps, getInputProps, isDragActive } = useDropzone ( {
2629 onDropAccepted : ( files : File [ ] ) => {
27- setFilesToUpload ( ( oldFiles ) => [ ...oldFiles , ...files ] ) ;
30+ setFilesToUpload ( ( oldFiles ) => {
31+ const updatedFiles = oldFiles ;
32+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
33+ // @ts -expect-error
34+ const oldSet = new Set < string > ( oldFiles . map ( ( f ) => f . relativePath ) ) ;
35+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
36+ // @ts -expect-error
37+ files . forEach ( file => oldSet . has ( file . relativePath ) ? { } : updatedFiles . push ( file ) )
38+ return updatedFiles ;
39+ } ) ;
2840 }
2941 } )
3042
3143 const cleanup = ( open ? : boolean ) => {
3244 if ( open !== undefined && open ) {
3345 return
3446 }
47+
48+ filePathsToAppend . current = new Set < string > ( [ ] ) ;
49+ setUploadPath ( "" )
3550 setFilesToUpload ( [ ] ) ;
51+ setDisableUploadPath ( false ) ;
3652 onOpenChange ( false )
3753 }
3854
3955 const handleFilesUpload = async ( e : MouseEvent < HTMLButtonElement > ) => {
4056 e . preventDefault ( )
57+ let success = true ;
58+ setDisableUploadPath ( true ) ;
4159
4260 try {
4361 // Create a new JSZip instance
@@ -66,14 +84,23 @@ export function UploadDialog({ open, onOpenChange }: UploadDialogProps) {
6684 formData . append ( 'uploadPath' , uploadPath ) ;
6785
6886 // Upload to agent API
69- uploadFiles ( formData )
87+ success = await uploadFiles ( formData )
7088
7189 // eslint-disable-next-line @typescript-eslint/no-explicit-any
7290 } catch ( error : any ) {
91+ success = false ;
7392 toast . error ( "Failed to zip and upload files:" , {
7493 description : error . message ,
7594 } ) ;
7695 }
96+ if ( success ) {
97+ for ( const filePath of filePathsToAppend . current ) {
98+ setMessage ( oldMessage => oldMessage + ' @"' + path . join ( uploadPath , filePath ) + '"' ) ;
99+ }
100+ cleanup ( )
101+ } else {
102+ setDisableUploadPath ( false ) ;
103+ }
77104 }
78105
79106
@@ -106,6 +133,7 @@ export function UploadDialog({ open, onOpenChange }: UploadDialogProps) {
106133 onChange = { ( e ) => setUploadPath ( e . target . value ) }
107134 className = "w-full px-3 py-2 text-sm border rounded-md focus:outline-none focus:ring-1 focus:ring-ring"
108135 placeholder = "Enter upload path..."
136+ disabled = { disableUploadPath }
109137 />
110138 </ div >
111139
@@ -131,7 +159,7 @@ export function UploadDialog({ open, onOpenChange }: UploadDialogProps) {
131159
132160 { filesToUpload . length > 0 && (
133161 < div className = "space-y-2" >
134- < h4 className = "text-sm font-medium" > Selected Files: </ h4 >
162+ < h4 className = "text-sm font-medium" > Selected Files (select the checkbox to append @filepath to message) </ h4 >
135163 < div className = "space-y-1 max-h-32 overflow-y-auto" >
136164 { filesToUpload . map ( ( file , index ) => (
137165 < div
@@ -141,14 +169,28 @@ export function UploadDialog({ open, onOpenChange }: UploadDialogProps) {
141169 { /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */ }
142170 { /*// @ts -expect-error*/ }
143171 < span className = "truncate" > { file . relativePath } </ span >
144- < Button
145- variant = "ghost"
146- size = "icon"
147- className = "h-6 w-6"
148- onClick = { ( ) => removeFile ( file ) }
149- >
150- < X className = "h-3 w-3" />
151- </ Button >
172+ < div className = "flex items-center justify-between gap-2" >
173+ < Checkbox onCheckedChange = { ( checked : boolean ) => {
174+ if ( checked ) {
175+ { /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */ }
176+ { /*// @ts -expect-error*/ }
177+ filePathsToAppend . current . add ( file . relativePath )
178+ } else {
179+ { /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */ }
180+ { /*// @ts -expect-error*/ }
181+ filePathsToAppend . current . delete ( file . relativePath )
182+ }
183+ } } />
184+ < Button
185+ variant = "ghost"
186+ size = "icon"
187+ className = "h-6 w-6"
188+ onClick = { ( ) => removeFile ( file ) }
189+ >
190+ < X className = "h-3 w-3" />
191+ </ Button >
192+ </ div >
193+
152194 </ div >
153195 ) ) }
154196 </ div >
0 commit comments