import Foundation import SwiftUI enum MergeGateMode: String, CaseIterable, Identifiable { case warn = "Warn on red (allow merge with confirm)" case block = "Block merge when CI is red" var id: String { rawValue } } /// Central settings: instance + token (Keychain) and app preferences. @MainActor final class SettingsStore: ObservableObject { static let shared = SettingsStore() @Published var instanceURL: String { didSet { KeychainHelper.set(instanceURL, key: "instanceURL") } } @Published var token: String { didSet { KeychainHelper.set(token, key: "token") } } @Published var mergeGateMode: MergeGateMode { didSet { UserDefaults.standard.set(mergeGateMode.rawValue, forKey: "mergeGateMode") } } @Published var deleteBranchAfterMerge: Bool { didSet { UserDefaults.standard.set(deleteBranchAfterMerge, forKey: "deleteBranchAfterMerge") } } /// Repos to monitor for the inbox. Empty = monitor all accessible repos. /// Stored as full names (e.g. "luis/config-nixos"). @Published var monitoredRepos: [String] { didSet { UserDefaults.standard.set(monitoredRepos, forKey: "monitoredRepos") } } private init() { instanceURL = KeychainHelper.get("instanceURL") ?? "https://gitea.bueso.eu" token = KeychainHelper.get("token") ?? "" let raw = UserDefaults.standard.string(forKey: "mergeGateMode") mergeGateMode = MergeGateMode(rawValue: raw ?? "") ?? .warn deleteBranchAfterMerge = UserDefaults.standard.bool(forKey: "deleteBranchAfterMerge") monitoredRepos = UserDefaults.standard.stringArray(forKey: "monitoredRepos") ?? [] } var isConfigured: Bool { !token.isEmpty && !instanceURL.isEmpty } var baseURL: URL? { URL(string: instanceURL.hasSuffix("/") ? String(instanceURL.dropLast()) : instanceURL) } func client() -> GiteaClient? { guard let base = baseURL else { return nil } return GiteaClient(instanceURL: base, token: token) } func logout() { KeychainHelper.delete("instanceURL") KeychainHelper.delete("token") instanceURL = "https://gitea.bueso.eu" token = "" } }