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