krz/hutch

an ios client for sourcehut

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

v2.12.0: Hutch/Views/Builds/BuildRowView.swift · raw

  1import SwiftUI
  2
  3struct BuildRowView: View {
  4    let job: JobSummary
  5
  6    var body: some View {
  7        HStack(spacing: 12) {
  8            JobStatusIcon(status: job.status)
  9                .frame(width: 28)
 10
 11            VStack(alignment: .leading, spacing: 4) {
 12                Text(job.displayLabel)
 13                    .font(.subheadline)
 14                    .lineLimit(1)
 15
 16                HStack(spacing: 8) {
 17                    if let image = job.image {
 18                        Text(image)
 19                            .font(.caption)
 20                            .foregroundStyle(.secondary)
 21                    }
 22
 23                    Spacer()
 24
 25                    Text(job.created.relativeDescription)
 26                        .font(.caption)
 27                        .foregroundStyle(.tertiary)
 28                }
 29
 30                if !job.tasks.isEmpty {
 31                    TaskProgressView(tasks: job.tasks)
 32                }
 33            }
 34        }
 35        .padding(.vertical, 2)
 36    }
 37}
 38
 39// MARK: - Job Status Icon
 40
 41struct JobStatusIcon: View {
 42    let status: JobStatus
 43
 44    var body: some View {
 45        Image(systemName: iconName)
 46            .foregroundStyle(color)
 47            .symbolEffect(.pulse, isActive: status == .running)
 48    }
 49
 50    private var iconName: String {
 51        switch status {
 52        case .success:              "checkmark.circle.fill"
 53        case .failed, .timeout:     "xmark.circle.fill"
 54        case .running:              "arrow.trianglehead.2.clockwise.rotate.90"
 55        case .queued:               "clock.fill"
 56        case .pending:              "circle.dashed"
 57        case .cancelled:            "minus.circle.fill"
 58        }
 59    }
 60
 61    private var color: Color {
 62        switch status {
 63        case .success:              .green
 64        case .failed, .timeout:     .red
 65        case .running:              .yellow
 66        case .queued:               .orange
 67        case .pending, .cancelled:  .gray
 68        }
 69    }
 70}
 71
 72// MARK: - Task Progress
 73
 74struct TaskProgressView: View {
 75    let tasks: [JobTaskSummary]
 76
 77    var body: some View {
 78        HStack(spacing: 6) {
 79            ProgressView(value: progress, total: 1.0)
 80                .tint(progressColor)
 81                .frame(maxWidth: 80)
 82
 83            Text("\(completedCount)/\(tasks.count) tasks")
 84                .font(.caption2)
 85                .foregroundStyle(.secondary)
 86        }
 87    }
 88
 89    private var completedCount: Int {
 90        tasks.filter { $0.status == .success }.count
 91    }
 92
 93    private var progress: Double {
 94        tasks.isEmpty ? 0 : Double(completedCount) / Double(tasks.count)
 95    }
 96
 97    private var progressColor: Color {
 98        if tasks.contains(where: { $0.status == .failed }) {
 99            return .red
100        }
101        if completedCount == tasks.count {
102            return .green
103        }
104        return .blue
105    }
106}