Skip to content

Commit 8576653

Browse files
committed
fix(file-explorer): improve action feedback and state consistency
1 parent ab59c48 commit 8576653

1 file changed

Lines changed: 50 additions & 7 deletions

File tree

Sources/Zero/Views/FileExplorerView.swift

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import SwiftUI
22

33
struct 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

Comments
 (0)