krz/hutch
an ios client for sourcehut
clone: git clone https://gitbay.org/krz/hutch.git
v2.10.1: Hutch/Views/Lookup/UserProfileViewModel.swift · raw
1import Foundation
2
3@Observable
4@MainActor
5final class UserProfileViewModel {
6 private(set) var repositories: [RepositorySummary] = []
7 private(set) var trackers: [TrackerSummary] = []
8 private(set) var isLoadingRepositories = false
9 private(set) var isLoadingTrackers = false
10 var repositoriesError: String?
11 var trackersError: String?
12
13 private let client: SRHTClient
14 let ownerUsername: String
15
16 init(ownerUsername: String, client: SRHTClient) {
17 self.ownerUsername = ownerUsername
18 self.client = client
19 }
20
21 func loadRepositories() async {
22 isLoadingRepositories = true
23 repositoriesError = nil
24 defer { isLoadingRepositories = false }
25
26 do {
27 let result = try await client.execute(
28 service: .git,
29 query: Self.repositoriesQuery,
30 variables: ["owner": ownerUsername],
31 responseType: UserRepositoriesResponse.self
32 )
33 repositories = result.user.repositories.results.map { $0.repositorySummary(service: .git) }
34 } catch {
35 repositoriesError = error.userFacingMessage
36 }
37 }
38
39 func loadTrackers() async {
40 isLoadingTrackers = true
41 trackersError = nil
42 defer { isLoadingTrackers = false }
43
44 do {
45 let result = try await client.execute(
46 service: .todo,
47 query: Self.trackersQuery,
48 variables: ["owner": ownerUsername],
49 responseType: UserTrackersResponse.self
50 )
51 trackers = result.user.trackers.results
52 } catch {
53 trackersError = error.userFacingMessage
54 }
55 }
56
57 private static let repositoriesQuery = """
58 query userRepositories($owner: String!) {
59 user(username: $owner) {
60 repositories {
61 results {
62 id
63 rid
64 name
65 description
66 visibility
67 updated
68 owner { canonicalName }
69 HEAD { name target }
70 }
71 cursor
72 }
73 }
74 }
75 """
76
77 private static let trackersQuery = """
78 query userTrackers($owner: String!) {
79 user(username: $owner) {
80 trackers {
81 results {
82 id
83 rid
84 name
85 description
86 visibility
87 updated
88 owner { canonicalName }
89 }
90 cursor
91 }
92 }
93 }
94 """
95
96 private struct UserRepositoriesResponse: Decodable, Sendable {
97 let user: UserRepositoriesContainer
98 }
99
100 private struct UserRepositoriesContainer: Decodable, Sendable {
101 let repositories: RepositoriesPage
102 }
103
104 private struct RepositoriesPage: Decodable, Sendable {
105 let results: [RepositoryPayload]
106 let cursor: String?
107 }
108
109 private struct RepositoryPayload: Decodable, Sendable {
110 let id: Int
111 let rid: String
112 let name: String
113 let description: String?
114 let visibility: Visibility
115 let updated: Date
116 let owner: Entity
117 let head: Reference?
118
119 enum CodingKeys: String, CodingKey {
120 case id, rid, name, description, visibility, updated, owner
121 case head = "HEAD"
122 }
123
124 func repositorySummary(service: SRHTService) -> RepositorySummary {
125 RepositorySummary(
126 id: id,
127 rid: rid,
128 service: service,
129 name: name,
130 description: description,
131 visibility: visibility,
132 updated: updated,
133 owner: owner,
134 head: head
135 )
136 }
137 }
138
139 private struct UserTrackersResponse: Decodable, Sendable {
140 let user: UserTrackersContainer
141 }
142
143 private struct UserTrackersContainer: Decodable, Sendable {
144 let trackers: TrackersPage
145 }
146
147 private struct TrackersPage: Decodable, Sendable {
148 let results: [TrackerSummary]
149 let cursor: String?
150 }
151}