krz/hutch

an ios client for sourcehut

clone: git clone https://gitbay.org/krz/hutch.git

v3.0.4: 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                if buildStatus != .none {
 21                    RepositoryBuildStatusIndicator(status: buildStatus)
 22                }
 23                VisibilityBadge(visibility: repository.visibility)
 24            }
 25
 26            Text(repository.owner.canonicalName)
 27                .font(.subheadline)
 28                .foregroundStyle(.secondary)
 29
 30            if let description = repository.description,
 31               !description.isEmpty {
 32                Text(description)
 33                    .font(.subheadline)
 34                    .foregroundStyle(.secondary)
 35                    .lineLimit(2)
 36            }
 37
 38            HStack(spacing: 12) {
 39                if let defaultBranchName = repository.defaultBranchName {
 40                    Label(defaultBranchName, systemImage: "arrow.triangle.branch")
 41                        .font(.caption)
 42                        .foregroundStyle(.secondary)
 43                }
 44
 45                Spacer()
 46
 47                Text(repository.updated.relativeDescription)
 48                    .font(.caption)
 49                    .foregroundStyle(.tertiary)
 50            }
 51        }
 52        .padding(.vertical, 2)
 53        .contextMenu {
 54            if let url = SRHTWebURL.repository(repository) {
 55                Button {
 56                    openURL(url)
 57                } label: {
 58                    Label("Open in Browser", systemImage: "safari")
 59                }
 60            }
 61
 62            Button {
 63                if let url = SRHTWebURL.repository(repository)?.absoluteString {
 64                    appState.copyToPasteboard(url, label: "repository URL")
 65                }
 66            } label: {
 67                Label("Copy URL", systemImage: "doc.on.doc")
 68            }
 69
 70            Button {
 71                if let url = SRHTWebURL.httpsCloneURL(repository) {
 72                    appState.copyToPasteboard(url, label: "HTTPS clone URL")
 73                }
 74            } label: {
 75                Label("Copy HTTPS URL", systemImage: "doc.on.doc")
 76            }
 77
 78            Button {
 79                appState.copyToPasteboard(SRHTWebURL.sshCloneURL(repository), label: "SSH clone URL")
 80            } label: {
 81                Label("Copy SSH URL", systemImage: "terminal")
 82            }
 83
 84            Button {
 85                appState.copyToPasteboard(repository.rid, label: "repository RID")
 86            } label: {
 87                Label("Copy RID", systemImage: "number")
 88            }
 89        }
 90    }
 91}
 92
 93private struct RepositoryBuildStatusIndicator: View {
 94    let status: RepositoryBuildStatus
 95
 96    var body: some View {
 97        Circle()
 98            .fill(color)
 99            .frame(width: 8, height: 8)
100            .overlay {
101                Circle()
102                    .strokeBorder(.primary.opacity(0.08))
103            }
104            .accessibilityLabel(accessibilityLabel)
105    }
106
107    private var color: Color {
108        switch status {
109        case .success:
110            .green
111        case .failed:
112            .red
113        case .running:
114            .orange
115        case .none:
116            .clear
117        }
118    }
119
120    private var accessibilityLabel: String {
121        switch status {
122        case .success:
123            "Latest build succeeded"
124        case .failed:
125            "Latest build failed"
126        case .running:
127            "Latest build is running"
128        case .none:
129            "No recent builds"
130        }
131    }
132}
133
134// MARK: - VisibilityBadge
135
136struct VisibilityBadge: View {
137    let visibility: Visibility
138
139    var body: some View {
140        Text(label)
141            .font(.caption2.weight(.medium))
142            .padding(.horizontal, 6)
143            .padding(.vertical, 2)
144            .background(color.opacity(0.15), in: Capsule())
145            .foregroundStyle(color)
146    }
147
148    private var label: String {
149        switch visibility {
150        case .public:   "PUBLIC"
151        case .unlisted: "UNLISTED"
152        case .private:  "PRIVATE"
153        }
154    }
155
156    private var color: Color {
157        switch visibility {
158        case .public:   .green
159        case .unlisted: .orange
160        case .private:  .red
161        }
162    }
163}