bugger/Sources/Services/PollerService.swift

101 lines
2.7 KiB
Swift

import AppKit
import Foundation
@Observable
final class PollerService {
static let shared = PollerService()
private let bugStore = BugStore.shared
private let feishuService = FeishuService()
private let tokenManager = TokenManager.shared
private let configService = AppStateService.shared
private var timer: Timer?
private var isFetching = false
private(set) var isRunning = false
private init() {}
func startIfConfigured() async {
guard let config = configService.config,
config.isConfigured,
tokenManager.isAuthenticated else {
return
}
start(interval: TimeInterval(config.pollIntervalSeconds))
}
func start(interval: TimeInterval) {
guard !isRunning else { return }
isRunning = true
Task { await performFetch() }
timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] _ in
Task { await self?.performFetch() }
}
timer?.tolerance = interval * 0.1
}
func stop() {
timer?.invalidate()
timer = nil
isRunning = false
}
func restart(interval: TimeInterval) {
stop()
start(interval: interval)
}
func fetchNow() async {
await performFetch()
}
private func performFetch() async {
guard !isFetching else { return }
isFetching = true
defer { isFetching = false }
await MainActor.run {
bugStore.setLoading(true)
bugStore.setError(nil)
}
do {
let config = try getConfig()
let token = try await tokenManager.getAccessToken()
let assignee = try await tokenManager.resolveAssigneeName(config: config, accessToken: token)
let bugs = try await feishuService.fetchBugs(
config: config,
assigneeName: assignee,
accessToken: token
)
await MainActor.run {
bugStore.update(with: bugs)
bugStore.setLoading(false)
AppDelegate.shared?.updateBadge(count: bugStore.unseenActiveCount)
}
} catch FeishuError.unauthorized {
await MainActor.run {
bugStore.setLoading(false)
tokenManager.clearTokens()
}
} catch {
await MainActor.run {
bugStore.setLoading(false)
bugStore.setError(error)
}
}
}
private func getConfig() throws -> AppConfig {
guard let config = configService.config, config.isConfigured else {
throw FeishuError.notConfigured
}
return config
}
}