krz/hutch
an ios client for sourcehut
clone: git clone https://gitbay.org/krz/hutch.git
v3.11.0: Hutch/Views/Repositories/RepositoryRowView.swift · raw
1import SwiftUI
2
3struct RepositoryRowView: View {
4 @Environment(AppState.self) private var appState
5 @Environment(\.openURL) private var openURL
6
7 let repository: RepositorySummary
8 let buildStatus: RepositoryBuildStatus
9
10 var body: some View {
11 VStack(alignment: .leading, spacing: 4) {
12 HStack(alignment: .firstTextBaseline) {
13 Text(repository.name)
14 .font(.headline)
15
16 Spacer()
17
18 RepositoryForgeBadge(service: repository.service)
19
20 VisibilityBadge(visibility: repository.visibility)
21 }
22
23 Text(repository.owner.canonicalName)
24 .font(.subheadline)
25 .foregroundStyle(.secondary)
26
27 if let description = repository.description,
28 !description.isEmpty {
29 Text(description)
30 .font(.subheadline)
31 .foregroundStyle(.secondary)
32 .lineLimit(2)
33 }
34
35 HStack(spacing: 12) {
36 if let defaultBranchName = repository.defaultBranchName {
37 Label(defaultBranchName, systemImage: "arrow.triangle.branch")
38 .font(.caption)
39 .foregroundStyle(.secondary)
40 }
41
42 Spacer()
43
44 HStack(spacing: 6) {
45 ZStack {
46 if buildStatus != .none {
47 RepositoryBuildStatusIndicator(status: buildStatus)
48 }
49 }
50 .frame(width: 8, height: 8)
51 .accessibilityHidden(buildStatus == .none)
52
53 Text(repository.updated.relativeDescription)
54 .font(.caption)
55 .foregroundStyle(.tertiary)
56 }
57 }
58 }
59 .padding(.vertical, 2)
60 .contextMenu {
61 if let url = SRHTWebURL.repository(repository) {
62 Button {
63 openURL(url)
64 } label: {
65 Label("Open in Browser", systemImage: "safari")
66 }
67 }
68
69 Button {
70 if let url = SRHTWebURL.repository(repository)?.absoluteString {
71 appState.copyToPasteboard(url, label: "repository URL")
72 }
73 } label: {
74 Label("Copy URL", systemImage: "doc.on.doc")
75 }
76
77 Button {
78 if let url = SRHTWebURL.httpsCloneURL(repository) {
79 appState.copyToPasteboard(url, label: "HTTPS clone URL")
80 }
81 } label: {
82 Label("Copy HTTPS URL", systemImage: "doc.on.doc")
83 }
84
85 Button {
86 appState.copyToPasteboard(SRHTWebURL.sshCloneURL(repository), label: "SSH clone URL")
87 } label: {
88 Label("Copy SSH URL", systemImage: "terminal")
89 }
90
91 Button {
92 appState.copyToPasteboard(repository.rid, label: "repository RID")
93 } label: {
94 Label("Copy RID", systemImage: "number")
95 }
96 }
97 }
98}
99
100private struct RepositoryBuildStatusIndicator: View {
101 let status: RepositoryBuildStatus
102
103 var body: some View {
104 Circle()
105 .fill(color)
106 .frame(width: 8, height: 8)
107 .overlay {
108 Circle()
109 .strokeBorder(.primary.opacity(0.08))
110 }
111 .accessibilityLabel(accessibilityLabel)
112 }
113
114 private var color: Color {
115 switch status {
116 case .success:
117 .green
118 case .failed:
119 .red
120 case .running:
121 .orange
122 case .none:
123 .clear
124 }
125 }
126
127 private var accessibilityLabel: String {
128 switch status {
129 case .success:
130 "Latest build succeeded"
131 case .failed:
132 "Latest build failed"
133 case .running:
134 "Latest build is running"
135 case .none:
136 "No recent builds"
137 }
138 }
139}
140
141// MARK: - VisibilityBadge
142
143struct VisibilityBadge: View {
144 let visibility: Visibility
145
146 var body: some View {
147 Text(label)
148 .font(.caption2.weight(.medium))
149 .padding(.horizontal, 6)
150 .padding(.vertical, 2)
151 .background(color.opacity(0.15), in: Capsule())
152 .foregroundStyle(color)
153 }
154
155 private var label: String {
156 switch visibility {
157 case .publicVisibility: "PUBLIC"
158 case .unlisted: "UNLISTED"
159 case .privateVisibility: "PRIVATE"
160 }
161 }
162
163 private var color: Color {
164 switch visibility {
165 case .publicVisibility: .green
166 case .unlisted: .orange
167 case .privateVisibility: .red
168 }
169 }
170}