diff --git a/Frameworks/Account/AccountManager.swift b/Frameworks/Account/AccountManager.swift index 744b2239a..52bdc6046 100644 --- a/Frameworks/Account/AccountManager.swift +++ b/Frameworks/Account/AccountManager.swift @@ -150,6 +150,11 @@ public final class AccountManager: UnreadCountProvider { activeAccounts.forEach { $0.refreshAll() } } + public func syncArticleStatusAll() { + + activeAccounts.forEach { $0.syncArticleStatus() } + } + public func anyAccountHasAtLeastOneFeed() -> Bool { for account in activeAccounts { diff --git a/Mac/AppDelegate.swift b/Mac/AppDelegate.swift index 41868c551..e80c54145 100644 --- a/Mac/AppDelegate.swift +++ b/Mac/AppDelegate.swift @@ -23,12 +23,17 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, var authorAvatarDownloader: AuthorAvatarDownloader! var feedIconDownloader: FeedIconDownloader! var appName: String! - var refreshTimer: RefreshTimer? + + var refreshTimer: AccountRefreshTimer? + var syncTimer: ArticleStatusSyncTimer? + var shuttingDown = false { didSet { if shuttingDown { refreshTimer?.shuttingDown = shuttingDown refreshTimer?.invalidate() + syncTimer?.shuttingDown = shuttingDown + syncTimer?.invalidate() } } } @@ -153,17 +158,20 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, self.toggleInspectorWindow(self) } - refreshTimer = RefreshTimer(delegate: self) + refreshTimer = AccountRefreshTimer() + syncTimer = ArticleStatusSyncTimer() #if RELEASE debugMenuItem.menu?.removeItem(debugMenuItem) DispatchQueue.main.async { self.refreshTimer!.timedRefresh(nil) + self.syncTimer!.timedRefresh(nil) } #endif #if DEBUG refreshTimer!.update() + syncTimer!.update() #endif #if !MAC_APP_STORE @@ -191,6 +199,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, // It’s possible there’s a refresh timer set to go off in the past. // In that case, refresh now and update the timer. refreshTimer?.fireOldTimer() + syncTimer?.fireOldTimer() } func applicationDidResignActive(_ notification: Notification) { @@ -534,11 +543,3 @@ extension AppDelegate : ScriptingAppDelegate { return self.scriptingMainWindowController?.scriptingSelectedArticles ?? [] } } - -extension AppDelegate: RefreshTimerDelegate { - - func refresh() { - AccountManager.shared.refreshAll() - } - -} diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 2972abf89..67965f7bf 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -37,8 +37,8 @@ 5183CCE3226F314C0010922C /* ProgressTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE2226F314C0010922C /* ProgressTableViewController.swift */; }; 5183CCE5226F4DFA0010922C /* RefreshInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */; }; 5183CCE6226F4E110010922C /* RefreshInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */; }; - 5183CCE8226F68D90010922C /* RefreshTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE7226F68D90010922C /* RefreshTimer.swift */; }; - 5183CCE9226F68D90010922C /* RefreshTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE7226F68D90010922C /* RefreshTimer.swift */; }; + 5183CCE8226F68D90010922C /* AccountRefreshTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */; }; + 5183CCE9226F68D90010922C /* AccountRefreshTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */; }; 5183CCED22711DCE0010922C /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5183CCEC22711DCE0010922C /* Settings.storyboard */; }; 5183CCEF227125970010922C /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCEE227125970010922C /* SettingsViewController.swift */; }; 519B8D332143397200FA689C /* SharingServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519B8D322143397200FA689C /* SharingServiceDelegate.swift */; }; @@ -115,6 +115,8 @@ 51C452B42265141B00C03939 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51C452B32265141B00C03939 /* WebKit.framework */; }; 51C452B82265178500C03939 /* styleSheet.css in Resources */ = {isa = PBXBuildFile; fileRef = 51C452B72265178500C03939 /* styleSheet.css */; }; 51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84702AA31FA27AC0006B8943 /* MarkStatusCommand.swift */; }; + 51E595A5228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E595A4228CC36500FCC42B /* ArticleStatusSyncTimer.swift */; }; + 51E595A6228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E595A4228CC36500FCC42B /* ArticleStatusSyncTimer.swift */; }; 51EC114C2149FE3300B296E3 /* FolderTreeMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51EC114B2149FE3300B296E3 /* FolderTreeMenu.swift */; }; 51EF0F77227716200050506E /* FaviconGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51EF0F76227716200050506E /* FaviconGenerator.swift */; }; 51EF0F79227716380050506E /* ColorHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51EF0F78227716380050506E /* ColorHash.swift */; }; @@ -676,7 +678,7 @@ 5183CCDE226F1FCC0010922C /* UINavigationController+Progress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationController+Progress.swift"; sourceTree = ""; }; 5183CCE2226F314C0010922C /* ProgressTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressTableViewController.swift; sourceTree = ""; }; 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshInterval.swift; sourceTree = ""; }; - 5183CCE7226F68D90010922C /* RefreshTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshTimer.swift; sourceTree = ""; }; + 5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountRefreshTimer.swift; sourceTree = ""; }; 5183CCEC22711DCE0010922C /* Settings.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; }; 5183CCEE227125970010922C /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; 519B8D322143397200FA689C /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = ""; }; @@ -702,6 +704,7 @@ 51C4528B2265095F00C03939 /* AddFolderViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddFolderViewController.swift; sourceTree = ""; }; 51C452B32265141B00C03939 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; }; 51C452B72265178500C03939 /* styleSheet.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = styleSheet.css; sourceTree = ""; }; + 51E595A4228CC36500FCC42B /* ArticleStatusSyncTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleStatusSyncTimer.swift; sourceTree = ""; }; 51EC114B2149FE3300B296E3 /* FolderTreeMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FolderTreeMenu.swift; path = AddFeed/FolderTreeMenu.swift; sourceTree = ""; }; 51EF0F76227716200050506E /* FaviconGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconGenerator.swift; sourceTree = ""; }; 51EF0F78227716380050506E /* ColorHash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorHash.swift; sourceTree = ""; }; @@ -1014,13 +1017,14 @@ path = Progress; sourceTree = ""; }; - 5183CCEA226F70350010922C /* Refresh */ = { + 5183CCEA226F70350010922C /* Timer */ = { isa = PBXGroup; children = ( 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */, - 5183CCE7226F68D90010922C /* RefreshTimer.swift */, + 5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */, + 51E595A4228CC36500FCC42B /* ArticleStatusSyncTimer.swift */, ); - path = Refresh; + path = Timer; sourceTree = ""; }; 5183CCEB227117C70010922C /* Settings */ = { @@ -1563,7 +1567,7 @@ 848F6AE31FC29CFA002D422E /* Favicons */, 845213211FCA5B10003B6E93 /* Images */, 8426119C1FCB6ED40086A189 /* HTMLMetadata */, - 5183CCEA226F70350010922C /* Refresh */, + 5183CCEA226F70350010922C /* Timer */, 849A97561ED9EB0D007D329B /* Data */, 512E08DD22687FA000BDCFDD /* Tree */, 849A97961ED9EFAA007D329B /* Extensions */, @@ -1919,12 +1923,12 @@ ORGANIZATIONNAME = "Ranchero Software"; TargetAttributes = { 6581C73220CED60000F4AD34 = { - DevelopmentTeam = M8L2WTLA8W; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Manual; }; 840D617B2029031C009BC708 = { CreatedOnToolsVersion = 9.3; - DevelopmentTeam = M8L2WTLA8W; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.BackgroundModes = { @@ -1940,7 +1944,7 @@ }; 849C645F1ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = M8L2WTLA8W; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Manual; SystemCapabilities = { com.apple.HardenedRuntime = { @@ -1950,7 +1954,7 @@ }; 849C64701ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = 9C84TZ7Q6Z; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; TestTargetID = 849C645F1ED37A5D003D8FC0; }; @@ -2342,6 +2346,7 @@ 51C452AF2265108300C03939 /* ArticleArray.swift in Sources */, 51C4528E2265099C00C03939 /* SmartFeedsController.swift in Sources */, 51C4529C22650A1000C03939 /* SingleFaviconDownloader.swift in Sources */, + 51E595A6228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */, 51C45290226509C100C03939 /* PseudoFeed.swift in Sources */, 51C452A922650DC600C03939 /* ArticleRenderer.swift in Sources */, 5115CAF42266301400B21BCE /* AddContainerViewController.swift in Sources */, @@ -2351,7 +2356,7 @@ 51C452AC22650FD200C03939 /* AppNotifications.swift in Sources */, 51EF0F7E2277A57D0050506E /* MasterTimelineAccessibilityCellLayout.swift in Sources */, 51C452762265091600C03939 /* MasterTimelineViewController.swift in Sources */, - 5183CCE9226F68D90010922C /* RefreshTimer.swift in Sources */, + 5183CCE9226F68D90010922C /* AccountRefreshTimer.swift in Sources */, 51C452882265093600C03939 /* AddFeedViewController.swift in Sources */, 51C4529B22650A1000C03939 /* FaviconDownloader.swift in Sources */, 5183CCE3226F314C0010922C /* ProgressTableViewController.swift in Sources */, @@ -2410,6 +2415,7 @@ D5907D7F2004AC00005947E5 /* NSApplication+Scriptability.swift in Sources */, 8405DD9C22153BD7008CE1BF /* NSView-Extensions.swift in Sources */, 849A979F1ED9F130007D329B /* SidebarCell.swift in Sources */, + 51E595A5228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */, 849A97651ED9EB96007D329B /* FeedTreeControllerDelegate.swift in Sources */, 849A97671ED9EB96007D329B /* UnreadCountView.swift in Sources */, 840BEE4121D70E64009BBAFA /* CrashReportWindowController.swift in Sources */, @@ -2448,7 +2454,7 @@ 849A97981ED9EFAA007D329B /* Node-Extensions.swift in Sources */, 849EE70F203919360082A1EA /* AppImages.swift in Sources */, 849A97531ED9EAC0007D329B /* AddFeedController.swift in Sources */, - 5183CCE8226F68D90010922C /* RefreshTimer.swift in Sources */, + 5183CCE8226F68D90010922C /* AccountRefreshTimer.swift in Sources */, 849A97831ED9EC63007D329B /* SidebarStatusBarView.swift in Sources */, 84F2D5381FC22FCC00998D64 /* TodayFeedDelegate.swift in Sources */, 841ABA5E20145E9200980E11 /* FolderInspectorViewController.swift in Sources */, diff --git a/Shared/Refresh/RefreshTimer.swift b/Shared/Timer/AccountRefreshTimer.swift similarity index 85% rename from Shared/Refresh/RefreshTimer.swift rename to Shared/Timer/AccountRefreshTimer.swift index a1d047623..91c294f20 100644 --- a/Shared/Refresh/RefreshTimer.swift +++ b/Shared/Timer/AccountRefreshTimer.swift @@ -7,25 +7,16 @@ // import Foundation +import Account -protocol RefreshTimerDelegate: class { - func refresh() -} - -class RefreshTimer { +class AccountRefreshTimer { var shuttingDown = false - - private weak var delegate: RefreshTimerDelegate? - + private var internalTimer: Timer? private var lastTimedRefresh: Date? private let launchTime = Date() - init(delegate: RefreshTimerDelegate) { - self.delegate = delegate - } - func fireOldTimer() { if let timer = internalTimer { if timer.fireDate < Date() { @@ -70,17 +61,20 @@ class RefreshTimer { let timer = Timer(fireAt: nextRefreshTime, interval: 0, target: self, selector: #selector(timedRefresh(_:)), userInfo: nil, repeats: false) RunLoop.main.add(timer, forMode: .common) internalTimer = timer - print("Next refresh date: \(nextRefreshTime)") } @objc func timedRefresh(_ sender: Timer?) { + guard !shuttingDown else { return } + lastTimedRefresh = Date() update() - delegate?.refresh() + + AccountManager.shared.refreshAll() + } } diff --git a/Shared/Timer/ArticleStatusSyncTimer.swift b/Shared/Timer/ArticleStatusSyncTimer.swift new file mode 100644 index 000000000..32e3cf604 --- /dev/null +++ b/Shared/Timer/ArticleStatusSyncTimer.swift @@ -0,0 +1,75 @@ +// +// ArticleStatusSyncTimer.swift +// NetNewsWire +// +// Created by Maurice Parker on 5/15/19. +// Copyright © 2019 Ranchero Software. All rights reserved. +// + +import Foundation +import Account + +class ArticleStatusSyncTimer { + + private static let intervalSeconds = Double(120) + + var shuttingDown = false + + private var internalTimer: Timer? + private var lastTimedRefresh: Date? + private let launchTime = Date() + + func fireOldTimer() { + if let timer = internalTimer { + if timer.fireDate < Date() { + timedRefresh(nil) + } + } + } + + func invalidate() { + guard let timer = internalTimer else { + return + } + if timer.isValid { + timer.invalidate() + } + internalTimer = nil + } + + func update() { + + guard !shuttingDown else { + return + } + + let lastRefreshDate = lastTimedRefresh ?? launchTime + var nextRefreshTime = lastRefreshDate.addingTimeInterval(ArticleStatusSyncTimer.intervalSeconds) + if nextRefreshTime < Date() { + nextRefreshTime = Date().addingTimeInterval(ArticleStatusSyncTimer.intervalSeconds) + } + if let currentNextFireDate = internalTimer?.fireDate, currentNextFireDate == nextRefreshTime { + return + } + + invalidate() + let timer = Timer(fireAt: nextRefreshTime, interval: 0, target: self, selector: #selector(timedRefresh(_:)), userInfo: nil, repeats: false) + RunLoop.main.add(timer, forMode: .common) + internalTimer = timer + + } + + @objc func timedRefresh(_ sender: Timer?) { + + guard !shuttingDown else { + return + } + + lastTimedRefresh = Date() + update() + + AccountManager.shared.syncArticleStatusAll() + + } + +} diff --git a/Shared/Refresh/RefreshInterval.swift b/Shared/Timer/RefreshInterval.swift similarity index 100% rename from Shared/Refresh/RefreshInterval.swift rename to Shared/Timer/RefreshInterval.swift