import SwiftUI struct BrowseView: View { let connection: NASConnection @Binding var selectedPath: String @Environment(\.dismiss) private var dismiss @State private var currentPath: String = "/" @State private var items: [NASItem] = [] @State private var isLoading = false @State private var error: String? = nil @State private var showCreateFolder = false @State private var newFolderName = "" private var service: any NASTransferProtocol { connection.nasProtocol == .smb ? SMBService() : SFTPService() } var body: some View { NavigationStack { ZStack { Color.white.ignoresSafeArea() VStack(spacing: 0) { // Path bar HStack { Image(systemName: "externaldrive.fill") .foregroundColor(AppTheme.textSecondary) .font(.system(size: 13)) Text(currentPath) .font(AppTheme.captionFont) .foregroundColor(AppTheme.textSecondary) .lineLimit(1) .truncationMode(.middle) Spacer() } .padding(.horizontal, 16) .padding(.vertical, 10) .background(Color(hex: "#F9FAFB")) Divider() if isLoading { Spacer() ProgressView() Spacer() } else if let error { Spacer() VStack(spacing: 12) { Image(systemName: "exclamationmark.triangle") .font(.system(size: 32)) .foregroundColor(AppTheme.textTertiary) Text(error) .font(AppTheme.bodyFont) .foregroundColor(AppTheme.textSecondary) .multilineTextAlignment(.center) } .padding() Spacer() } else { List { if currentPath != "/" { Button(action: navigateUp) { HStack { Image(systemName: "arrow.up") .foregroundColor(AppTheme.textSecondary) Text("Parent folder") .font(AppTheme.bodyFont) .foregroundColor(AppTheme.textSecondary) } } } ForEach(items) { item in FolderRow( item: item, isSelected: selectedPath == item.path ) { if item.isDirectory { navigate(to: item.path) } else { selectedPath = item.path } } .listRowInsets(EdgeInsets()) .listRowSeparator(.visible) } } .listStyle(.plain) } } } .navigationTitle("Choose folder") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarLeading) { Button("Cancel") { dismiss() } } ToolbarItem(placement: .navigationBarTrailing) { Button { newFolderName = "" showCreateFolder = true } label: { Image(systemName: "folder.badge.plus") } } ToolbarItem(placement: .bottomBar) { Button("Select this folder") { selectedPath = currentPath dismiss() } .font(.system(size: 17, weight: .semibold)) .foregroundColor(AppTheme.blue) } } .alert("New folder", isPresented: $showCreateFolder) { TextField("Folder name", text: $newFolderName) Button("Create") { Task { await createFolder() } } Button("Cancel", role: .cancel) {} } } .task { await loadDirectory(path: currentPath) } } private func loadDirectory(path: String) async { isLoading = true error = nil do { let s: any NASTransferProtocol = connection.nasProtocol == .smb ? SMBService() : SFTPService() try await s.connect(to: connection.host, port: connection.port, username: connection.username, password: connection.password) items = try await s.listDirectory(at: path).filter { $0.isDirectory } s.disconnect() } catch let e as BackupError { self.error = e.errorDescription } catch { self.error = error.localizedDescription } isLoading = false } private func navigate(to path: String) { currentPath = path Task { await loadDirectory(path: path) } } private func navigateUp() { let parent = (currentPath as NSString).deletingLastPathComponent navigate(to: parent.isEmpty ? "/" : parent) } private func createFolder() async { guard !newFolderName.isEmpty else { return } let newPath = "\(currentPath)/\(newFolderName)" do { let s: any NASTransferProtocol = connection.nasProtocol == .smb ? SMBService() : SFTPService() try await s.connect(to: connection.host, port: connection.port, username: connection.username, password: connection.password) try await s.createDirectory(at: newPath) s.disconnect() await loadDirectory(path: currentPath) } catch {} } }