Files
hapi/ios/Hapi/Features/DirectoryBrowser/RemoteDirectoryBrowserView.swift
T

129 lines
4.9 KiB
Swift

import HapiClient
import SwiftUI
/// Reusable SwiftUI presentation for ``RemoteDirectoryBrowserModel``.
struct RemoteDirectoryBrowserView: View {
@Bindable var model: RemoteDirectoryBrowserModel
let onSelect: (String) -> Void
@Environment(\.dismiss) private var dismiss
var body: some View {
NavigationStack {
List {
if model.roots.count > 1 {
Section("Workspace Roots") {
ForEach(model.roots, id: \.self) { root in
Button {
model.navigate(to: root)
} label: {
Label(root, systemImage: "externaldrive")
.lineLimit(1)
.truncationMode(.middle)
}
}
}
}
Section("Current Directory") {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 4) {
ForEach(model.breadcrumbs) { breadcrumb in
Button(breadcrumb.label) {
model.navigate(to: breadcrumb.path)
}
.buttonStyle(.bordered)
.controlSize(.small)
}
}
}
Text(model.path)
.font(.system(.footnote, design: .monospaced))
.foregroundStyle(.secondary)
.lineLimit(2)
.truncationMode(.middle)
Toggle(
"Show Hidden",
isOn: Binding(
get: { model.includeHidden },
set: { model.setIncludeHidden($0) }
)
)
}
Section("Subdirectories") {
if model.isLoading {
HStack {
Spacer()
ProgressView()
Spacer()
}
} else if let error = model.error {
VStack(alignment: .leading, spacing: 8) {
Text(error).foregroundStyle(.red)
Button("Retry") {
model.refresh()
}
}
} else if model.entries.isEmpty {
Text("No subdirectories")
.foregroundStyle(.secondary)
} else {
ForEach(model.entries, id: \.name) { entry in
Button {
model.navigateEntry(entry.name)
} label: {
HStack {
Image(systemName: "folder")
Text(entry.name)
.lineLimit(1)
.truncationMode(.middle)
Spacer()
Image(systemName: "chevron.right")
.font(.caption)
.foregroundStyle(.tertiary)
}
}
}
}
}
}
.navigationTitle("Choose Directory")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel") {
model.close()
dismiss()
}
}
ToolbarItem(placement: .confirmationAction) {
Button("Select") {
let path = model.path
onSelect(path)
model.close()
dismiss()
}
.disabled(model.isLoading || model.error != nil)
}
ToolbarItemGroup(placement: .bottomBar) {
Button {
model.navigateUp()
} label: {
Label("Up", systemImage: "arrow.up")
}
.disabled(!model.canGoUp || model.isLoading)
Spacer()
Button {
model.refresh()
} label: {
Label("Refresh", systemImage: "arrow.clockwise")
}
.disabled(model.isLoading)
}
}
}
}
}