feat: add 8h/12h intervals + daily schedule refresh mode
- Added 8-hour and 12-hour polling interval options - Added 'Daily schedule' mode (pollIntervalSeconds = -1): fires at specific wall-clock times configured in dailyRefreshTimes (e.g. 08:00, 14:00) - PollerService: in daily mode, checks every 60s and triggers fetch when current time matches a scheduled time (with 2-min dedup window) - Settings UI: shows HH:mm text field when daily schedule is selected - AppConfig: added dailyRefreshTimes: [String] field Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
64a35752c5
commit
b81d3edf42
|
|
@ -6,6 +6,9 @@ struct AppConfig: Codable, Equatable {
|
|||
var assigneeName: String = ""
|
||||
var fieldMappings: FieldMappings = FieldMappings()
|
||||
var pollIntervalSeconds: Int = 300
|
||||
/// Daily refresh times in "HH:mm" format, e.g. ["08:00", "14:00"].
|
||||
/// Only used when pollIntervalSeconds == -1.
|
||||
var dailyRefreshTimes: [String] = []
|
||||
var showFloatingWidget: Bool = false
|
||||
var launchAtLogin: Bool = true
|
||||
var feishuBaseDomain: String = "xorbitlab.feishu.cn"
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ final class PollerService {
|
|||
|
||||
private var timer: Timer?
|
||||
private var isFetching = false
|
||||
private var lastFetchTime: Date?
|
||||
|
||||
private(set) var isRunning = false
|
||||
|
||||
|
|
@ -32,10 +33,18 @@ final class PollerService {
|
|||
|
||||
Task { await performFetch() }
|
||||
|
||||
timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] _ in
|
||||
Task { await self?.performFetch() }
|
||||
if interval < 0 {
|
||||
// Daily schedule mode: check every 60s, fire at configured times
|
||||
timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { [weak self] _ in
|
||||
self?.checkDailySchedule()
|
||||
}
|
||||
timer?.tolerance = 5
|
||||
} else {
|
||||
timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] _ in
|
||||
Task { await self?.performFetch() }
|
||||
}
|
||||
timer?.tolerance = interval * 0.1
|
||||
}
|
||||
timer?.tolerance = interval * 0.1
|
||||
}
|
||||
|
||||
func stop() {
|
||||
|
|
@ -53,11 +62,34 @@ final class PollerService {
|
|||
await performFetch()
|
||||
}
|
||||
|
||||
// MARK: - Daily schedule
|
||||
|
||||
private func checkDailySchedule() {
|
||||
guard let config = configService.config else { return }
|
||||
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "HH:mm"
|
||||
let now = formatter.string(from: Date())
|
||||
|
||||
guard config.dailyRefreshTimes.contains(now) else { return }
|
||||
|
||||
// Avoid firing twice in the same minute
|
||||
if let last = lastFetchTime, Date().timeIntervalSince(last) < 120 {
|
||||
return
|
||||
}
|
||||
|
||||
Task { await performFetch() }
|
||||
}
|
||||
|
||||
// MARK: - Fetch
|
||||
|
||||
private func performFetch() async {
|
||||
guard !isFetching else { return }
|
||||
isFetching = true
|
||||
defer { isFetching = false }
|
||||
|
||||
lastFetchTime = Date()
|
||||
|
||||
await MainActor.run {
|
||||
bugStore.setLoading(true)
|
||||
bugStore.setError(nil)
|
||||
|
|
|
|||
|
|
@ -51,7 +51,25 @@ struct SettingsView: View {
|
|||
Text("1 hour").tag(3_600)
|
||||
Text("2 hours").tag(7_200)
|
||||
Text("4 hours").tag(14_400)
|
||||
Text("8 hours").tag(28_800)
|
||||
Text("12 hours").tag(43_200)
|
||||
Text("1 day").tag(86_400)
|
||||
Divider()
|
||||
Text("Daily schedule").tag(-1)
|
||||
}
|
||||
|
||||
if config.pollIntervalSeconds == -1 {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("Refresh at these times (comma-separated, 24h):")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
TextField("08:00, 14:00", text: dailyTimesBinding)
|
||||
.font(.caption)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
Text("Example: 08:00, 14:00, 20:00")
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -147,6 +165,20 @@ struct SettingsView: View {
|
|||
.padding(.vertical, 2)
|
||||
}
|
||||
|
||||
// MARK: - Daily times binding
|
||||
|
||||
private var dailyTimesBinding: Binding<String> {
|
||||
Binding<String>(
|
||||
get: { config.dailyRefreshTimes.joined(separator: ", ") },
|
||||
set: { newValue in
|
||||
config.dailyRefreshTimes = newValue
|
||||
.components(separatedBy: ",")
|
||||
.map { $0.trimmingCharacters(in: .whitespaces) }
|
||||
.filter { !$0.isEmpty }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
private func save() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue