import SwiftUI struct LogView: View { @State private var model: LogViewModel init(client: GitbayClient, repo: String) { _model = State(initialValue: LogViewModel(client: client, repoPath: repo)) } var body: some View { List { ForEach(model.state.value ?? []) { commit in CommitRow(commit: commit) } } .overlay { LoadStateOverlay(state: model.state) } .navigationTitle("History") .navigationBarTitleDisplayMode(.inline) .task { await model.load() } .refreshable { await model.load() } } } struct CommitRow: View { let commit: Commit var body: some View { VStack(alignment: .leading, spacing: 4) { Text(commit.subject) .font(.subheadline.weight(.medium)) .lineLimit(2) HStack(spacing: 6) { Text(commit.shortSHA) .font(.caption.monospaced()) .foregroundStyle(.secondary) SignatureBadge(signature: commit.signature) Spacer() Text(commit.authorName) .font(.caption) .foregroundStyle(.secondary) .lineLimit(1) Text(commit.date, format: .relative(presentation: .named)) .font(.caption) .foregroundStyle(.tertiary) } } .padding(.vertical, 2) } } /// The server's signature verdict, worn as a small badge. The states come /// from the registry; the app only chooses glyph and colour. struct SignatureBadge: View { let signature: Commit.Signature var body: some View { if let (icon, color, text) = presentation { Label { Text(text) } icon: { Image(systemName: icon) } .font(.caption2) .foregroundStyle(color) .labelStyle(.titleAndIcon) } } /// Unsigned commits are the norm — no badge at all. Everything else /// says what the server concluded. private var presentation: (String, Color, String)? { switch signature.state { case .unsigned: nil case .verified: ("checkmark.seal.fill", .green, signature.signer ?? "verified") case .signedUnknownKey: ("questionmark.diamond", .secondary, "unknown key") case .signedEmailMismatch: ("exclamationmark.triangle", .orange, "email mismatch") case .signedKeyExpired: ("clock.badge.exclamationmark", .orange, "key expired") case .signedKeyRevoked: ("xmark.seal", .red, "key revoked") case .badSignature: ("xmark.seal.fill", .red, "bad signature") case .unrecognized(let state): ("questionmark.circle", .secondary, state) } } }