import SwiftUI struct TreeView: View { @State private var model: TreeViewModel init(client: GitbayClient, repo: String, directory: String, ref: String?) { _model = State(initialValue: TreeViewModel( client: client, repoPath: repo, directory: directory, ref: ref )) } var body: some View { List { ForEach(model.entries) { entry in if entry.isDirectory { NavigationLink(value: RepoRoute.tree( repo: model.repoPath, directory: model.childDirectory(entry), ref: model.ref )) { EntryRow(entry: entry) } } else { NavigationLink(value: RepoRoute.file( repo: model.repoPath, path: model.childDirectory(entry), ref: model.ref )) { EntryRow(entry: entry) } } } } .overlay { LoadStateOverlay(state: model.state) } .navigationTitle(model.directory.isEmpty ? String(model.repoPath.split(separator: "/").last ?? "") : String(model.directory.split(separator: "/").last ?? "")) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarTrailing) { NavigationLink(value: RepoRoute.log( repo: model.repoPath, ref: model.ref, path: nil )) { Image(systemName: "clock") } .accessibilityIdentifier("tree-history-button") } } .task { await model.load() } .refreshable { await model.load() } } } private struct EntryRow: View { let entry: TreeEntry var body: some View { HStack { Label { Text(entry.name) .lineLimit(1) } icon: { Image(systemName: icon) .foregroundStyle(entry.isDirectory ? Color.accentColor : .secondary) } Spacer() if let size = entry.size, !entry.isDirectory { Text(size.formatted(.byteCount(style: .file))) .font(.gbSans(.caption)) .foregroundStyle(.tertiary) .monospacedDigit() } } } private var icon: String { if entry.isDirectory { return "folder.fill" } if entry.isSymlink { return "arrow.turn.down.right" } return "doc" } }