gitbay/Views/Repos/TreeView.swift
72 lines · 2314 bytes
1import SwiftUI
2
3struct TreeView: View {
4
5 @State private var model: TreeViewModel
6
7 init(client: GitbayClient, repo: String, directory: String, ref: String?) {
8 _model = State(initialValue: TreeViewModel(
9 client: client, repoPath: repo, directory: directory, ref: ref
10 ))
11 }
12
13 var body: some View {
14 List {
15 ForEach(model.entries) { entry in
16 if entry.isDirectory {
17 NavigationLink(value: RepoRoute.tree(
18 repo: model.repoPath,
19 directory: model.childDirectory(entry),
20 ref: model.ref
21 )) {
22 EntryRow(entry: entry)
23 }
24 } else {
25 NavigationLink(value: RepoRoute.file(
26 repo: model.repoPath,
27 path: model.childDirectory(entry),
28 ref: model.ref
29 )) {
30 EntryRow(entry: entry)
31 }
32 }
33 }
34 }
35 .overlay { LoadStateOverlay(state: model.state) }
36 .navigationTitle(model.directory.isEmpty
37 ? String(model.repoPath.split(separator: "/").last ?? "")
38 : String(model.directory.split(separator: "/").last ?? ""))
39 .navigationBarTitleDisplayMode(.inline)
40 .task { await model.load() }
41 .refreshable { await model.load() }
42 }
43}
44
45private struct EntryRow: View {
46 let entry: TreeEntry
47
48 var body: some View {
49 HStack {
50 Label {
51 Text(entry.name)
52 .lineLimit(1)
53 } icon: {
54 Image(systemName: icon)
55 .foregroundStyle(entry.isDirectory ? Color.accentColor : .secondary)
56 }
57 Spacer()
58 if let size = entry.size, !entry.isDirectory {
59 Text(size.formatted(.byteCount(style: .file)))
60 .font(.caption)
61 .foregroundStyle(.tertiary)
62 .monospacedDigit()
63 }
64 }
65 }
66
67 private var icon: String {
68 if entry.isDirectory { return "folder.fill" }
69 if entry.isSymlink { return "arrow.turn.down.right" }
70 return "doc"
71 }
72}