summaryrefslogtreecommitdiff
path: root/Hutch/Views/Lookup/UserProfileViewModel.swift
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-04-02 00:17:30 -0500
committerChristian Cleberg <[email protected]>2026-04-02 00:17:30 -0500
commit7f79cadf99ecf014834855e2e4a5c7d60dd709ac (patch)
treee1435c08b416390064e0d2772d6a7703a1737db1 /Hutch/Views/Lookup/UserProfileViewModel.swift
parentcd19a6fb24ebd5550068a28c0daefd07697bebda (diff)
downloadhutch-2.9.0.tar.gz
hutch-2.9.0.tar.bz2
hutch-2.9.0.zip
feat: browse user repositories and trackers from profilev2.9.0
Diffstat (limited to 'Hutch/Views/Lookup/UserProfileViewModel.swift')
-rw-r--r--Hutch/Views/Lookup/UserProfileViewModel.swift151
1 files changed, 151 insertions, 0 deletions
diff --git a/Hutch/Views/Lookup/UserProfileViewModel.swift b/Hutch/Views/Lookup/UserProfileViewModel.swift
new file mode 100644
index 0000000..bec79fb
--- /dev/null
+++ b/Hutch/Views/Lookup/UserProfileViewModel.swift
@@ -0,0 +1,151 @@
+import Foundation
+
+@Observable
+@MainActor
+final class UserProfileViewModel {
+ private(set) var repositories: [RepositorySummary] = []
+ private(set) var trackers: [TrackerSummary] = []
+ private(set) var isLoadingRepositories = false
+ private(set) var isLoadingTrackers = false
+ var repositoriesError: String?
+ var trackersError: String?
+
+ private let client: SRHTClient
+ let ownerUsername: String
+
+ init(ownerUsername: String, client: SRHTClient) {
+ self.ownerUsername = ownerUsername
+ self.client = client
+ }
+
+ func loadRepositories() async {
+ isLoadingRepositories = true
+ repositoriesError = nil
+ defer { isLoadingRepositories = false }
+
+ do {
+ let result = try await client.execute(
+ service: .git,
+ query: Self.repositoriesQuery,
+ variables: ["owner": ownerUsername],
+ responseType: UserRepositoriesResponse.self
+ )
+ repositories = result.user.repositories.results.map { $0.repositorySummary(service: .git) }
+ } catch {
+ repositoriesError = error.userFacingMessage
+ }
+ }
+
+ func loadTrackers() async {
+ isLoadingTrackers = true
+ trackersError = nil
+ defer { isLoadingTrackers = false }
+
+ do {
+ let result = try await client.execute(
+ service: .todo,
+ query: Self.trackersQuery,
+ variables: ["owner": ownerUsername],
+ responseType: UserTrackersResponse.self
+ )
+ trackers = result.user.trackers.results
+ } catch {
+ trackersError = error.userFacingMessage
+ }
+ }
+
+ private static let repositoriesQuery = """
+ query userRepositories($owner: String!) {
+ user(username: $owner) {
+ repositories {
+ results {
+ id
+ rid
+ name
+ description
+ visibility
+ updated
+ owner { canonicalName }
+ HEAD { name target }
+ }
+ cursor
+ }
+ }
+ }
+ """
+
+ private static let trackersQuery = """
+ query userTrackers($owner: String!) {
+ user(username: $owner) {
+ trackers {
+ results {
+ id
+ rid
+ name
+ description
+ visibility
+ updated
+ owner { canonicalName }
+ }
+ cursor
+ }
+ }
+ }
+ """
+
+ private struct UserRepositoriesResponse: Decodable, Sendable {
+ let user: UserRepositoriesContainer
+ }
+
+ private struct UserRepositoriesContainer: Decodable, Sendable {
+ let repositories: RepositoriesPage
+ }
+
+ private struct RepositoriesPage: Decodable, Sendable {
+ let results: [RepositoryPayload]
+ let cursor: String?
+ }
+
+ private struct RepositoryPayload: Decodable, Sendable {
+ let id: Int
+ let rid: String
+ let name: String
+ let description: String?
+ let visibility: Visibility
+ let updated: Date
+ let owner: Entity
+ let head: Reference?
+
+ enum CodingKeys: String, CodingKey {
+ case id, rid, name, description, visibility, updated, owner
+ case head = "HEAD"
+ }
+
+ func repositorySummary(service: SRHTService) -> RepositorySummary {
+ RepositorySummary(
+ id: id,
+ rid: rid,
+ service: service,
+ name: name,
+ description: description,
+ visibility: visibility,
+ updated: updated,
+ owner: owner,
+ head: head
+ )
+ }
+ }
+
+ private struct UserTrackersResponse: Decodable, Sendable {
+ let user: UserTrackersContainer
+ }
+
+ private struct UserTrackersContainer: Decodable, Sendable {
+ let trackers: TrackersPage
+ }
+
+ private struct TrackersPage: Decodable, Sendable {
+ let results: [TrackerSummary]
+ let cursor: String?
+ }
+}