diff options
| author | Christian Cleberg <[email protected]> | 2026-05-14 13:18:55 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-05-14 13:27:41 -0500 |
| commit | 51b2eb0e6296734960f37cebd40fee34a1f261f9 (patch) | |
| tree | 65063727d8dcff3540818676d7b53288499475dd /Hutch/Views | |
| parent | 9a853f8777230e0639261b5bc817201ec2d9deca (diff) | |
| download | hutch-51b2eb0e6296734960f37cebd40fee34a1f261f9.tar.gz hutch-51b2eb0e6296734960f37cebd40fee34a1f261f9.tar.bz2 hutch-51b2eb0e6296734960f37cebd40fee34a1f261f9.zip | |
feat: add App Intents for Hutch navigation
- add read-only App Intents for core Hutch workflows
- route intents through the existing Hutch navigation/deep-link model
- expose Work Queue, Recent Activity, System Status, pinned resources, projects, failed builds, assigned tickets, saved searches, and search where supported
- keep App Intents non-mutating for the initial implementation
- preserve existing widget, Safari extension, and hutch:// routing behavior
References: https://todo.sr.ht/~ccleberg/hutch/71
Diffstat (limited to 'Hutch/Views')
| -rw-r--r-- | Hutch/Views/Builds/BuildListView.swift | 9 | ||||
| -rw-r--r-- | Hutch/Views/Builds/BuildListViewModel.swift | 8 | ||||
| -rw-r--r-- | Hutch/Views/Home/HomeView.swift | 4 | ||||
| -rw-r--r-- | Hutch/Views/Lookup/LookupView.swift | 26 | ||||
| -rw-r--r-- | Hutch/Views/More/MoreView.swift | 2 | ||||
| -rw-r--r-- | Hutch/Views/Work/WorkView.swift | 28 |
6 files changed, 60 insertions, 17 deletions
diff --git a/Hutch/Views/Builds/BuildListView.swift b/Hutch/Views/Builds/BuildListView.swift index 20414d6..cc31e67 100644 --- a/Hutch/Views/Builds/BuildListView.swift +++ b/Hutch/Views/Builds/BuildListView.swift @@ -124,6 +124,10 @@ struct BuildListView: View { let vm = BuildListViewModel(client: appState.client, defaults: appState.accountDefaults) vm.repoFilter = savedRepoFilter vm.lookbackDays = lookbackDays + if let pendingFilter = appState.pendingBuildListFilter { + vm.filter = pendingFilter + appState.pendingBuildListFilter = nil + } viewModel = vm await vm.loadJobs() } @@ -134,6 +138,11 @@ struct BuildListView: View { .onChange(of: lookbackDays) { _, newValue in viewModel?.lookbackDays = newValue } + .onChange(of: appState.pendingBuildListFilter) { _, newValue in + guard let newValue else { return } + viewModel?.filter = newValue + appState.pendingBuildListFilter = nil + } .onDisappear { viewModel?.stopAutoRefresh() } diff --git a/Hutch/Views/Builds/BuildListViewModel.swift b/Hutch/Views/Builds/BuildListViewModel.swift index 42500a8..eb01d9e 100644 --- a/Hutch/Views/Builds/BuildListViewModel.swift +++ b/Hutch/Views/Builds/BuildListViewModel.swift @@ -21,6 +21,7 @@ private struct SubmittedJob: Decodable, Sendable { enum BuildListFilter: String, CaseIterable, Sendable { case attention = "Attention" + case failed = "Failed" case active = "Active" case all = "All" } @@ -413,6 +414,13 @@ final class BuildListViewModel { case .success, .cancelled: return false } + case .failed: + switch job.status { + case .failed, .timeout: + return true + case .success, .cancelled, .running, .queued, .pending: + return false + } case .active: switch job.status { case .running, .queued, .pending: diff --git a/Hutch/Views/Home/HomeView.swift b/Hutch/Views/Home/HomeView.swift index 9e06a00..409d095 100644 --- a/Hutch/Views/Home/HomeView.swift +++ b/Hutch/Views/Home/HomeView.swift @@ -106,7 +106,7 @@ struct HomeView: View { private func workSection(_ viewModel: HomeViewModel) -> some View { Section("Work") { - NavigationLink(value: HomeRoute.work) { + NavigationLink(value: HomeRoute.work(scope: .all)) { HomeSummaryRow( title: workTitle(viewModel), summary: workSummary(viewModel), @@ -424,7 +424,7 @@ struct HomeView: View { } enum HomeRoute: Hashable { - case work + case work(scope: HutchWorkQueueScope) } private enum HomeSummaryEmphasis { diff --git a/Hutch/Views/Lookup/LookupView.swift b/Hutch/Views/Lookup/LookupView.swift index 55c421b..6f7ebe1 100644 --- a/Hutch/Views/Lookup/LookupView.swift +++ b/Hutch/Views/Lookup/LookupView.swift @@ -92,11 +92,17 @@ final class LookupViewModel { ) } - init(client: SRHTClient, appState: AppState, defaults: UserDefaults = .standard) { + init( + client: SRHTClient, + appState: AppState, + defaults: UserDefaults = .standard, + initialQuery: String = "" + ) { self.client = client self.appState = appState self.defaults = defaults self.history = LookupHistoryStore.load(defaults: defaults) + self.inputText = initialQuery.trimmingCharacters(in: .whitespacesAndNewlines) } func lookup() async { @@ -329,6 +335,11 @@ final class LookupViewModel { struct LookupView: View { @Environment(AppState.self) private var appState @State private var viewModel: LookupViewModel? + private let initialQuery: String + + init(initialQuery: String = "") { + self.initialQuery = initialQuery + } var body: some View { Group { @@ -341,7 +352,12 @@ struct LookupView: View { .navigationTitle("Look Up") .task { if viewModel == nil { - viewModel = LookupViewModel(client: appState.client, appState: appState, defaults: appState.accountDefaults) + viewModel = LookupViewModel( + client: appState.client, + appState: appState, + defaults: appState.accountDefaults, + initialQuery: initialQuery + ) } } } @@ -427,8 +443,8 @@ struct LookupView: View { } .navigationDestination(for: MoreRoute.self) { route in switch route { - case .lookup: - LookupView() + case .lookup(let query): + LookupView(initialQuery: query ?? "") case .projects: ProjectsListView() case .lists: @@ -445,6 +461,8 @@ struct LookupView: View { AboutView() case .userProfile(let owner): UserProfileDeepLinkView(owner: owner) + case .projectDashboard(let id, let title): + ProjectDashboardDeepLinkView(projectID: id, title: title) case .mailingList(let mailingList): MailingListDetailView(mailingList: mailingList) case .thread(let thread): diff --git a/Hutch/Views/More/MoreView.swift b/Hutch/Views/More/MoreView.swift index 5c757ef..ad02077 100644 --- a/Hutch/Views/More/MoreView.swift +++ b/Hutch/Views/More/MoreView.swift @@ -13,7 +13,7 @@ struct MoreView: View { var body: some View { List { Section("Search") { - NavigationLink(value: MoreRoute.lookup) { + NavigationLink(value: MoreRoute.lookup(query: nil)) { Label("Look Up", systemImage: "magnifyingglass") } .themedRow() diff --git a/Hutch/Views/Work/WorkView.swift b/Hutch/Views/Work/WorkView.swift index 8b932dd..6ba1d0c 100644 --- a/Hutch/Views/Work/WorkView.swift +++ b/Hutch/Views/Work/WorkView.swift @@ -1,20 +1,28 @@ import SwiftUI -struct WorkView: View { - private enum Scope: String, CaseIterable, Identifiable { - case all = "All" - case unread = "Unread" - case assigned = "Assigned" - - var id: String { rawValue } +extension HutchWorkQueueScope: Identifiable { + var id: String { rawValue } + + var displayName: String { + switch self { + case .all: "All" + case .unread: "Unread" + case .assigned: "Assigned" + } } +} +struct WorkView: View { @AppStorage(AppStorageKeys.swipeActionsEnabled, store: .standard) private var swipeActionsEnabled = true @Environment(AppState.self) private var appState @Environment(\.isAMOLEDTheme) private var isAMOLED @Environment(\.scenePhase) private var scenePhase @State private var viewModel: HomeViewModel? - @State private var scope: Scope = .all + @State private var scope: HutchWorkQueueScope + + init(initialScope: HutchWorkQueueScope = .all) { + _scope = State(initialValue: initialScope) + } var body: some View { Group { @@ -106,8 +114,8 @@ struct WorkView: View { private var scopeSection: some View { Section { Picker("Scope", selection: $scope) { - ForEach(Scope.allCases) { scope in - Text(scope.rawValue).tag(scope) + ForEach(HutchWorkQueueScope.allCases) { scope in + Text(scope.displayName).tag(scope) } } .pickerStyle(.segmented) |
