Files
ios-gitea-prs/GiteaPR/Settings/SettingsStore.swift
T
Hermes bae531f850
Build iOS App / Build .ipa and publish source (push) Successful in 31s
Add push-equivalent notifications via background polling; bump to 3.6
Sideloaded apps can't use APNs (no push entitlement without Apple signing),
so deliver notifications via an iOS BGAppRefreshTask that polls the monitored
repos every ~15 min and fires local notifications (no Apple push needed):
- new PRs that appear in the open set
- CI status changes on existing PRs

Persistence: NotificationState tracks announced PR ids + last-known CI status
so we only fire on actual changes, not every poll. AppDelegate registers the
background task and reschedules the next one. Info.plist gets UIBackgroundModes
fetch + the BGTask identifier. Settings gains a Notifications toggle; also
poll after a foreground inbox load so state seeds and changes are caught
promptly. Bump to 3.6.
2026-08-24 08:18:29 +00:00

76 lines
2.9 KiB
Swift

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") }
}
/// Set when a PR is merged, so the inbox knows to reload on next appearance
/// (otherwise a just-merged PR could linger as stale in the list).
@Published var inboxNeedsRefresh = false
/// Whether background polling for push-equivalent notifications is on.
@Published var notificationsEnabled: Bool {
didSet { UserDefaults.standard.set(notificationsEnabled,
forKey: "notificationsEnabled") }
}
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") ?? []
notificationsEnabled = UserDefaults.standard.object(forKey: "notificationsEnabled") as? Bool ?? false
}
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 = ""
}
}