summaryrefslogtreecommitdiff
path: root/Hutch/Views/Repositories/CommitDetailViewModel.swift
diff options
context:
space:
mode:
Diffstat (limited to 'Hutch/Views/Repositories/CommitDetailViewModel.swift')
-rw-r--r--Hutch/Views/Repositories/CommitDetailViewModel.swift102
1 files changed, 102 insertions, 0 deletions
diff --git a/Hutch/Views/Repositories/CommitDetailViewModel.swift b/Hutch/Views/Repositories/CommitDetailViewModel.swift
new file mode 100644
index 0000000..5a8a23d
--- /dev/null
+++ b/Hutch/Views/Repositories/CommitDetailViewModel.swift
@@ -0,0 +1,102 @@
+import Foundation
+
+// MARK: - Response types (file-private to avoid @MainActor Decodable issues)
+
+private struct CommitResponse: Decodable, Sendable {
+ let repository: CommitRepository?
+}
+
+private struct CommitRepository: Decodable, Sendable {
+ // swiftlint:disable:next identifier_name
+ let revparse_single: CommitDetail
+}
+
+// MARK: - View Model
+
+@Observable
+@MainActor
+final class CommitDetailViewModel {
+
+ let repositoryRid: String
+ let service: SRHTService
+ private let client: SRHTClient
+
+ private(set) var commit: CommitDetail?
+ private(set) var isLoading = false
+ var error: String?
+
+ init(repositoryRid: String, service: SRHTService, commitId: String, client: SRHTClient) {
+ self.repositoryRid = repositoryRid
+ self.service = service
+ self.commitId = commitId
+ self.client = client
+ }
+
+ private let commitId: String
+
+ // MARK: - Query
+
+ private static let query = """
+ query commit($rid: ID!, $id: String!) {
+ repository(rid: $rid) {
+ revparse_single(revspec: $id) {
+ id
+ shortId
+ author { name email time }
+ committer { name email time }
+ message
+ diff
+ trailers { name value }
+ parents { id shortId author { name } }
+ tree {
+ entries {
+ results { id name mode object { type id shortId } }
+ cursor
+ }
+ }
+ }
+ }
+ }
+ """
+
+ func loadCommit() async {
+ guard !isLoading else { return }
+ isLoading = true
+ error = nil
+
+ do {
+ let result = try await executeWithRetry()
+ commit = result.repository?.revparse_single
+ } catch {
+ self.error = error.localizedDescription
+ }
+
+ isLoading = false
+ }
+
+ /// Execute the commit query, retrying once after a 1-second delay on 502/503.
+ private func executeWithRetry() async throws -> CommitResponse {
+ do {
+ return try await client.execute(
+ service: service,
+ query: Self.query,
+ variables: [
+ "rid": repositoryRid,
+ "id": commitId
+ ],
+ responseType: CommitResponse.self
+ )
+ } catch let SRHTError.httpError(code) where code == 502 || code == 503 {
+ try await Task.sleep(for: .seconds(1))
+ return try await client.execute(
+ service: service,
+ query: Self.query,
+ variables: [
+ "rid": repositoryRid,
+ "id": commitId
+ ],
+ responseType: CommitResponse.self
+ )
+ }
+ }
+}