krz/hutch
an ios client for sourcehut
clone: git clone https://gitbay.org/krz/hutch.git
v3.2.1: Hutch/Views/Repositories/CommitDetailViewModel.swift · raw
1import Foundation
2
3// MARK: - Response types (file-private to avoid @MainActor Decodable issues)
4
5private struct CommitResponse: Decodable, Sendable {
6 let repository: CommitRepository?
7}
8
9private struct CommitRepository: Decodable, Sendable {
10 let revparseSingle: CommitDetail
11
12 enum CodingKeys: String, CodingKey {
13 case revparseSingle = "revparse_single"
14 }
15}
16
17// MARK: - View Model
18
19@Observable
20@MainActor
21final class CommitDetailViewModel {
22
23 let repositoryRid: String
24 let service: SRHTService
25 private let client: SRHTClient
26
27 private(set) var commit: CommitDetail?
28 private(set) var isLoading = false
29 var error: String?
30
31 init(repositoryRid: String, service: SRHTService, commitId: String, client: SRHTClient) {
32 self.repositoryRid = repositoryRid
33 self.service = service
34 self.commitId = commitId
35 self.client = client
36 }
37
38 private let commitId: String
39
40 // MARK: - Query
41
42 private static let query = """
43 query commit($rid: ID!, $id: String!) {
44 repository(rid: $rid) {
45 revparse_single(revspec: $id) {
46 id
47 shortId
48 author { name email time }
49 committer { name email time }
50 message
51 diff
52 trailers { name value }
53 parents { id shortId author { name } }
54 tree {
55 entries {
56 results { id name mode object { type id shortId } }
57 cursor
58 }
59 }
60 }
61 }
62 }
63 """
64
65 func loadCommit() async {
66 guard !isLoading else { return }
67 isLoading = true
68 error = nil
69
70 do {
71 let result = try await executeWithRetry()
72 commit = result.repository?.revparseSingle
73 } catch {
74 self.error = error.userFacingMessage
75 }
76
77 isLoading = false
78 }
79
80 /// Execute the commit query, retrying once after a 1-second delay on 502/503.
81 private func executeWithRetry() async throws -> CommitResponse {
82 do {
83 return try await client.execute(
84 service: service,
85 query: Self.query,
86 variables: [
87 "rid": repositoryRid,
88 "id": commitId
89 ],
90 responseType: CommitResponse.self
91 )
92 } catch let SRHTError.httpError(code) where code == 502 || code == 503 {
93 try await Task.sleep(for: .seconds(1))
94 return try await client.execute(
95 service: service,
96 query: Self.query,
97 variables: [
98 "rid": repositoryRid,
99 "id": commitId
100 ],
101 responseType: CommitResponse.self
102 )
103 }
104 }
105}