krz/hutch

an ios client for sourcehut

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

v2.7.0: Hutch/Views/Repositories/RepositoryRowView.swift · raw

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