krz/hutch
an ios client for sourcehut
clone: git clone https://gitbay.org/krz/hutch.git
v3.1.5: Hutch/Views/Repositories/ReferencesListView.swift · raw
1import SwiftUI
2
3struct ReferencesListView: View {
4 let viewModel: RepositoryDetailViewModel
5
6 var body: some View {
7 List {
8 if !viewModel.branches.isEmpty {
9 Section("Branches") {
10 ForEach(viewModel.branches, id: \.name) { ref in
11 ReferenceRow(reference: ref, prefix: "refs/heads/")
12 }
13 .themedRow()
14 }
15 }
16
17 if !viewModel.tags.isEmpty {
18 Section("Tags") {
19 ForEach(viewModel.tags, id: \.name) { ref in
20 ReferenceRow(reference: ref, prefix: "refs/tags/")
21 }
22 .themedRow()
23 }
24 }
25 }
26 .themedList()
27 .listStyle(.insetGrouped)
28 .overlay {
29 if viewModel.isLoadingRefs, viewModel.branches.isEmpty, viewModel.tags.isEmpty {
30 SRHTLoadingStateView(message: "Loading references…")
31 } else if let error = viewModel.error, viewModel.branches.isEmpty, viewModel.tags.isEmpty {
32 SRHTErrorStateView(
33 title: "Couldn't Load References",
34 message: error,
35 retryAction: { await viewModel.loadReferences() }
36 )
37 } else if viewModel.branches.isEmpty, viewModel.tags.isEmpty {
38 ContentUnavailableView(
39 "No References",
40 systemImage: "arrow.triangle.branch",
41 description: Text("This repository has no branches or tags.")
42 )
43 }
44 }
45 .task {
46 if viewModel.branches.isEmpty, viewModel.tags.isEmpty {
47 await viewModel.loadReferences()
48 }
49 }
50 .refreshable {
51 await viewModel.loadReferences()
52 }
53 }
54}
55
56private struct ReferenceRow: View {
57 let reference: ReferenceDetail
58 let prefix: String
59
60 var body: some View {
61 HStack {
62 Label {
63 VStack(alignment: .leading, spacing: 2) {
64 Text(shortName)
65 .font(.body.monospaced())
66 if let date = reference.date {
67 Text(date.relativeDescription)
68 .font(.caption)
69 .foregroundStyle(.secondary)
70 }
71 }
72 } icon: {
73 Image(systemName: icon)
74 .foregroundStyle(iconColor)
75 }
76
77 Spacer()
78
79 Text(String((reference.target ?? "").prefix(8)))
80 .font(.caption.monospaced())
81 .foregroundStyle(.secondary)
82 }
83 }
84
85 private var shortName: String {
86 if reference.name.hasPrefix(prefix) {
87 String(reference.name.dropFirst(prefix.count))
88 } else {
89 reference.name
90 }
91 }
92
93 private var icon: String {
94 prefix.contains("tags") ? "tag" : "arrow.triangle.branch"
95 }
96
97 private var iconColor: Color {
98 prefix.contains("tags") ? .orange : .blue
99 }
100}