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