11import SwiftUI
22
33struct FileItem : Identifiable {
4- let id = UUID ( )
4+ var id : String { path }
55 let name : String
66 let path : String
77 let isDirectory : Bool
@@ -25,6 +25,8 @@ struct FileExplorerView: View {
2525 @State private var showDeleteConfirmation = false
2626 @State private var actionTarget : FileItem ?
2727 @State private var newItemName = " "
28+ @State private var sheetOperationErrorMessage : String ?
29+ @State private var isPerformingOperation = false
2830
2931 let containerName : String
3032 let projectName : String
@@ -162,9 +164,15 @@ struct FileExplorerView: View {
162164 Button ( " Delete " , role: . destructive) {
163165 Task { await deleteItem ( ) }
164166 }
165- Button ( " Cancel " , role: . cancel) { }
167+ Button ( " Cancel " , role: . cancel) {
168+ actionTarget = nil
169+ }
166170 } message: {
167- Text ( " Are you sure you want to delete \( actionTarget? . name ?? " this item " ) ? " )
171+ if actionTarget? . isDirectory == true {
172+ Text ( " Are you sure you want to delete \( actionTarget? . name ?? " this folder " ) and all contents? " )
173+ } else {
174+ Text ( " Are you sure you want to delete \( actionTarget? . name ?? " this item " ) ? " )
175+ }
168176 }
169177 . task {
170178 await loadFiles ( )
@@ -204,63 +212,87 @@ struct FileExplorerView: View {
204212 actionTarget = parent
205213 newItemName = " "
206214 operationErrorMessage = nil
215+ sheetOperationErrorMessage = nil
207216 showCreateFileSheet = true
208217 }
209218
210219 private func prepareCreateFolder( in parent: FileItem ? = nil ) {
211220 actionTarget = parent
212221 newItemName = " "
213222 operationErrorMessage = nil
223+ sheetOperationErrorMessage = nil
214224 showCreateFolderSheet = true
215225 }
216226
217227 private func prepareRename( _ item: FileItem ) {
218228 actionTarget = item
219229 newItemName = item. name
220230 operationErrorMessage = nil
231+ sheetOperationErrorMessage = nil
221232 showRenameSheet = true
222233 }
223234
224235 private func prepareDelete( _ item: FileItem ) {
225236 actionTarget = item
226237 operationErrorMessage = nil
238+ sheetOperationErrorMessage = nil
227239 showDeleteConfirmation = true
228240 }
229241
230242 private func createFile( ) async {
243+ guard !isPerformingOperation else { return }
244+ isPerformingOperation = true
245+ defer { isPerformingOperation = false }
246+
231247 do {
232248 let directoryPath = targetDirectoryPath ( )
233249 let path = ( ( directoryPath as NSString ) . appendingPathComponent ( newItemName) as NSString ) . standardizingPath
234250 try await fileService. createFile ( path: path)
251+ selectedFile = FileItem ( name: ( path as NSString ) . lastPathComponent, path: path, isDirectory: false )
252+ sheetOperationErrorMessage = nil
235253 showCreateFileSheet = false
236254 await loadFiles ( )
237255 } catch {
238- operationErrorMessage = " Failed to create file: \( error. localizedDescription) "
256+ sheetOperationErrorMessage = " Failed to create file: \( error. localizedDescription) "
239257 }
240258 }
241259
242260 private func createFolder( ) async {
261+ guard !isPerformingOperation else { return }
262+ isPerformingOperation = true
263+ defer { isPerformingOperation = false }
264+
243265 do {
244266 let directoryPath = targetDirectoryPath ( )
245267 let path = ( ( directoryPath as NSString ) . appendingPathComponent ( newItemName) as NSString ) . standardizingPath
246268 try await fileService. createDirectory ( path: path)
269+ selectedFile = FileItem ( name: ( path as NSString ) . lastPathComponent, path: path, isDirectory: true )
270+ sheetOperationErrorMessage = nil
247271 showCreateFolderSheet = false
248272 await loadFiles ( )
249273 } catch {
250- operationErrorMessage = " Failed to create folder: \( error. localizedDescription) "
274+ sheetOperationErrorMessage = " Failed to create folder: \( error. localizedDescription) "
251275 }
252276 }
253277
254278 private func renameItem( ) async {
255279 guard let actionTarget else { return }
280+ guard !isPerformingOperation else { return }
281+ isPerformingOperation = true
282+ defer { isPerformingOperation = false }
283+
256284 do {
257285 let parentDirectory = ( actionTarget. path as NSString ) . deletingLastPathComponent
258286 let destinationPath = ( ( parentDirectory as NSString ) . appendingPathComponent ( newItemName) as NSString ) . standardizingPath
259287 try await fileService. renameItem ( at: actionTarget. path, to: destinationPath)
288+ if selectedFile? . path == actionTarget. path {
289+ selectedFile = FileItem ( name: ( destinationPath as NSString ) . lastPathComponent, path: destinationPath, isDirectory: actionTarget. isDirectory)
290+ }
291+ sheetOperationErrorMessage = nil
260292 showRenameSheet = false
261293 await loadFiles ( )
262294 } catch {
263- operationErrorMessage = " Failed to rename item: \( error. localizedDescription) "
295+ sheetOperationErrorMessage = " Failed to rename item: \( error. localizedDescription) "
264296 }
265297 }
266298
@@ -271,6 +303,7 @@ struct FileExplorerView: View {
271303 if selectedFile? . path == actionTarget. path {
272304 selectedFile = nil
273305 }
306+ self . actionTarget = nil
274307 await loadFiles ( )
275308 } catch {
276309 operationErrorMessage = " Failed to delete item: \( error. localizedDescription) "
@@ -310,11 +343,21 @@ struct FileExplorerView: View {
310343
311344 TextField ( prompt, text: $newItemName)
312345 . textFieldStyle ( . roundedBorder)
346+ . disabled ( isPerformingOperation)
347+
348+ if let sheetOperationErrorMessage {
349+ Text ( sheetOperationErrorMessage)
350+ . font ( . caption)
351+ . foregroundStyle ( . red)
352+ }
313353
314354 Spacer ( )
315355
316356 HStack {
317357 Button ( " Cancel " ) {
358+ actionTarget = nil
359+ sheetOperationErrorMessage = nil
360+ isPerformingOperation = false
318361 showCreateFileSheet = false
319362 showCreateFolderSheet = false
320363 showRenameSheet = false
@@ -326,7 +369,7 @@ struct FileExplorerView: View {
326369 Task { await onSubmit ( ) }
327370 }
328371 . keyboardShortcut ( . defaultAction)
329- . disabled ( newItemName. trimmingCharacters ( in: . whitespacesAndNewlines) . isEmpty)
372+ . disabled ( newItemName. trimmingCharacters ( in: . whitespacesAndNewlines) . isEmpty || isPerformingOperation )
330373 }
331374 }
332375 . padding ( 20 )
0 commit comments