gitbay/Views/Repos/LogView.swift
92 lines · 2938 bytes
1import SwiftUI
2
3struct LogView: View {
4
5 @State private var model: LogViewModel
6
7 init(client: GitbayClient, repo: String) {
8 _model = State(initialValue: LogViewModel(client: client, repoPath: repo))
9 }
10
11 var body: some View {
12 List {
13 ForEach(model.state.value ?? []) { commit in
14 CommitRow(commit: commit)
15 }
16 }
17 .overlay { LoadStateOverlay(state: model.state) }
18 .navigationTitle("History")
19 .navigationBarTitleDisplayMode(.inline)
20 .task { await model.load() }
21 .refreshable { await model.load() }
22 }
23}
24
25struct CommitRow: View {
26 let commit: Commit
27
28 var body: some View {
29 VStack(alignment: .leading, spacing: 4) {
30 Text(commit.subject)
31 .font(.subheadline.weight(.medium))
32 .lineLimit(2)
33 HStack(spacing: 6) {
34 Text(commit.shortSHA)
35 .font(.caption.monospaced())
36 .foregroundStyle(.secondary)
37 SignatureBadge(signature: commit.signature)
38 Spacer()
39 Text(commit.authorName)
40 .font(.caption)
41 .foregroundStyle(.secondary)
42 .lineLimit(1)
43 Text(commit.date, format: .relative(presentation: .named))
44 .font(.caption)
45 .foregroundStyle(.tertiary)
46 }
47 }
48 .padding(.vertical, 2)
49 }
50}
51
52/// The server's signature verdict, worn as a small badge. The states come
53/// from the registry; the app only chooses glyph and colour.
54struct SignatureBadge: View {
55 let signature: Commit.Signature
56
57 var body: some View {
58 if let (icon, color, text) = presentation {
59 Label {
60 Text(text)
61 } icon: {
62 Image(systemName: icon)
63 }
64 .font(.caption2)
65 .foregroundStyle(color)
66 .labelStyle(.titleAndIcon)
67 }
68 }
69
70 /// Unsigned commits are the norm — no badge at all. Everything else
71 /// says what the server concluded.
72 private var presentation: (String, Color, String)? {
73 switch signature.state {
74 case .unsigned:
75 nil
76 case .verified:
77 ("checkmark.seal.fill", .green, signature.signer ?? "verified")
78 case .signedUnknownKey:
79 ("questionmark.diamond", .secondary, "unknown key")
80 case .signedEmailMismatch:
81 ("exclamationmark.triangle", .orange, "email mismatch")
82 case .signedKeyExpired:
83 ("clock.badge.exclamationmark", .orange, "key expired")
84 case .signedKeyRevoked:
85 ("xmark.seal", .red, "key revoked")
86 case .badSignature:
87 ("xmark.seal.fill", .red, "bad signature")
88 case .unrecognized(let state):
89 ("questionmark.circle", .secondary, state)
90 }
91 }
92}