1
1
"use client"
2
2
3
- import { useState , MouseEvent } from "react"
3
+ import { useState , MouseEvent , Dispatch , SetStateAction , useRef } from "react"
4
4
import { useDropzone } from "react-dropzone"
5
5
import { Dialog , DialogContent , DialogHeader , DialogTitle } from "./ui/dialog"
6
6
import { Button } from "./ui/button"
7
7
import { Upload , X } from "lucide-react"
8
8
import JSZip from "jszip" ;
9
9
import { useChat } from "@/components/chat-provider" ;
10
10
import { toast } from "sonner" ;
11
+ import { Checkbox } from "@/components/ui/checkbox" ;
12
+ import path from "node:path" ;
11
13
12
14
interface UploadDialogProps {
13
15
open : boolean
14
16
onOpenChange : ( open : boolean ) => void
17
+ setMessage : Dispatch < SetStateAction < string > >
15
18
}
16
19
17
- export function UploadDialog ( { open, onOpenChange } : UploadDialogProps ) {
20
+ export function UploadDialog ( { open, onOpenChange, setMessage } : UploadDialogProps ) {
18
21
const [ uploadPath , setUploadPath ] = useState ( "" )
19
-
20
22
const { uploadFiles} = useChat ( ) ;
21
-
22
23
const [ filesToUpload , setFilesToUpload ] = useState < File [ ] > ( [ ] ) ;
24
+ const filePathsToAppend = useRef < Set < string > > ( new Set ( [ ] ) ) ;
25
+ const [ disableUploadPath , setDisableUploadPath ] = useState ( false ) ;
23
26
24
27
25
28
const { getRootProps, getInputProps, isDragActive } = useDropzone ( {
26
29
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
+ } ) ;
28
40
}
29
41
} )
30
42
31
43
const cleanup = ( open ? : boolean ) => {
32
44
if ( open !== undefined && open ) {
33
45
return
34
46
}
47
+
48
+ filePathsToAppend . current = new Set < string > ( [ ] ) ;
49
+ setUploadPath ( "" )
35
50
setFilesToUpload ( [ ] ) ;
51
+ setDisableUploadPath ( false ) ;
36
52
onOpenChange ( false )
37
53
}
38
54
39
55
const handleFilesUpload = async ( e : MouseEvent < HTMLButtonElement > ) => {
40
56
e . preventDefault ( )
57
+ let success = true ;
58
+ setDisableUploadPath ( true ) ;
41
59
42
60
try {
43
61
// Create a new JSZip instance
@@ -66,14 +84,23 @@ export function UploadDialog({ open, onOpenChange }: UploadDialogProps) {
66
84
formData . append ( 'uploadPath' , uploadPath ) ;
67
85
68
86
// Upload to agent API
69
- uploadFiles ( formData )
87
+ success = await uploadFiles ( formData )
70
88
71
89
// eslint-disable-next-line @typescript-eslint/no-explicit-any
72
90
} catch ( error : any ) {
91
+ success = false ;
73
92
toast . error ( "Failed to zip and upload files:" , {
74
93
description : error . message ,
75
94
} ) ;
76
95
}
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
+ }
77
104
}
78
105
79
106
@@ -106,6 +133,7 @@ export function UploadDialog({ open, onOpenChange }: UploadDialogProps) {
106
133
onChange = { ( e ) => setUploadPath ( e . target . value ) }
107
134
className = "w-full px-3 py-2 text-sm border rounded-md focus:outline-none focus:ring-1 focus:ring-ring"
108
135
placeholder = "Enter upload path..."
136
+ disabled = { disableUploadPath }
109
137
/>
110
138
</ div >
111
139
@@ -131,7 +159,7 @@ export function UploadDialog({ open, onOpenChange }: UploadDialogProps) {
131
159
132
160
{ filesToUpload . length > 0 && (
133
161
< 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 >
135
163
< div className = "space-y-1 max-h-32 overflow-y-auto" >
136
164
{ filesToUpload . map ( ( file , index ) => (
137
165
< div
@@ -141,14 +169,28 @@ export function UploadDialog({ open, onOpenChange }: UploadDialogProps) {
141
169
{ /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */ }
142
170
{ /*// @ts -expect-error*/ }
143
171
< 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
+
152
194
</ div >
153
195
) ) }
154
196
</ div >
0 commit comments