krz/hutch

an ios client for sourcehut

clone: git clone https://gitbay.org/krz/hutch.git

v3.1.6: 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}