native macOS codings agent orchestrator prowl.onev.cat
Something went wrong. Try again.
Swift
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214import ComposableArchitectureimport SwiftUI
@MainActor @Observablefinal class GithubSettingsViewModel { enum State: Equatable { case loading case unavailable case outdated case notAuthenticated case authenticated(GithubAuthStatusSnapshot) case error(String) }
var state: State = .loading
@ObservationIgnored @Dependency(GithubIntegrationClient.self) private var githubIntegration
@ObservationIgnored @Dependency(GithubCLIClient.self) private var githubCLI
func load() async { state = .loading let isAvailable = await githubIntegration.isAvailable() guard isAvailable else { state = .unavailable return }
do { let snapshot = try await githubCLI.authStatusSnapshot() if snapshot.allAccounts.contains(where: { $0.active }) { state = .authenticated(snapshot) } else { state = .notAuthenticated } } catch let error as GithubCLIError { switch error { case .outdated: state = .outdated case .unavailable: state = .unavailable case .commandFailed(let message): state = .error(message) } } catch { state = .error(error.localizedDescription) } }}
struct GithubSettingsView: View { @Bindable var store: StoreOf<SettingsFeature> @State private var viewModel = GithubSettingsViewModel()
var body: some View { VStack(alignment: .leading, spacing: 0) { Form { Section("GitHub integration") { Toggle( "Enable GitHub integration", isOn: $store.githubIntegrationEnabled ) .help("Enable GitHub integration") } Section("GitHub CLI") { switch viewModel.state { case .loading: HStack(spacing: 8) { ProgressView() .controlSize(.small) Text("Checking GitHub CLI...") .foregroundStyle(.secondary) }
case .unavailable: VStack(alignment: .leading, spacing: 8) { Label("GitHub integration unavailable", systemImage: "xmark.circle") .foregroundStyle(.red) Text("Enable GitHub integration and install gh CLI to use pull request checks.") .foregroundStyle(.secondary) .font(.callout) }
case .notAuthenticated: VStack(alignment: .leading, spacing: 8) { Label("Not authenticated", systemImage: "exclamationmark.triangle") .foregroundStyle(.orange) Text("Run `gh auth login` in terminal to authenticate.") .foregroundStyle(.secondary) .font(.callout) }
case .outdated: VStack(alignment: .leading, spacing: 8) { Label("GitHub CLI outdated", systemImage: "exclamationmark.triangle") .foregroundStyle(.orange) Text("Update GitHub CLI to the latest version to use GitHub integration.") .foregroundStyle(.secondary) .font(.callout) }
case .authenticated(let snapshot): ForEach(snapshot.sortedHosts, id: \.self) { host in let accounts = snapshot.accounts(on: host) VStack(alignment: .leading, spacing: 8) { LabeledContent("Host") { Text(host) .font(.body) } ForEach(accounts) { account in HStack { Text(account.login) if account.active { Text("Active") .foregroundStyle(.secondary) } Spacer() if let status = GithubAuthAccountStatusDisplay(account.state) { Text(status.title) .foregroundStyle(status.style) .help(status.help) } } .font(.body) } } }
case .error(let message): VStack(alignment: .leading, spacing: 8) { Label("Error checking status", systemImage: "exclamationmark.triangle") .foregroundStyle(.red) Text(message) .foregroundStyle(.secondary) .font(.callout) } } } Section("Pull Requests") { Picker(selection: $store.pullRequestMergeStrategy) { ForEach(PullRequestMergeStrategy.allCases) { strategy in Text(strategy.title).tag(strategy) } } label: { Text("Merge strategy") Text("Default strategy when merging PRs from the command palette.") } } } .formStyle(.grouped)
switch viewModel.state { case .unavailable: HStack { Button("Get GitHub CLI") { NSWorkspace.shared.open(URL(string: "https://cli.github.com")!) } .help("Open GitHub CLI website") Spacer() } .padding(.top) case .outdated: HStack { Button("Update GitHub CLI") { NSWorkspace.shared.open(URL(string: "https://cli.github.com")!) } .help("Open GitHub CLI website") Spacer() } .padding(.top) default: EmptyView() } } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) .task { await viewModel.load() } .onChange(of: store.githubIntegrationEnabled) { _, _ in Task { await viewModel.load() } } }}
private struct GithubAuthAccountStatusDisplay { let title: String let help: String let style: Color
init?(_ state: String?) { guard let state else { return nil } switch state.lowercased() { case "success": return nil case "timeout": title = String(localized: "Timed out") help = String(localized: "gh could not verify this account in time.") style = .orange case "error": title = String(localized: "Needs attention") help = String(localized: "gh could not verify this account.") style = .red default: title = String(localized: "Check failed") help = String(localized: "gh reported an authentication issue.") style = .orange } }}