Commit b0ba9d0cc4

b0ba9d0cc4bb20317996cf6e577cd3821470e668

parent: 303f741e56

Unsigned

cmc <hello@cleberg.net> · 2026-04-14 01:07 UTC

fix: sonarqube code smell fixes

Layout: unified · split

Hutch.xcodeproj/project.pbxproj +8 −8
@@ -515,7 +515,7 @@
515515 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
516516 CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements;
517517 CODE_SIGN_STYLE = Automatic;
518 CURRENT_PROJECT_VERSION = 71;
518 CURRENT_PROJECT_VERSION = 72;
519519 DEVELOPMENT_TEAM = ZCNAX3VL9D;
520520 ENABLE_PREVIEWS = YES;
521521 GENERATE_INFOPLIST_FILE = YES;
@@ -532,7 +532,7 @@
532532 "$(inherited)",
533533 "@executable_path/Frameworks",
534534 );
535 MARKETING_VERSION = 3.1.3;
535 MARKETING_VERSION = 3.1.4;
536536 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch;
537537 PRODUCT_NAME = "$(TARGET_NAME)";
538538 STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -552,7 +552,7 @@
552552 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
553553 CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements;
554554 CODE_SIGN_STYLE = Automatic;
555 CURRENT_PROJECT_VERSION = 71;
555 CURRENT_PROJECT_VERSION = 72;
556556 DEVELOPMENT_TEAM = ZCNAX3VL9D;
557557 ENABLE_PREVIEWS = YES;
558558 GENERATE_INFOPLIST_FILE = YES;
@@ -569,7 +569,7 @@
569569 "$(inherited)",
570570 "@executable_path/Frameworks",
571571 );
572 MARKETING_VERSION = 3.1.3;
572 MARKETING_VERSION = 3.1.4;
573573 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch;
574574 PRODUCT_NAME = "$(TARGET_NAME)";
575575 STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -632,7 +632,7 @@
632632 APPLICATION_EXTENSION_API_ONLY = YES;
633633 CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements;
634634 CODE_SIGN_STYLE = Automatic;
635 CURRENT_PROJECT_VERSION = 71;
635 CURRENT_PROJECT_VERSION = 72;
636636 DEVELOPMENT_TEAM = ZCNAX3VL9D;
637637 GENERATE_INFOPLIST_FILE = NO;
638638 INFOPLIST_FILE = HutchWidgetExtension/Info.plist;
@@ -642,7 +642,7 @@
642642 "@executable_path/Frameworks",
643643 "@executable_path/../../Frameworks",
644644 );
645 MARKETING_VERSION = 3.1.3;
645 MARKETING_VERSION = 3.1.4;
646646 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension;
647647 PRODUCT_NAME = "$(TARGET_NAME)";
648648 SKIP_INSTALL = YES;
@@ -661,7 +661,7 @@
661661 APPLICATION_EXTENSION_API_ONLY = YES;
662662 CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements;
663663 CODE_SIGN_STYLE = Automatic;
664 CURRENT_PROJECT_VERSION = 71;
664 CURRENT_PROJECT_VERSION = 72;
665665 DEVELOPMENT_TEAM = ZCNAX3VL9D;
666666 GENERATE_INFOPLIST_FILE = NO;
667667 INFOPLIST_FILE = HutchWidgetExtension/Info.plist;
@@ -671,7 +671,7 @@
671671 "@executable_path/Frameworks",
672672 "@executable_path/../../Frameworks",
673673 );
674 MARKETING_VERSION = 3.1.3;
674 MARKETING_VERSION = 3.1.4;
675675 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension;
676676 PRODUCT_NAME = "$(TARGET_NAME)";
677677 SKIP_INSTALL = YES;
Hutch/Models/Git.swift +2 −2
@@ -4,9 +4,9 @@ import Foundation
44
55/// Repository visibility level.
66enum Visibility: String, Codable, Sendable {
7 case `public` = "PUBLIC"
7 case publicVisibility = "PUBLIC"
88 case unlisted = "UNLISTED"
9 case `private` = "PRIVATE"
9 case privateVisibility = "PRIVATE"
1010}
1111
1212/// Repository access mode.
Hutch/Models/Project.swift +2 −2
@@ -230,11 +230,11 @@ extension String {
230230extension Visibility {
231231 var displayName: String {
232232 switch self {
233 case .public:
233 case .publicVisibility:
234234 "Public"
235235 case .unlisted:
236236 "Unlisted"
237 case .private:
237 case .privateVisibility:
238238 "Private"
239239 }
240240 }
Hutch/Models/RepositorySummary.swift +34 −29
@@ -20,26 +20,29 @@ struct RepositorySummary: Codable, Sendable, Identifiable, Hashable {
2020 case head = "HEAD"
2121 }
2222
23 init(
24 id: Int,
25 rid: String,
26 service: SRHTService,
27 name: String,
28 description: String?,
29 visibility: Visibility,
30 updated: Date,
31 owner: Entity,
32 head: Reference?
33 ) {
34 self.id = id
35 self.rid = rid
36 self.service = service
37 self.name = name
38 self.description = description
39 self.visibility = visibility
40 self.updated = updated
41 self.owner = owner
42 self.head = head
23 /// Grouped initializer fields (single parameter keeps APIs explicit without exceeding parameter-count limits).
24 struct Fields: Sendable, Hashable {
25 let id: Int
26 let rid: String
27 let service: SRHTService
28 let name: String
29 let description: String?
30 let visibility: Visibility
31 let updated: Date
32 let owner: Entity
33 let head: Reference?
34 }
35
36 init(fields: Fields) {
37 id = fields.id
38 rid = fields.rid
39 service = fields.service
40 name = fields.name
41 description = fields.description
42 visibility = fields.visibility
43 updated = fields.updated
44 owner = fields.owner
45 head = fields.head
4346 }
4447
4548 init(from decoder: any Decoder) throws {
@@ -69,15 +72,17 @@ extension RepositorySummary {
6972 head: Reference? = nil
7073 ) -> RepositorySummary {
7174 RepositorySummary(
72 id: id,
73 rid: rid,
74 service: service,
75 name: name ?? self.name,
76 description: description ?? self.description,
77 visibility: visibility ?? self.visibility,
78 updated: updated ?? self.updated,
79 owner: owner,
80 head: head ?? self.head
75 fields: .init(
76 id: id,
77 rid: rid,
78 service: service,
79 name: name ?? self.name,
80 description: description ?? self.description,
81 visibility: visibility ?? self.visibility,
82 updated: updated ?? self.updated,
83 owner: owner,
84 head: head ?? self.head
85 )
8186 )
8287 }
8388
Hutch/Networking/ProjectService.swift +5 −5
@@ -49,7 +49,7 @@ private struct ProjectSummaryPayload: Decodable, Sendable {
4949 name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
5050 description = try container.decodeIfPresent(String.self, forKey: .description)
5151 website = try container.decodeIfPresent(String.self, forKey: .website)
52 visibility = try container.decodeIfPresent(Visibility.self, forKey: .visibility) ?? .public
52 visibility = try container.decodeIfPresent(Visibility.self, forKey: .visibility) ?? .publicVisibility
5353 tags = try container.decodeIfPresent([String].self, forKey: .tags) ?? []
5454 updated = try container.decodeIfPresent(Date.self, forKey: .updated) ?? .distantPast
5555 }
@@ -90,7 +90,7 @@ private struct ProjectDetailPayload: Decodable, Sendable {
9090 name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
9191 description = try container.decodeIfPresent(String.self, forKey: .description)
9292 website = try container.decodeIfPresent(String.self, forKey: .website)
93 visibility = try container.decodeIfPresent(Visibility.self, forKey: .visibility) ?? .public
93 visibility = try container.decodeIfPresent(Visibility.self, forKey: .visibility) ?? .publicVisibility
9494 tags = try container.decodeIfPresent([String].self, forKey: .tags) ?? []
9595 updated = try container.decodeIfPresent(Date.self, forKey: .updated) ?? .distantPast
9696 mailingLists = try container.decodeIfPresent(ProjectMailingListPage.self, forKey: .mailingLists) ?? .empty
@@ -126,7 +126,7 @@ private struct ProjectMailingListPayload: Decodable, Sendable {
126126 rid = try container.decode(String.self, forKey: .rid)
127127 name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
128128 description = try container.decodeIfPresent(String.self, forKey: .description)
129 visibility = try container.decodeIfPresent(Visibility.self, forKey: .visibility) ?? .public
129 visibility = try container.decodeIfPresent(Visibility.self, forKey: .visibility) ?? .publicVisibility
130130 owner = try container.decodeIfPresent(Entity.self, forKey: .owner) ?? Entity(canonicalName: "~unknown")
131131 }
132132}
@@ -160,7 +160,7 @@ private struct ProjectSourcePayload: Decodable, Sendable {
160160 rid = try container.decode(String.self, forKey: .rid)
161161 name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
162162 description = try container.decodeIfPresent(String.self, forKey: .description)
163 visibility = try container.decodeIfPresent(Visibility.self, forKey: .visibility) ?? .public
163 visibility = try container.decodeIfPresent(Visibility.self, forKey: .visibility) ?? .publicVisibility
164164 owner = try container.decodeIfPresent(Entity.self, forKey: .owner) ?? Entity(canonicalName: "~unknown")
165165 repoType = try container.decodeIfPresent(Project.SourceRepo.RepoType.self, forKey: .repoType) ?? .git
166166 }
@@ -193,7 +193,7 @@ private struct ProjectTrackerPayload: Decodable, Sendable {
193193 rid = try container.decode(String.self, forKey: .rid)
194194 name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
195195 description = try container.decodeIfPresent(String.self, forKey: .description)
196 visibility = try container.decodeIfPresent(Visibility.self, forKey: .visibility) ?? .public
196 visibility = try container.decodeIfPresent(Visibility.self, forKey: .visibility) ?? .publicVisibility
197197 owner = try container.decodeIfPresent(Entity.self, forKey: .owner) ?? Entity(canonicalName: "~unknown")
198198 }
199199}
Hutch/Networking/SRHTClient.swift +19 −29
@@ -57,7 +57,7 @@ final class SRHTClient: Sendable {
5757 service: SRHTService,
5858 query: String,
5959 variables: [String: any Sendable]? = nil,
60 responseType: T.Type
60 responseType _: T.Type
6161 ) async throws -> T {
6262 guard let token = _token.withLock({ $0 }), !token.isEmpty else {
6363 throw SRHTError.unauthorized
@@ -164,20 +164,14 @@ final class SRHTClient: Sendable {
164164 /// - service: The target SourceHut service.
165165 /// - query: The GraphQL mutation string.
166166 /// - variables: Variables dict; the file variable should be set to `nil`.
167 /// - fileVariablePath: The dot-separated path to the file variable (e.g. "input.avatar").
168 /// - fileData: The raw file data (e.g. JPEG).
169 /// - fileName: The file name to send (e.g. "avatar.jpg").
170 /// - mimeType: The MIME type (e.g. "image/jpeg").
167 /// - file: Multipart file payload (`variablePath` is the dot-separated GraphQL variable, e.g. `input.avatar`).
171168 /// - responseType: The expected `Decodable` type nested under `data`.
172169 func executeMultipart<T: Decodable>(
173170 service: SRHTService,
174171 query: String,
175172 variables: [String: any Sendable],
176 fileVariablePath: String,
177 fileData: Data,
178 fileName: String,
179 mimeType: String,
180 responseType: T.Type
173 file: MultipartUploadFile,
174 responseType _: T.Type
181175 ) async throws -> T {
182176 guard let token = _token.withLock({ $0 }), !token.isEmpty else {
183177 throw SRHTError.unauthorized
@@ -197,8 +191,8 @@ final class SRHTClient: Sendable {
197191 )
198192 let operationsData = try encoder.encode(operationsBody)
199193
200 // Build the map JSON: { "0": ["variables.<fileVariablePath>"] }
201 let mapDict = ["0": ["variables.\(fileVariablePath)"]]
194 // Build the map JSON: { "0": ["variables.<variablePath>"] }
195 let mapDict = ["0": ["variables.\(file.variablePath)"]]
202196 let mapData = try encoder.encode(mapDict)
203197
204198 // Assemble multipart body
@@ -220,9 +214,9 @@ final class SRHTClient: Sendable {
220214
221215 // Part: file
222216 body.append("--\(boundary)\r\n")
223 body.append("Content-Disposition: form-data; name=\"0\"; filename=\"\(fileName)\"\r\n")
224 body.append("Content-Type: \(mimeType)\r\n\r\n")
225 body.append(fileData)
217 body.append("Content-Disposition: form-data; name=\"0\"; filename=\"\(file.fileName)\"\r\n")
218 body.append("Content-Type: \(file.mimeType)\r\n\r\n")
219 body.append(file.fileData)
226220 body.append("\r\n")
227221
228222 // Closing boundary
@@ -311,7 +305,7 @@ final class SRHTClient: Sendable {
311305 query: String,
312306 variables: [String: any Sendable],
313307 files: [MultipartUploadFile],
314 responseType: T.Type
308 responseType _: T.Type
315309 ) async throws -> T {
316310 guard let token = _token.withLock({ $0 }), !token.isEmpty else {
317311 throw SRHTError.unauthorized
@@ -408,15 +402,14 @@ final class SRHTClient: Sendable {
408402 service: SRHTService,
409403 query: String,
410404 variables: [String: any Sendable]? = nil,
411 responseType: T.Type,
405 responseType _: T.Type,
412406 cacheKey: String
413407 ) async throws -> T {
414408 // Try cache first
415 if let cachedData = responseCache.get(forKey: cacheKey) {
416 if let cached = try? decoder.decode(GraphQLResponse<T>.self, from: cachedData),
417 let data = cached.data {
418 return data
419 }
409 if let cachedData = responseCache.get(forKey: cacheKey),
410 let cached = try? decoder.decode(GraphQLResponse<T>.self, from: cachedData),
411 let data = cached.data {
412 return data
420413 }
421414
422415 // No cache hit — fetch normally
@@ -434,7 +427,7 @@ final class SRHTClient: Sendable {
434427 service: SRHTService,
435428 query: String,
436429 variables: [String: any Sendable]? = nil,
437 responseType: T.Type,
430 responseType _: T.Type,
438431 cacheKey: String
439432 ) async throws -> T {
440433 guard let token = _token.withLock({ $0 }), !token.isEmpty else {
@@ -580,8 +573,7 @@ final class SRHTClient: Sendable {
580573 service: SRHTService,
581574 query: String,
582575 variables: [String: any Sendable]? = nil,
583 resultKeyPath: String,
584 type: T.Type
576 resultKeyPath: String
585577 ) -> SRHTPaginatedSequence<T> {
586578 SRHTPaginatedSequence(
587579 client: self,
@@ -598,16 +590,14 @@ final class SRHTClient: Sendable {
598590 service: SRHTService,
599591 query: String,
600592 variables: [String: any Sendable]? = nil,
601 resultKeyPath: String,
602 type: T.Type
593 resultKeyPath: String
603594 ) async throws -> [T] {
604595 var all: [T] = []
605596 for try await element in paginated(
606597 service: service,
607598 query: query,
608599 variables: variables,
609 resultKeyPath: resultKeyPath,
610 type: type
600 resultKeyPath: resultKeyPath
611601 ) {
612602 all.append(element)
613603 }
Hutch/Views/Builds/BuildDetailView.swift +3 −3
@@ -468,7 +468,7 @@ private struct EditResubmitBuildSheet: View {
468468 _manifest = State(initialValue: job.manifest ?? "")
469469 _tagsText = State(initialValue: job.tags.joined(separator: ", "))
470470 _note = State(initialValue: job.note ?? "")
471 _visibility = State(initialValue: job.visibility ?? .public)
471 _visibility = State(initialValue: job.visibility ?? .publicVisibility)
472472 }
473473
474474 var body: some View {
@@ -491,9 +491,9 @@ private struct EditResubmitBuildSheet: View {
491491 .autocorrectionDisabled()
492492 .themedRow()
493493 Picker("Visibility", selection: $visibility) {
494 Text("Public").tag(Visibility.public)
494 Text("Public").tag(Visibility.publicVisibility)
495495 Text("Unlisted").tag(Visibility.unlisted)
496 Text("Private").tag(Visibility.private)
496 Text("Private").tag(Visibility.privateVisibility)
497497 }
498498 .themedRow()
499499 Toggle("Start build now", isOn: $execute)
Hutch/Views/Builds/BuildListView.swift +3 −3
@@ -265,7 +265,7 @@ private struct SubmitBuildSheet: View {
265265 @State private var note = ""
266266 @State private var secrets = false
267267 @State private var execute = true
268 @State private var visibility: Visibility = .public
268 @State private var visibility: Visibility = .publicVisibility
269269
270270 init(viewModel: BuildListViewModel, onSubmitted: @escaping (Int) -> Void) {
271271 self.viewModel = viewModel
@@ -293,9 +293,9 @@ private struct SubmitBuildSheet: View {
293293 .autocorrectionDisabled()
294294 .themedRow()
295295 Picker("Visibility", selection: $visibility) {
296 Text("Public").tag(Visibility.public)
296 Text("Public").tag(Visibility.publicVisibility)
297297 Text("Unlisted").tag(Visibility.unlisted)
298 Text("Private").tag(Visibility.private)
298 Text("Private").tag(Visibility.privateVisibility)
299299 }
300300 .themedRow()
301301 Toggle("Start build now", isOn: $execute)
Hutch/Views/Lookup/LookupView.swift +11 −9
@@ -233,15 +233,17 @@ final class LookupViewModel {
233233
234234 let repository = try await appState.resolveRepository(owner: owner, name: name, service: service)
235235 let resolvedRepository = RepositorySummary(
236 id: repository.id,
237 rid: repository.rid,
238 service: service,
239 name: repository.name,
240 description: repository.description,
241 visibility: repository.visibility,
242 updated: repository.updated,
243 owner: repository.owner,
244 head: repository.head
236 fields: .init(
237 id: repository.id,
238 rid: repository.rid,
239 service: service,
240 name: repository.name,
241 description: repository.description,
242 visibility: repository.visibility,
243 updated: repository.updated,
244 owner: repository.owner,
245 head: repository.head
246 )
245247 )
246248
247249 return .repository(resolvedRepository)
Hutch/Views/Lookup/UserProfileViewModel.swift +11 −9
@@ -179,15 +179,17 @@ final class UserProfileViewModel {
179179
180180 func repositorySummary(service: SRHTService) -> RepositorySummary {
181181 RepositorySummary(
182 id: id,
183 rid: rid,
184 service: service,
185 name: name,
186 description: description,
187 visibility: visibility,
188 updated: updated,
189 owner: owner,
190 head: head
182 fields: .init(
183 id: id,
184 rid: rid,
185 service: service,
186 name: name,
187 description: description,
188 visibility: visibility,
189 updated: updated,
190 owner: owner,
191 head: head
192 )
191193 )
192194 }
193195 }
Hutch/Views/Pastes/PasteDetailView.swift +5 −5
@@ -442,27 +442,27 @@ private struct PasteVisibilitySheet: View {
442442 }
443443
444444 private var visibilityOptions: [Visibility] {
445 [.public, .unlisted, .private]
445 [.publicVisibility, .unlisted, .privateVisibility]
446446 }
447447
448448 private func title(for visibility: Visibility) -> String {
449449 switch visibility {
450 case .public:
450 case .publicVisibility:
451451 "Public"
452452 case .unlisted:
453453 "Unlisted"
454 case .private:
454 case .privateVisibility:
455455 "Private"
456456 }
457457 }
458458
459459 private func description(for visibility: Visibility) -> String {
460460 switch visibility {
461 case .public:
461 case .publicVisibility:
462462 "Visible to everyone and listed on your profile."
463463 case .unlisted:
464464 "Visible to anyone with the URL, but not listed on your profile."
465 case .private:
465 case .privateVisibility:
466466 "Visible only to explicitly allowed viewers."
467467 }
468468 }
Hutch/Views/Pastes/PasteListView.swift +8 −8
@@ -179,33 +179,33 @@ struct PasteListView: View {
179179
180180 private func nextVisibilityLabel(for visibility: Visibility) -> String {
181181 switch visibility {
182 case .public:
182 case .publicVisibility:
183183 return "Make Unlisted"
184184 case .unlisted:
185185 return "Make Private"
186 case .private:
186 case .privateVisibility:
187187 return "Make Public"
188188 }
189189 }
190190
191191 private func nextVisibilityIcon(for visibility: Visibility) -> String {
192192 switch visibility {
193 case .public:
193 case .publicVisibility:
194194 return "eye.slash"
195195 case .unlisted:
196196 return "lock"
197 case .private:
197 case .privateVisibility:
198198 return "globe"
199199 }
200200 }
201201
202202 private func nextVisibilityColor(for visibility: Visibility) -> Color {
203203 switch visibility {
204 case .public:
204 case .publicVisibility:
205205 return .orange
206206 case .unlisted:
207207 return .red
208 case .private:
208 case .privateVisibility:
209209 return .green
210210 }
211211 }
@@ -316,9 +316,9 @@ private struct CreatePasteSheet: View {
316316
317317 Section("Visibility") {
318318 Picker("Visibility", selection: $visibility) {
319 Text("Public").tag(Visibility.public)
319 Text("Public").tag(Visibility.publicVisibility)
320320 Text("Unlisted").tag(Visibility.unlisted)
321 Text("Private").tag(Visibility.private)
321 Text("Private").tag(Visibility.privateVisibility)
322322 }
323323 .themedRow()
324324 }
Hutch/Views/Pastes/PasteListViewModel.swift +4 −4
@@ -131,12 +131,12 @@ final class PasteListViewModel {
131131 func cycleVisibility(for paste: Paste) async {
132132 let next: Visibility
133133 switch paste.visibility {
134 case .public:
134 case .publicVisibility:
135135 next = .unlisted
136136 case .unlisted:
137 next = .private
138 case .private:
139 next = .public
137 next = .privateVisibility
138 case .privateVisibility:
139 next = .publicVisibility
140140 }
141141
142142 let original = pastes
Hutch/Views/Repositories/FileTreeViewModel.swift +1 −4
@@ -327,14 +327,11 @@ final class FileTreeViewModel {
327327 }
328328
329329 case .binaryBlob(let blob):
330 if blob.content != nil {
330 if blob.content != nil || blob.id == nil {
331331 viewingEntry = entry
332332 viewingObject = object
333333 } else if let blobId = blob.id {
334334 await loadBlob(entry: entry, blobId: blobId)
335 } else {
336 viewingEntry = entry
337 viewingObject = object
338335 }
339336
340337 case .unknown:
Hutch/Views/Repositories/HgRepositorySettingsView.swift +2 −2
@@ -121,9 +121,9 @@ struct HgRepositorySettingsView: View {
121121 .themedRow()
122122
123123 Picker("Visibility", selection: Bindable(viewModel).editedVisibility) {
124 Text("Public").tag(Visibility.public)
124 Text("Public").tag(Visibility.publicVisibility)
125125 Text("Unlisted").tag(Visibility.unlisted)
126 Text("Private").tag(Visibility.private)
126 Text("Private").tag(Visibility.privateVisibility)
127127 }
128128 .themedRow()
129129
Hutch/Views/Repositories/ReadmeView.swift +1 −1
@@ -1828,7 +1828,7 @@ private final class HTMLWebViewCoordinator: NSObject, WKNavigationDelegate, @unc
18281828 }
18291829
18301830 func webView(
1831 _ webView: WKWebView,
1831 _: WKWebView,
18321832 decidePolicyFor navigationAction: WKNavigationAction,
18331833 decisionHandler: @escaping @MainActor (WKNavigationActionPolicy) -> Void
18341834 ) {
Hutch/Views/Repositories/RepositoryListView.swift +3 −3
@@ -214,7 +214,7 @@ private struct CreateRepositorySheet: View {
214214 @State private var name = ""
215215 @State private var description = ""
216216 @State private var cloneURL = ""
217 @State private var visibility: Visibility = .public
217 @State private var visibility: Visibility = .publicVisibility
218218 @State private var service: RepositoryCreationService = .git
219219
220220 var body: some View {
@@ -235,9 +235,9 @@ private struct CreateRepositorySheet: View {
235235 .lineLimit(2...4)
236236 .themedRow()
237237 Picker("Visibility", selection: $visibility) {
238 Text("Public").tag(Visibility.public)
238 Text("Public").tag(Visibility.publicVisibility)
239239 Text("Unlisted").tag(Visibility.unlisted)
240 Text("Private").tag(Visibility.private)
240 Text("Private").tag(Visibility.privateVisibility)
241241 }
242242 .themedRow()
243243 }
Hutch/Views/Repositories/RepositoryListViewModel.swift +39 −56
@@ -397,15 +397,17 @@ final class RepositoryListViewModel {
397397
398398 func repositorySummary(service: SRHTService) -> RepositorySummary {
399399 RepositorySummary(
400 id: id,
401 rid: rid,
402 service: service,
403 name: name,
404 description: description,
405 visibility: visibility,
406 updated: updated,
407 owner: owner,
408 head: head
400 fields: .init(
401 id: id,
402 rid: rid,
403 service: service,
404 name: name,
405 description: description,
406 visibility: visibility,
407 updated: updated,
408 owner: owner,
409 head: head
410 )
409411 )
410412 }
411413 }
@@ -422,15 +424,17 @@ final class RepositoryListViewModel {
422424
423425 func repositorySummary(service: SRHTService) -> RepositorySummary {
424426 RepositorySummary(
425 id: id,
426 rid: rid,
427 service: service,
428 name: name,
429 description: description,
430 visibility: visibility,
431 updated: updated,
432 owner: owner,
433 head: tip.map { Reference(name: $0.branch, target: nil) }
427 fields: .init(
428 id: id,
429 rid: rid,
430 service: service,
431 name: name,
432 description: description,
433 visibility: visibility,
434 updated: updated,
435 owner: owner,
436 head: tip.map { Reference(name: $0.branch, target: nil) }
437 )
434438 )
435439 }
436440 }
@@ -463,17 +467,7 @@ final class RepositoryListViewModel {
463467 }
464468
465469 if useCache && cursor == nil {
466 switch service {
467 case .git:
468 let result = try await client.executeAndCache(
469 service: service,
470 query: Self.gitQuery,
471 variables: variables.isEmpty ? nil : variables,
472 responseType: RepositoriesResponse.self,
473 cacheKey: cacheKey(for: service)
474 )
475 return result.repositories ?? Self.emptyPage
476 case .hg:
470 if service == .hg {
477471 let hgVariables = cursor.map { ["cursor": $0 as any Sendable] }
478472 let result = try await client.executeAndCache(
479473 service: service,
@@ -497,27 +491,17 @@ final class RepositoryListViewModel {
497491 } ?? [],
498492 cursor: result.repositories?.cursor
499493 )
500 default:
501 let result = try await client.executeAndCache(
502 service: service,
503 query: Self.gitQuery,
504 variables: variables.isEmpty ? nil : variables,
505 responseType: RepositoriesResponse.self,
506 cacheKey: cacheKey(for: service)
507 )
508 return result.repositories ?? Self.emptyPage
509494 }
495 let result = try await client.executeAndCache(
496 service: service,
497 query: Self.gitQuery,
498 variables: variables.isEmpty ? nil : variables,
499 responseType: RepositoriesResponse.self,
500 cacheKey: cacheKey(for: service)
501 )
502 return result.repositories ?? Self.emptyPage
510503 } else {
511 switch service {
512 case .git:
513 let result = try await client.execute(
514 service: service,
515 query: Self.gitQuery,
516 variables: variables.isEmpty ? nil : variables,
517 responseType: RepositoriesResponse.self
518 )
519 return result.repositories ?? Self.emptyPage
520 case .hg:
504 if service == .hg {
521505 let hgVariables = cursor.map { ["cursor": $0 as any Sendable] }
522506 let result = try await client.execute(
523507 service: service,
@@ -540,15 +524,14 @@ final class RepositoryListViewModel {
540524 } ?? [],
541525 cursor: result.repositories?.cursor
542526 )
543 default:
544 let result = try await client.execute(
545 service: service,
546 query: Self.gitQuery,
547 variables: variables.isEmpty ? nil : variables,
548 responseType: RepositoriesResponse.self
549 )
550 return result.repositories ?? Self.emptyPage
551527 }
528 let result = try await client.execute(
529 service: service,
530 query: Self.gitQuery,
531 variables: variables.isEmpty ? nil : variables,
532 responseType: RepositoriesResponse.self
533 )
534 return result.repositories ?? Self.emptyPage
552535 }
553536 }
554537
Hutch/Views/Repositories/RepositoryRowView.swift +4 −4
@@ -154,17 +154,17 @@ struct VisibilityBadge: View {
154154
155155 private var label: String {
156156 switch visibility {
157 case .public: "PUBLIC"
157 case .publicVisibility: "PUBLIC"
158158 case .unlisted: "UNLISTED"
159 case .private: "PRIVATE"
159 case .privateVisibility: "PRIVATE"
160160 }
161161 }
162162
163163 private var color: Color {
164164 switch visibility {
165 case .public: .green
165 case .publicVisibility: .green
166166 case .unlisted: .orange
167 case .private: .red
167 case .privateVisibility: .red
168168 }
169169 }
170170}
Hutch/Views/Repositories/RepositorySettingsView.swift +4 −4
@@ -227,9 +227,9 @@ struct RepositorySettingsView: View {
227227 private func visibilitySection(_ viewModel: RepositorySettingsViewModel) -> some View {
228228 Section {
229229 Picker("Visibility", selection: Bindable(viewModel).editedVisibility) {
230 Text("Public").tag(Visibility.public)
230 Text("Public").tag(Visibility.publicVisibility)
231231 Text("Unlisted").tag(Visibility.unlisted)
232 Text("Private").tag(Visibility.private)
232 Text("Private").tag(Visibility.privateVisibility)
233233 }
234234 .themedRow()
235235
@@ -283,11 +283,11 @@ struct RepositorySettingsView: View {
283283
284284 private func visibilityConfirmationMessage(for viewModel: RepositorySettingsViewModel) -> String {
285285 switch viewModel.editedVisibility {
286 case .public:
286 case .publicVisibility:
287287 "Anyone will be able to find and view this repository."
288288 case .unlisted:
289289 "People with the link can view this repository, but it won't appear in public listings."
290 case .private:
290 case .privateVisibility:
291291 "Only people with explicit access will be able to view this repository."
292292 }
293293 }
Hutch/Views/Repositories/RepositorySettingsViewModel.swift +11 −9
@@ -20,15 +20,17 @@ private struct UpdatedRepositoryPayload: Decodable, Sendable {
2020
2121 func repositorySummary(using owner: Entity, service: SRHTService) -> RepositorySummary {
2222 RepositorySummary(
23 id: id,
24 rid: rid,
25 service: service,
26 name: name,
27 description: description,
28 visibility: visibility,
29 updated: updated,
30 owner: owner,
31 head: head
23 fields: .init(
24 id: id,
25 rid: rid,
26 service: service,
27 name: name,
28 description: description,
29 visibility: visibility,
30 updated: updated,
31 owner: owner,
32 head: head
33 )
3234 )
3335 }
3436}
Hutch/Views/Repositories/RepositorySummarySupport.swift +2 −2
@@ -37,11 +37,11 @@ func repositoryCloneURLs(for repository: RepositorySummary) -> RepositoryCloneUR
3737
3838func repositoryVisibilityLabel(_ visibility: Visibility) -> String {
3939 switch visibility {
40 case .public:
40 case .publicVisibility:
4141 return "Public"
4242 case .unlisted:
4343 return "Unlisted"
44 case .private:
44 case .privateVisibility:
4545 return "Private"
4646 }
4747}
Hutch/Views/Settings/SettingsViewModel.swift +6 −4
@@ -228,10 +228,12 @@ final class SettingsViewModel {
228228 service: .meta,
229229 query: Self.updateUserMutation,
230230 variables: ["input": input],
231 fileVariablePath: "input.avatar",
232 fileData: jpegData,
233 fileName: "avatar.jpg",
234 mimeType: "image/jpeg",
231 file: MultipartUploadFile(
232 variablePath: "input.avatar",
233 fileData: jpegData,
234 fileName: "avatar.jpg",
235 mimeType: "image/jpeg"
236 ),
235237 responseType: UpdateUserResponse.self
236238 )
237239 let updated = result.updateUser
Hutch/Views/Tickets/TicketListView.swift +2
@@ -1400,11 +1400,13 @@ struct FlowLayout: Layout {
14001400 var spacing: CGFloat = 4
14011401
14021402 func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
1403 _ = cache
14031404 let result = layoutSubviews(proposal: proposal, subviews: subviews)
14041405 return result.size
14051406 }
14061407
14071408 func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
1409 _ = cache
14081410 let result = layoutSubviews(proposal: proposal, subviews: subviews)
14091411 for (index, position) in result.positions.enumerated() {
14101412 subviews[index].place(
Hutch/Views/Tickets/TrackerListView.swift +1 −1
@@ -37,7 +37,7 @@ struct TrackerListView: View {
3737 error: viewModel.error,
3838 initialName: "",
3939 initialDescription: "",
40 initialVisibility: .public
40 initialVisibility: .publicVisibility
4141 ) { name, description, visibility in
4242 if let tracker = await viewModel.createTracker(
4343 name: name,
Hutch/Views/Tickets/TrackerManagementView.swift +2 −2
@@ -659,9 +659,9 @@ struct TrackerEditorSheet: View {
659659 .lineLimit(2...4)
660660 .themedRow()
661661 Picker("Visibility", selection: $visibility) {
662 Text("Public").tag(Visibility.public)
662 Text("Public").tag(Visibility.publicVisibility)
663663 Text("Unlisted").tag(Visibility.unlisted)
664 Text("Private").tag(Visibility.private)
664 Text("Private").tag(Visibility.privateVisibility)
665665 }
666666 .themedRow()
667667 }
HutchTests/AppStateTests.swift +12 −10
@@ -39,22 +39,24 @@ struct AppStateTests {
3939 func navigationHelpersQueueExpectedTargets() {
4040 let appState = AppState()
4141 let repository = RepositorySummary(
42 id: 1,
43 rid: "repo",
44 service: .git,
45 name: "hutch",
46 description: nil,
47 visibility: .public,
48 updated: .distantPast,
49 owner: Entity(canonicalName: "~owner"),
50 head: nil
42 fields: .init(
43 id: 1,
44 rid: "repo",
45 service: .git,
46 name: "hutch",
47 description: nil,
48 visibility: .publicVisibility,
49 updated: .distantPast,
50 owner: Entity(canonicalName: "~owner"),
51 head: nil
52 )
5153 )
5254 let tracker = TrackerSummary(
5355 id: 2,
5456 rid: "tracker",
5557 name: "todo",
5658 description: nil,
57 visibility: .public,
59 visibility: .publicVisibility,
5860 updated: .distantPast,
5961 owner: Entity(canonicalName: "~owner")
6062 )
HutchTests/ProjectTests.swift +6 −6
@@ -15,7 +15,7 @@ struct ProjectTests {
1515 name: "Hutch",
1616 description: nil,
1717 website: nil,
18 visibility: .public,
18 visibility: .publicVisibility,
1919 tags: [],
2020 updated: Date(timeIntervalSince1970: 0)
2121 ),
@@ -25,7 +25,7 @@ struct ProjectTests {
2525 id: "list-1",
2626 name: "hutch-devel",
2727 description: nil,
28 visibility: .public,
28 visibility: .publicVisibility,
2929 owner: Entity(canonicalName: "~owner")
3030 )
3131 ],
@@ -34,7 +34,7 @@ struct ProjectTests {
3434 id: "repo-1",
3535 name: "hutch",
3636 description: nil,
37 visibility: .public,
37 visibility: .publicVisibility,
3838 owner: Entity(canonicalName: "~owner"),
3939 repoType: .git
4040 ),
@@ -42,7 +42,7 @@ struct ProjectTests {
4242 id: "repo-2",
4343 name: "hutch-web",
4444 description: nil,
45 visibility: .public,
45 visibility: .publicVisibility,
4646 owner: Entity(canonicalName: "~owner"),
4747 repoType: .git
4848 )
@@ -52,7 +52,7 @@ struct ProjectTests {
5252 id: "tracker-1",
5353 name: "bugs",
5454 description: nil,
55 visibility: .public,
55 visibility: .publicVisibility,
5656 owner: Entity(canonicalName: "~owner")
5757 )
5858 ],
@@ -71,7 +71,7 @@ struct ProjectTests {
7171 name: "Docs",
7272 description: nil,
7373 website: Fixture.exampleWebsite,
74 visibility: .public,
74 visibility: .publicVisibility,
7575 tags: [],
7676 updated: Date(timeIntervalSince1970: 0)
7777 ),
HutchTests/RepositoryACLViewModelTests.swift +11 −9
@@ -110,15 +110,17 @@ struct RepositoryACLViewModelTests {
110110 @MainActor
111111 private func makeRepository() -> RepositorySummary {
112112 RepositorySummary(
113 id: 1,
114 rid: "rid-1",
115 service: .git,
116 name: "repo",
117 description: nil,
118 visibility: .public,
119 updated: .now,
120 owner: Entity(canonicalName: "~owner"),
121 head: nil
113 fields: .init(
114 id: 1,
115 rid: "rid-1",
116 service: .git,
117 name: "repo",
118 description: nil,
119 visibility: .publicVisibility,
120 updated: .now,
121 owner: Entity(canonicalName: "~owner"),
122 head: nil
123 )
122124 )
123125 }
124126}
HutchTests/RepositoryListViewModelTests.swift +11 −9
@@ -79,15 +79,17 @@ struct RepositoryListViewModelTests {
7979 branch: String = "main"
8080 ) -> RepositorySummary {
8181 RepositorySummary(
82 id: id,
83 rid: "rid-\(id)",
84 service: service,
85 name: name,
86 description: description,
87 visibility: .public,
88 updated: Date(timeIntervalSince1970: TimeInterval(id)),
89 owner: Entity(canonicalName: owner),
90 head: Reference(name: branch, target: nil)
82 fields: .init(
83 id: id,
84 rid: "rid-\(id)",
85 service: service,
86 name: name,
87 description: description,
88 visibility: .publicVisibility,
89 updated: Date(timeIntervalSince1970: TimeInterval(id)),
90 owner: Entity(canonicalName: owner),
91 head: Reference(name: branch, target: nil)
92 )
9193 )
9294 }
9395}
HutchTests/RepositorySettingsViewModelTests.swift +11 −9
@@ -118,15 +118,17 @@ struct RepositorySettingsViewModelTests {
118118 @MainActor
119119 private func makeRepository(headName: String?) -> RepositorySummary {
120120 RepositorySummary(
121 id: 1,
122 rid: "rid-1",
123 service: .git,
124 name: "repo",
125 description: "desc",
126 visibility: .public,
127 updated: .now,
128 owner: Entity(canonicalName: "~owner"),
129 head: headName.map { Reference(name: $0, target: nil) }
121 fields: .init(
122 id: 1,
123 rid: "rid-1",
124 service: .git,
125 name: "repo",
126 description: "desc",
127 visibility: .publicVisibility,
128 updated: .now,
129 owner: Entity(canonicalName: "~owner"),
130 head: headName.map { Reference(name: $0, target: nil) }
131 )
130132 )
131133 }
132134}
HutchTests/SRHTWebURLTests.swift +12 −10
@@ -14,22 +14,24 @@ struct SRHTWebURLTests {
1414 }
1515
1616 private let repository = RepositorySummary(
17 id: 1,
18 rid: "repo-1",
19 service: .git,
20 name: "hutch",
21 description: nil,
22 visibility: .public,
23 updated: .distantPast,
24 owner: Entity(canonicalName: "~ccleberg"),
25 head: nil
17 fields: .init(
18 id: 1,
19 rid: "repo-1",
20 service: .git,
21 name: "hutch",
22 description: nil,
23 visibility: .publicVisibility,
24 updated: .distantPast,
25 owner: Entity(canonicalName: "~ccleberg"),
26 head: nil
27 )
2628 )
2729 private let tracker = TrackerSummary(
2830 id: 2,
2931 rid: "tracker-1",
3032 name: "todo",
3133 description: nil,
32 visibility: .public,
34 visibility: .publicVisibility,
3335 updated: .distantPast,
3436 owner: Entity(canonicalName: "~ccleberg")
3537 )
HutchTests/TrackerListViewModelTests.swift +1 −1
@@ -66,7 +66,7 @@ struct TrackerListViewModelTests {
6666 rid: "rid-\(id)",
6767 name: name,
6868 description: description,
69 visibility: .public,
69 visibility: .publicVisibility,
7070 updated: Date(),
7171 owner: Entity(canonicalName: owner)
7272 )