@@ -2,7 +2,7 @@ import SwiftUI
22import AppKit
33import Observation
44
5- private let refreshIntervalSeconds : UInt64 = 15
5+ private let refreshIntervalSeconds : UInt64 = 30
66private let nanosPerSecond : UInt64 = 1_000_000_000
77private let refreshIntervalNanos : UInt64 = refreshIntervalSeconds * nanosPerSecond
88private let statusItemWidth : CGFloat = NSStatusItem . variableLength
@@ -28,7 +28,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
2828 private var popover : NSPopover !
2929 private let store = AppStore ( )
3030 let updateChecker = UpdateChecker ( )
31- private var dispatchTimer : DispatchSourceTimer ?
3231 /// Held for the lifetime of the app to opt out of App Nap and Automatic Termination.
3332 private var backgroundActivity : NSObjectProtocol ?
3433
@@ -104,7 +103,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
104103 <string>ObjC.import( " Foundation " ); $.NSDistributedNotificationCenter.defaultCenter.postNotificationNameObjectUserInfoDeliverImmediately( " com.codeburn.refresh " , $(), $(), true)</string>
105104 </array>
106105 <key>StartInterval</key>
107- <integer>15 </integer>
106+ <integer>30 </integer>
108107 <key>RunAtLoad</key>
109108 <true/>
110109</dict>
@@ -158,10 +157,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
158157 }
159158 }
160159
160+ private var lastRefreshTime : Date = . distantPast
161+
161162 private func forceRefresh( ) {
163+ let now = Date ( )
164+ guard now. timeIntervalSince ( lastRefreshTime) > 5 else { return }
165+ lastRefreshTime = now
166+
162167 Task {
163- await store. refreshQuietly ( period: . today)
164- refreshStatusButton ( )
165168 await store. refresh ( includeOptimize: true )
166169 refreshStatusButton ( )
167170 }
@@ -189,33 +192,11 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
189192 }
190193 }
191194
192- func applicationWillTerminate( _ notification: Notification ) {
193- dispatchTimer? . cancel ( )
194- }
195-
196195 private func startRefreshLoop( ) {
197- // Initial fetch on launch
198196 Task {
199- await store. refreshQuietly ( period: . today)
200- refreshStatusButton ( )
201197 await store. refresh ( includeOptimize: true )
202198 refreshStatusButton ( )
203199 }
204-
205- // Use DispatchSourceTimer for more reliable background execution
206- let timer = DispatchSource . makeTimerSource ( queue: . main)
207- timer. schedule ( deadline: . now( ) + . seconds( Int ( refreshIntervalSeconds) ) , repeating: . seconds( Int ( refreshIntervalSeconds) ) , leeway: . seconds( 1 ) )
208- timer. setEventHandler { [ weak self] in
209- guard let self = self else { return }
210- Task { @MainActor in
211- await self . store. refreshQuietly ( period: . today)
212- self . refreshStatusButton ( )
213- await self . store. refresh ( includeOptimize: true )
214- self . refreshStatusButton ( )
215- }
216- }
217- timer. resume ( )
218- dispatchTimer = timer
219200 }
220201
221202 private func observeStore( ) {
@@ -285,11 +266,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
285266 attributes: [ . font: font, . foregroundColor: color, . baselineOffset: - 1.0 ]
286267 ) )
287268 button. attributedTitle = composed
288- // Force immediate redraw. NSStatusItem sometimes defers the status bar paint for an
289- // accessory app that is not foreground, so the label visually freezes until the user
290- // opens the popover (which triggers NSApp.activate + a forced redraw cycle).
291- button. needsDisplay = true
292- button. display ( )
293269 }
294270
295271 // MARK: - Popover
0 commit comments