summaryrefslogtreecommitdiff
path: root/Hutch/Networking
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-04-12 22:06:16 -0500
committerChristian Cleberg <[email protected]>2026-04-12 22:06:16 -0500
commit86bcd9452d3107f68aca085e03e69b02667c0dbb (patch)
tree8b0adece5cb0f8ffb178eaab2cd12d025c9ebf0a /Hutch/Networking
parent31cbca674957eea7ae2529012e20a9d95158e561 (diff)
downloadhutch-86bcd9452d3107f68aca085e03e69b02667c0dbb.tar.gz
hutch-86bcd9452d3107f68aca085e03e69b02667c0dbb.tar.bz2
hutch-86bcd9452d3107f68aca085e03e69b02667c0dbb.zip
feat: polish read-only project views and home integration
Refs: https://todo.sr.ht/~ccleberg/hutch/36 Refs: https://todo.sr.ht/~ccleberg/hutch/37 Refs: https://todo.sr.ht/~ccleberg/hutch/38
Diffstat (limited to 'Hutch/Networking')
-rw-r--r--Hutch/Networking/ProjectService.swift118
1 files changed, 118 insertions, 0 deletions
diff --git a/Hutch/Networking/ProjectService.swift b/Hutch/Networking/ProjectService.swift
index 5a0b466..93ce71f 100644
--- a/Hutch/Networking/ProjectService.swift
+++ b/Hutch/Networking/ProjectService.swift
@@ -11,6 +11,17 @@ private struct ProjectPageUser: Decodable, Sendable {
private struct ProjectPage: Decodable, Sendable {
let results: [ProjectSummaryPayload]
let cursor: String?
+
+ private enum CodingKeys: String, CodingKey {
+ case results
+ case cursor
+ }
+
+ init(from decoder: any Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ results = try container.decodeIfPresent([ProjectSummaryPayload].self, forKey: .results) ?? []
+ cursor = try container.decodeIfPresent(String.self, forKey: .cursor)
+ }
}
private struct ProjectSummaryPayload: Decodable, Sendable {
@@ -21,6 +32,27 @@ private struct ProjectSummaryPayload: Decodable, Sendable {
let visibility: Visibility
let tags: [String]
let updated: Date
+
+ private enum CodingKeys: String, CodingKey {
+ case rid
+ case name
+ case description
+ case website
+ case visibility
+ case tags
+ case updated
+ }
+
+ init(from decoder: any Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ rid = try container.decode(String.self, forKey: .rid)
+ name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
+ description = try container.decodeIfPresent(String.self, forKey: .description)
+ website = try container.decodeIfPresent(String.self, forKey: .website)
+ visibility = try container.decodeIfPresent(Visibility.self, forKey: .visibility) ?? .public
+ tags = try container.decodeIfPresent([String].self, forKey: .tags) ?? []
+ updated = try container.decodeIfPresent(Date.self, forKey: .updated) ?? .distantPast
+ }
}
private struct ProjectDetailResponse: Decodable, Sendable {
@@ -38,11 +70,40 @@ private struct ProjectDetailPayload: Decodable, Sendable {
let mailingLists: ProjectMailingListPage
let sources: ProjectSourcePage
let trackers: ProjectTrackerPage
+
+ private enum CodingKeys: String, CodingKey {
+ case rid
+ case name
+ case description
+ case website
+ case visibility
+ case tags
+ case updated
+ case mailingLists
+ case sources
+ case trackers
+ }
+
+ init(from decoder: any Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ rid = try container.decode(String.self, forKey: .rid)
+ name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
+ description = try container.decodeIfPresent(String.self, forKey: .description)
+ website = try container.decodeIfPresent(String.self, forKey: .website)
+ visibility = try container.decodeIfPresent(Visibility.self, forKey: .visibility) ?? .public
+ tags = try container.decodeIfPresent([String].self, forKey: .tags) ?? []
+ updated = try container.decodeIfPresent(Date.self, forKey: .updated) ?? .distantPast
+ mailingLists = try container.decodeIfPresent(ProjectMailingListPage.self, forKey: .mailingLists) ?? .empty
+ sources = try container.decodeIfPresent(ProjectSourcePage.self, forKey: .sources) ?? .empty
+ trackers = try container.decodeIfPresent(ProjectTrackerPage.self, forKey: .trackers) ?? .empty
+ }
}
private struct ProjectMailingListPage: Decodable, Sendable {
let results: [ProjectMailingListPayload]
let cursor: String?
+
+ static let empty = ProjectMailingListPage(results: [], cursor: nil)
}
private struct ProjectMailingListPayload: Decodable, Sendable {
@@ -51,11 +112,30 @@ private struct ProjectMailingListPayload: Decodable, Sendable {
let description: String?
let visibility: Visibility
let owner: Entity
+
+ private enum CodingKeys: String, CodingKey {
+ case rid
+ case name
+ case description
+ case visibility
+ case owner
+ }
+
+ init(from decoder: any Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ rid = try container.decode(String.self, forKey: .rid)
+ name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
+ description = try container.decodeIfPresent(String.self, forKey: .description)
+ visibility = try container.decodeIfPresent(Visibility.self, forKey: .visibility) ?? .public
+ owner = try container.decodeIfPresent(Entity.self, forKey: .owner) ?? Entity(canonicalName: "~unknown")
+ }
}
private struct ProjectSourcePage: Decodable, Sendable {
let results: [ProjectSourcePayload]
let cursor: String?
+
+ static let empty = ProjectSourcePage(results: [], cursor: nil)
}
private struct ProjectSourcePayload: Decodable, Sendable {
@@ -65,11 +145,32 @@ private struct ProjectSourcePayload: Decodable, Sendable {
let visibility: Visibility
let owner: Entity
let repoType: Project.SourceRepo.RepoType
+
+ private enum CodingKeys: String, CodingKey {
+ case rid
+ case name
+ case description
+ case visibility
+ case owner
+ case repoType
+ }
+
+ init(from decoder: any Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ rid = try container.decode(String.self, forKey: .rid)
+ name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
+ description = try container.decodeIfPresent(String.self, forKey: .description)
+ visibility = try container.decodeIfPresent(Visibility.self, forKey: .visibility) ?? .public
+ owner = try container.decodeIfPresent(Entity.self, forKey: .owner) ?? Entity(canonicalName: "~unknown")
+ repoType = try container.decodeIfPresent(Project.SourceRepo.RepoType.self, forKey: .repoType) ?? .git
+ }
}
private struct ProjectTrackerPage: Decodable, Sendable {
let results: [ProjectTrackerPayload]
let cursor: String?
+
+ static let empty = ProjectTrackerPage(results: [], cursor: nil)
}
private struct ProjectTrackerPayload: Decodable, Sendable {
@@ -78,6 +179,23 @@ private struct ProjectTrackerPayload: Decodable, Sendable {
let description: String?
let visibility: Visibility
let owner: Entity
+
+ private enum CodingKeys: String, CodingKey {
+ case rid
+ case name
+ case description
+ case visibility
+ case owner
+ }
+
+ init(from decoder: any Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ rid = try container.decode(String.self, forKey: .rid)
+ name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
+ description = try container.decodeIfPresent(String.self, forKey: .description)
+ visibility = try container.decodeIfPresent(Visibility.self, forKey: .visibility) ?? .public
+ owner = try container.decodeIfPresent(Entity.self, forKey: .owner) ?? Entity(canonicalName: "~unknown")
+ }
}
struct ProjectService: Sendable {