Commit c619fe4a96
Unsigned
Layout: unified · split
Hutch.xcodeproj/project.pbxproj +8 −8
| @@ -539,7 +539,7 @@ | ||
| 539 | 539 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; |
| 540 | 540 | CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements; |
| 541 | 541 | CODE_SIGN_STYLE = Automatic; |
| 542 | CURRENT_PROJECT_VERSION = 19; | |
| 542 | CURRENT_PROJECT_VERSION = 20; | |
| 543 | 543 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 544 | 544 | ENABLE_PREVIEWS = YES; |
| 545 | 545 | GENERATE_INFOPLIST_FILE = YES; |
| @@ -556,7 +556,7 @@ | ||
| 556 | 556 | "$(inherited)", |
| 557 | 557 | "@executable_path/Frameworks", |
| 558 | 558 | ); |
| 559 | MARKETING_VERSION = 2.8.0; | |
| 559 | MARKETING_VERSION = 2.8.1; | |
| 560 | 560 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch; |
| 561 | 561 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 562 | 562 | STRING_CATALOG_GENERATE_SYMBOLS = YES; |
| @@ -576,7 +576,7 @@ | ||
| 576 | 576 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; |
| 577 | 577 | CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements; |
| 578 | 578 | CODE_SIGN_STYLE = Automatic; |
| 579 | CURRENT_PROJECT_VERSION = 19; | |
| 579 | CURRENT_PROJECT_VERSION = 20; | |
| 580 | 580 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 581 | 581 | ENABLE_PREVIEWS = YES; |
| 582 | 582 | GENERATE_INFOPLIST_FILE = YES; |
| @@ -593,7 +593,7 @@ | ||
| 593 | 593 | "$(inherited)", |
| 594 | 594 | "@executable_path/Frameworks", |
| 595 | 595 | ); |
| 596 | MARKETING_VERSION = 2.8.0; | |
| 596 | MARKETING_VERSION = 2.8.1; | |
| 597 | 597 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch; |
| 598 | 598 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 599 | 599 | STRING_CATALOG_GENERATE_SYMBOLS = YES; |
| @@ -656,7 +656,7 @@ | ||
| 656 | 656 | APPLICATION_EXTENSION_API_ONLY = YES; |
| 657 | 657 | CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements; |
| 658 | 658 | CODE_SIGN_STYLE = Automatic; |
| 659 | CURRENT_PROJECT_VERSION = 19; | |
| 659 | CURRENT_PROJECT_VERSION = 20; | |
| 660 | 660 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 661 | 661 | GENERATE_INFOPLIST_FILE = NO; |
| 662 | 662 | INFOPLIST_FILE = HutchWidgetExtension/Info.plist; |
| @@ -666,7 +666,7 @@ | ||
| 666 | 666 | "@executable_path/Frameworks", |
| 667 | 667 | "@executable_path/../../Frameworks", |
| 668 | 668 | ); |
| 669 | MARKETING_VERSION = 2.8.0; | |
| 669 | MARKETING_VERSION = 2.8.1; | |
| 670 | 670 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension; |
| 671 | 671 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 672 | 672 | SKIP_INSTALL = YES; |
| @@ -685,7 +685,7 @@ | ||
| 685 | 685 | APPLICATION_EXTENSION_API_ONLY = YES; |
| 686 | 686 | CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements; |
| 687 | 687 | CODE_SIGN_STYLE = Automatic; |
| 688 | CURRENT_PROJECT_VERSION = 19; | |
| 688 | CURRENT_PROJECT_VERSION = 20; | |
| 689 | 689 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 690 | 690 | GENERATE_INFOPLIST_FILE = NO; |
| 691 | 691 | INFOPLIST_FILE = HutchWidgetExtension/Info.plist; |
| @@ -695,7 +695,7 @@ | ||
| 695 | 695 | "@executable_path/Frameworks", |
| 696 | 696 | "@executable_path/../../Frameworks", |
| 697 | 697 | ); |
| 698 | MARKETING_VERSION = 2.8.0; | |
| 698 | MARKETING_VERSION = 2.8.1; | |
| 699 | 699 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension; |
| 700 | 700 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 701 | 701 | SKIP_INSTALL = YES; |
Hutch/Models/Git.swift +7
| @@ -82,6 +82,13 @@ struct Reference: Codable, Sendable, Hashable { | ||
| 82 | 82 | let target: String? |
| 83 | 83 | } |
| 84 | 84 | |
| 85 | /// A Git reference enriched with the date of its tip commit or tag, for display in the Refs tab. | |
| 86 | struct ReferenceDetail: Sendable, Hashable { | |
| 87 | let name: String | |
| 88 | let target: String? | |
| 89 | let date: Date? | |
| 90 | } | |
| 91 | ||
| 85 | 92 | // MARK: - TreeEntry |
| 86 | 93 | |
| 87 | 94 | /// An entry in a Git tree (file or directory). |
Hutch/Views/Repositories/ReferencesListView.swift +10 −3
| @@ -51,14 +51,21 @@ struct ReferencesListView: View { | ||
| 51 | 51 | } |
| 52 | 52 | |
| 53 | 53 | private struct ReferenceRow: View { |
| 54 | let reference: Reference | |
| 54 | let reference: ReferenceDetail | |
| 55 | 55 | let prefix: String |
| 56 | 56 | |
| 57 | 57 | var body: some View { |
| 58 | 58 | HStack { |
| 59 | 59 | Label { |
| 60 | Text(shortName) | |
| 61 | .font(.body.monospaced()) | |
| 60 | VStack(alignment: .leading, spacing: 2) { | |
| 61 | Text(shortName) | |
| 62 | .font(.body.monospaced()) | |
| 63 | if let date = reference.date { | |
| 64 | Text(date.relativeDescription) | |
| 65 | .font(.caption) | |
| 66 | .foregroundStyle(.secondary) | |
| 67 | } | |
| 68 | } | |
| 62 | 69 | } icon: { |
| 63 | 70 | Image(systemName: icon) |
| 64 | 71 | .foregroundStyle(iconColor) |
Hutch/Views/Repositories/RepositoryDetailViewModel.swift +36 −6
| @@ -24,10 +24,33 @@ private struct RefsRepository: Decodable, Sendable { | ||
| 24 | 24 | } |
| 25 | 25 | |
| 26 | 26 | private struct RefsPage: Decodable, Sendable { |
| 27 | let results: [Reference] | |
| 27 | let results: [ReferencePayload] | |
| 28 | 28 | let cursor: String? |
| 29 | 29 | } |
| 30 | 30 | |
| 31 | /// Decodes a single reference including the optional follow object for date extraction. | |
| 32 | private struct ReferencePayload: Decodable, Sendable { | |
| 33 | let name: String | |
| 34 | let target: String? | |
| 35 | let follow: ReferenceFollow? | |
| 36 | ||
| 37 | func toDetail() -> ReferenceDetail { | |
| 38 | // Branches and lightweight tags follow to a Commit (author.time). | |
| 39 | // Annotated tags follow to a Tag object (tagger.time). | |
| 40 | let date = follow?.author?.time ?? follow?.tagger?.time | |
| 41 | return ReferenceDetail(name: name, target: target, date: date) | |
| 42 | } | |
| 43 | } | |
| 44 | ||
| 45 | private struct ReferenceFollow: Decodable, Sendable { | |
| 46 | let author: ReferenceSignature? | |
| 47 | let tagger: ReferenceSignature? | |
| 48 | } | |
| 49 | ||
| 50 | private struct ReferenceSignature: Decodable, Sendable { | |
| 51 | let time: Date | |
| 52 | } | |
| 53 | ||
| 31 | 54 | private struct ReadmeResponse: Decodable, Sendable { |
| 32 | 55 | let repository: ReadmeRepository? |
| 33 | 56 | } |
| @@ -98,8 +121,8 @@ final class RepositoryDetailViewModel { | ||
| 98 | 121 | |
| 99 | 122 | // MARK: - References state |
| 100 | 123 | |
| 101 | private(set) var branches: [Reference] = [] | |
| 102 | private(set) var tags: [Reference] = [] | |
| 124 | private(set) var branches: [ReferenceDetail] = [] | |
| 125 | private(set) var tags: [ReferenceDetail] = [] | |
| 103 | 126 | private(set) var isLoadingRefs = false |
| 104 | 127 | |
| 105 | 128 | // MARK: - README state |
| @@ -220,7 +243,14 @@ final class RepositoryDetailViewModel { | ||
| 220 | 243 | query refs($rid: ID!) { |
| 221 | 244 | repository(rid: $rid) { |
| 222 | 245 | references { |
| 223 | results { name target } | |
| 246 | results { | |
| 247 | name | |
| 248 | target | |
| 249 | follow { | |
| 250 | ... on Commit { author { time } } | |
| 251 | ... on Tag { tagger { time } } | |
| 252 | } | |
| 253 | } | |
| 224 | 254 | cursor |
| 225 | 255 | } |
| 226 | 256 | } |
| @@ -240,8 +270,8 @@ final class RepositoryDetailViewModel { | ||
| 240 | 270 | responseType: RefsResponse.self |
| 241 | 271 | ) |
| 242 | 272 | let allRefs = result.repository?.references.results ?? [] |
| 243 | branches = allRefs.filter { $0.name.hasPrefix("refs/heads/") } | |
| 244 | tags = allRefs.filter { $0.name.hasPrefix("refs/tags/") } | |
| 273 | branches = allRefs.filter { $0.name.hasPrefix("refs/heads/") }.map { $0.toDetail() } | |
| 274 | tags = allRefs.filter { $0.name.hasPrefix("refs/tags/") }.map { $0.toDetail() } | |
| 245 | 275 | } catch { |
| 246 | 276 | self.error = error.userFacingMessage |
| 247 | 277 | } |
Hutch/Views/Repositories/RepositorySettingsView.swift +1 −1
| @@ -2,7 +2,7 @@ import SwiftUI | ||
| 2 | 2 | |
| 3 | 3 | struct RepositorySettingsView: View { |
| 4 | 4 | let repository: RepositorySummary |
| 5 | let branches: [Reference] | |
| 5 | let branches: [ReferenceDetail] | |
| 6 | 6 | let client: SRHTClient |
| 7 | 7 | let onRenamed: (String) -> Void |
| 8 | 8 | let onDeleted: () -> Void |
Hutch/Views/Repositories/RepositorySettingsViewModel.swift +2 −2
| @@ -102,7 +102,7 @@ final class RepositorySettingsViewModel { | ||
| 102 | 102 | |
| 103 | 103 | // MARK: - Branches (for HEAD picker) |
| 104 | 104 | |
| 105 | var branches: [Reference] | |
| 105 | var branches: [ReferenceDetail] | |
| 106 | 106 | |
| 107 | 107 | // MARK: - Results |
| 108 | 108 | |
| @@ -112,7 +112,7 @@ final class RepositorySettingsViewModel { | ||
| 112 | 112 | |
| 113 | 113 | init( |
| 114 | 114 | repository: RepositorySummary, |
| 115 | branches: [Reference], | |
| 115 | branches: [ReferenceDetail], | |
| 116 | 116 | client: SRHTClient |
| 117 | 117 | ) { |
| 118 | 118 | self.repositoryId = repository.id |
codex-fix-refs-type-conflict.md added +89
| @@ -0,0 +1,89 @@ | ||
| 1 | # Fix: Conflicting `[Reference]` vs `[ReferenceDetail]` in RepositorySettingsView | |
| 2 | ||
| 3 | ## Background | |
| 4 | ||
| 5 | `RepositoryDetailViewModel.branches` was recently changed from `[Reference]` to | |
| 6 | `[ReferenceDetail]` to support displaying commit dates in the Refs tab. | |
| 7 | `ReferenceDetail` has the same `name: String` and `target: String?` fields as | |
| 8 | `Reference`, plus an additional `date: Date?`. | |
| 9 | ||
| 10 | `RepositorySettingsView` and `RepositorySettingsViewModel` still declare their | |
| 11 | `branches` parameter as `[Reference]`, causing this compiler error: | |
| 12 | ||
| 13 | ``` | |
| 14 | RepositoryDetailView.swift:56:51 | |
| 15 | Conflicting arguments to generic parameter 'T' ('[ReferenceDetail]' vs. '[Reference]') | |
| 16 | ``` | |
| 17 | ||
| 18 | The settings view model only uses `branches` for the HEAD branch picker | |
| 19 | (`selectedHeadReferenceForSave()` accesses `$0.name`). No mapping back to | |
| 20 | `Reference` is needed — just update the type throughout the settings layer. | |
| 21 | ||
| 22 | --- | |
| 23 | ||
| 24 | ## Changes Required | |
| 25 | ||
| 26 | ### 1. `Hutch/Views/Repositories/RepositorySettingsViewModel.swift` | |
| 27 | ||
| 28 | **Change the stored property type:** | |
| 29 | ||
| 30 | Find: | |
| 31 | ```swift | |
| 32 | var branches: [Reference] | |
| 33 | ``` | |
| 34 | ||
| 35 | Replace with: | |
| 36 | ```swift | |
| 37 | var branches: [ReferenceDetail] | |
| 38 | ``` | |
| 39 | ||
| 40 | **Change the `init` parameter type:** | |
| 41 | ||
| 42 | Find: | |
| 43 | ```swift | |
| 44 | init( | |
| 45 | repository: RepositorySummary, | |
| 46 | branches: [Reference], | |
| 47 | client: SRHTClient | |
| 48 | ) { | |
| 49 | ``` | |
| 50 | ||
| 51 | Replace with: | |
| 52 | ```swift | |
| 53 | init( | |
| 54 | repository: RepositorySummary, | |
| 55 | branches: [ReferenceDetail], | |
| 56 | client: SRHTClient | |
| 57 | ) { | |
| 58 | ``` | |
| 59 | ||
| 60 | ### 2. `Hutch/Views/Repositories/RepositorySettingsView.swift` | |
| 61 | ||
| 62 | **Change the stored property type:** | |
| 63 | ||
| 64 | Find: | |
| 65 | ```swift | |
| 66 | let branches: [Reference] | |
| 67 | ``` | |
| 68 | ||
| 69 | Replace with: | |
| 70 | ```swift | |
| 71 | let branches: [ReferenceDetail] | |
| 72 | ``` | |
| 73 | ||
| 74 | --- | |
| 75 | ||
| 76 | ## No Other Changes | |
| 77 | ||
| 78 | - Do not modify `Reference` or `ReferenceDetail` in `Git.swift`. | |
| 79 | - Do not modify `RepositoryDetailView.swift` — the call site is already correct. | |
| 80 | - Do not modify `ReferencesListView.swift` or `RepositoryDetailViewModel.swift`. | |
| 81 | - `selectedHeadReferenceForSave()` in the settings view model accesses only | |
| 82 | `$0.name` on each branch, which exists on `ReferenceDetail` — no logic | |
| 83 | changes are needed. | |
| 84 | ||
| 85 | ## Verification | |
| 86 | ||
| 87 | Build the project. The compiler error at `RepositoryDetailView.swift:56` should | |
| 88 | be gone. Confirm the repository settings sheet still opens and the HEAD branch | |
| 89 | picker populates correctly. | |