gitbay/Views/Repos/TreeView.swift
82 lines · 2691 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 .toolbar {
41 ToolbarItem(placement: .topBarTrailing) {
42 NavigationLink(value: RepoRoute.log(
43 repo: model.repoPath, ref: model.ref, path: nil
44 )) {
45 Image(systemName: "clock")
46 }
47 .accessibilityIdentifier("tree-history-button")
48 }
49 }
50 .task { await model.load() }
51 .refreshable { await model.load() }
52 }
53}
54
55private struct EntryRow: View {
56 let entry: TreeEntry
57
58 var body: some View {
59 HStack {
60 Label {
61 Text(entry.name)
62 .lineLimit(1)
63 } icon: {
64 Image(systemName: icon)
65 .foregroundStyle(entry.isDirectory ? Color.accentColor : .secondary)
66 }
67 Spacer()
68 if let size = entry.size, !entry.isDirectory {
69 Text(size.formatted(.byteCount(style: .file)))
70 .font(.gbSans(.caption))
71 .foregroundStyle(.tertiary)
72 .monospacedDigit()
73 }
74 }
75 }
76
77 private var icon: String {
78 if entry.isDirectory { return "folder.fill" }
79 if entry.isSymlink { return "arrow.turn.down.right" }
80 return "doc"
81 }
82}