Separate FeedDownload into UpdateScheduler & WebFeed
This commit is contained in:
@@ -29,8 +29,9 @@
|
||||
54A07A82220E723D00082C51 /* MapUnreadTotal.m in Sources */ = {isa = PBXBuildFile; fileRef = 54A07A81220E723D00082C51 /* MapUnreadTotal.m */; };
|
||||
54A2D63922EF81A4007C61F3 /* QLOPML.qlgenerator in CopyFiles */ = {isa = PBXBuildFile; fileRef = 54A2D63822EF8193007C61F3 /* QLOPML.qlgenerator */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
54ACC28C21061B3C0020715F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 54ACC28B21061B3C0020715F /* main.m */; };
|
||||
54ACC29521061E270020715F /* FeedDownload.m in Sources */ = {isa = PBXBuildFile; fileRef = 54ACC29421061E270020715F /* FeedDownload.m */; };
|
||||
54ACC29521061E270020715F /* UpdateScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 54ACC29421061E270020715F /* UpdateScheduler.m */; };
|
||||
54ACC29821061FBA0020715F /* Preferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 54ACC29721061FBA0020715F /* Preferences.m */; };
|
||||
54AD4E0023005297000AE386 /* WebFeed.m in Sources */ = {isa = PBXBuildFile; fileRef = 54AD4DFF23005297000AE386 /* WebFeed.m */; };
|
||||
54B51704226DC339006C1B29 /* ModalFeedEditView.m in Sources */ = {isa = PBXBuildFile; fileRef = 54B51703226DC339006C1B29 /* ModalFeedEditView.m */; };
|
||||
54B517072270E990006C1B29 /* NSView+Ext.m in Sources */ = {isa = PBXBuildFile; fileRef = 54B517062270E92A006C1B29 /* NSView+Ext.m */; };
|
||||
54B749DA2204A85C0022CC6D /* BarStatusItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 54B749D92204A85C0022CC6D /* BarStatusItem.m */; };
|
||||
@@ -135,10 +136,12 @@
|
||||
54ACC28321061B3B0020715F /* DBv1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = DBv1.xcdatamodel; sourceTree = "<group>"; };
|
||||
54ACC28A21061B3C0020715F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
54ACC28B21061B3C0020715F /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
54ACC29321061E270020715F /* FeedDownload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FeedDownload.h; sourceTree = "<group>"; };
|
||||
54ACC29421061E270020715F /* FeedDownload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FeedDownload.m; sourceTree = "<group>"; };
|
||||
54ACC29321061E270020715F /* UpdateScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UpdateScheduler.h; sourceTree = "<group>"; };
|
||||
54ACC29421061E270020715F /* UpdateScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UpdateScheduler.m; sourceTree = "<group>"; };
|
||||
54ACC29621061FBA0020715F /* Preferences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Preferences.h; sourceTree = "<group>"; };
|
||||
54ACC29721061FBA0020715F /* Preferences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Preferences.m; sourceTree = "<group>"; };
|
||||
54AD4DFE23005297000AE386 /* WebFeed.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebFeed.h; sourceTree = "<group>"; };
|
||||
54AD4DFF23005297000AE386 /* WebFeed.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WebFeed.m; sourceTree = "<group>"; };
|
||||
54B51702226DC339006C1B29 /* ModalFeedEditView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ModalFeedEditView.h; sourceTree = "<group>"; };
|
||||
54B51703226DC339006C1B29 /* ModalFeedEditView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ModalFeedEditView.m; sourceTree = "<group>"; };
|
||||
54B517052270E8C6006C1B29 /* NSView+Ext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSView+Ext.h"; sourceTree = "<group>"; };
|
||||
@@ -201,8 +204,6 @@
|
||||
children = (
|
||||
54209E922117325100F3B5EF /* DrawImage.h */,
|
||||
54209E932117325100F3B5EF /* DrawImage.m */,
|
||||
54ACC29321061E270020715F /* FeedDownload.h */,
|
||||
54ACC29421061E270020715F /* FeedDownload.m */,
|
||||
54BB048721FD2AB500C303A5 /* NSDate+Ext.h */,
|
||||
54BB048821FD2AB500C303A5 /* NSDate+Ext.m */,
|
||||
54B517052270E8C6006C1B29 /* NSView+Ext.h */,
|
||||
@@ -290,6 +291,7 @@
|
||||
544936F721F1E51E00DEE9AA /* Helper */,
|
||||
541A90EF21257D4F002680A6 /* Status Bar Menu */,
|
||||
54A07A8322105E0800082C51 /* Core Data */,
|
||||
54AD4E04230084FD000AE386 /* Feed Import */,
|
||||
546FC44D2118B357007CC3A3 /* Preferences */,
|
||||
54ACC28A21061B3C0020715F /* Info.plist */,
|
||||
54F7101322EE0DDA006985D1 /* Artwork */,
|
||||
@@ -299,6 +301,19 @@
|
||||
path = baRSS;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
54AD4E04230084FD000AE386 /* Feed Import */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
54ACC29321061E270020715F /* UpdateScheduler.h */,
|
||||
54ACC29421061E270020715F /* UpdateScheduler.m */,
|
||||
54AD4DFE23005297000AE386 /* WebFeed.h */,
|
||||
54AD4DFF23005297000AE386 /* WebFeed.m */,
|
||||
54F6025B21C1D4170006D338 /* OpmlFile.h */,
|
||||
54F6025C21C1D4170006D338 /* OpmlFile.m */,
|
||||
);
|
||||
path = "Feed Import";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
54D857CF228022AB001BA1C8 /* General Tab */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -339,8 +354,6 @@
|
||||
546FC43C21188AD5007CC3A3 /* SettingsFeeds.m */,
|
||||
54D55D7122E624CD00057B98 /* SettingsFeeds+DragDrop.h */,
|
||||
54D55D7222E624CD00057B98 /* SettingsFeeds+DragDrop.m */,
|
||||
54F6025B21C1D4170006D338 /* OpmlFile.h */,
|
||||
54F6025C21C1D4170006D338 /* OpmlFile.m */,
|
||||
54E8831D211B509D00064188 /* ModalFeedEdit.h */,
|
||||
54E8831E211B509D00064188 /* ModalFeedEdit.m */,
|
||||
5478DF02225A7AE200D30C64 /* SettingsFeedsView.h */,
|
||||
@@ -491,6 +504,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
54AD4E0023005297000AE386 /* WebFeed.m in Sources */,
|
||||
54B51704226DC339006C1B29 /* ModalFeedEditView.m in Sources */,
|
||||
546A6A2C22C584AF0034E806 /* SettingsAppearanceView.m in Sources */,
|
||||
54E9CF32225914300023696F /* SettingsAbout.m in Sources */,
|
||||
@@ -500,7 +514,7 @@
|
||||
544B011D2114EE9100386E5C /* AppHook.m in Sources */,
|
||||
546FC44321189975007CC3A3 /* SettingsGeneral.m in Sources */,
|
||||
546A6A2922C583390034E806 /* SettingsGeneralView.m in Sources */,
|
||||
54ACC29521061E270020715F /* FeedDownload.m in Sources */,
|
||||
54ACC29521061E270020715F /* UpdateScheduler.m in Sources */,
|
||||
54BB048921FD2AB500C303A5 /* NSDate+Ext.m in Sources */,
|
||||
5477D34E21233C62002BA27F /* FeedGroup+Ext.m in Sources */,
|
||||
5478DF04225A7AE200D30C64 /* SettingsFeedsView.m in Sources */,
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
|
||||
#import "AppHook.h"
|
||||
#import "BarStatusItem.h"
|
||||
#import "FeedDownload.h"
|
||||
#import "WebFeed.h"
|
||||
#import "UpdateScheduler.h"
|
||||
#import "Preferences.h"
|
||||
#import "DrawImage.h"
|
||||
#import "SettingsFeeds+DragDrop.h"
|
||||
@@ -52,15 +53,15 @@
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
||||
[_statusItem asyncReloadUnreadCount];
|
||||
[FeedDownload registerNetworkChangeNotification]; // will call update scheduler
|
||||
[UpdateScheduler registerNetworkChangeNotification]; // will call update scheduler
|
||||
if ([StoreCoordinator isEmpty]) {
|
||||
[_statusItem showWelcomeMessage];
|
||||
[FeedDownload autoDownloadAndParseUpdateURL];
|
||||
[WebFeed autoDownloadAndParseUpdateURL];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification {
|
||||
[FeedDownload unregisterNetworkChangeNotification];
|
||||
[UpdateScheduler unregisterNetworkChangeNotification];
|
||||
}
|
||||
|
||||
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent {
|
||||
@@ -72,7 +73,7 @@
|
||||
url = [url substringFromIndex:2];
|
||||
}
|
||||
if ([scheme isEqualToString:@"feed"]) {
|
||||
[FeedDownload autoDownloadAndParseURL:url addAnyway:NO modify:nil];
|
||||
[WebFeed autoDownloadAndParseURL:url addAnyway:NO modify:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +108,7 @@
|
||||
- (void)preferencesClosed:(id)sender {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowWillCloseNotification object:self.prefWindow.window];
|
||||
self.prefWindow = nil;
|
||||
[FeedDownload scheduleUpdateForUpcomingFeeds];
|
||||
[UpdateScheduler scheduleNextFeed];
|
||||
}
|
||||
|
||||
/// Close previous preferences window and re-open at the same position (will drop undo manager stack!)
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
#import "FeedMeta+Ext.h"
|
||||
#import "FeedGroup+Ext.h"
|
||||
#import "StoreCoordinator.h"
|
||||
#import "FeedDownload.h"
|
||||
#import "Constants.h"
|
||||
#import "NSDate+Ext.h"
|
||||
#import "NSView+Ext.h"
|
||||
|
||||
#import <RSXML/RSXML.h>
|
||||
|
||||
#pragma mark - Helper
|
||||
|
||||
38
baRSS/Feed Import/UpdateScheduler.h
Normal file
38
baRSS/Feed Import/UpdateScheduler.h
Normal file
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// The MIT License (MIT)
|
||||
// Copyright (c) 2019 Oleg Geier
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is furnished to do
|
||||
// so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface UpdateScheduler : NSObject
|
||||
@property (class, readonly) NSDate *dateScheduled;
|
||||
@property (class, readonly) BOOL allowNetworkConnection;
|
||||
@property (class, readonly) BOOL isUpdating;
|
||||
@property (class, setter=setPaused:) BOOL isPaused;
|
||||
|
||||
+ (void)beginUpdate;
|
||||
+ (void)endUpdate;
|
||||
+ (void)scheduleNextFeed;
|
||||
+ (void)forceUpdateAllFeeds;
|
||||
// Register for network change notifications
|
||||
+ (void)registerNetworkChangeNotification;
|
||||
+ (void)unregisterNetworkChangeNotification;
|
||||
@end
|
||||
210
baRSS/Feed Import/UpdateScheduler.m
Normal file
210
baRSS/Feed Import/UpdateScheduler.m
Normal file
@@ -0,0 +1,210 @@
|
||||
//
|
||||
// The MIT License (MIT)
|
||||
// Copyright (c) 2019 Oleg Geier
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is furnished to do
|
||||
// so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#import "UpdateScheduler.h"
|
||||
#import "WebFeed.h"
|
||||
#import "Constants.h"
|
||||
#import "StoreCoordinator.h"
|
||||
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
|
||||
static NSTimer *_timer;
|
||||
static SCNetworkReachabilityRef _reachability = NULL;
|
||||
static BOOL _isReachable = NO;
|
||||
static BOOL _isUpdating = NO;
|
||||
static BOOL _updatePaused = NO;
|
||||
static BOOL _nextUpdateIsForced = NO;
|
||||
|
||||
|
||||
@implementation UpdateScheduler
|
||||
|
||||
#pragma mark - User Interaction
|
||||
|
||||
/// @return Date when background update will fire. If updates are paused, date is @c distantFuture.
|
||||
+ (NSDate *)dateScheduled { return _timer.fireDate; }
|
||||
|
||||
/// @return @c YES if current network state is reachable and updates are not paused by user.
|
||||
+ (BOOL)allowNetworkConnection { return (_isReachable && !_updatePaused); }
|
||||
|
||||
/// @return @c YES if batch update is running
|
||||
+ (BOOL)isUpdating { return _isUpdating; }
|
||||
|
||||
/// @return @c YES if update is paused by user.
|
||||
+ (BOOL)isPaused { return _updatePaused; }
|
||||
|
||||
/// Set paused flag and cancel timer regardless of network connectivity.
|
||||
+ (void)setPaused:(BOOL)flag {
|
||||
_updatePaused = flag;
|
||||
if (_updatePaused)
|
||||
[self pauseUpdates];
|
||||
else
|
||||
[self resumeUpdates];
|
||||
}
|
||||
|
||||
/// Cancel current timer and stop any updates until enabled again.
|
||||
+ (void)pauseUpdates {
|
||||
[self scheduleTimer:nil];
|
||||
}
|
||||
|
||||
/// Start normal (non forced) schedule if network is reachable.
|
||||
+ (void)resumeUpdates {
|
||||
if (_isReachable)
|
||||
[self scheduleNextFeed];
|
||||
}
|
||||
|
||||
/// Set @c isUpdating @c = @c YES
|
||||
+ (void)beginUpdate { _isUpdating = YES; }
|
||||
|
||||
/// Set @c isUpdating @c = @c NO
|
||||
+ (void)endUpdate { _isUpdating = NO; }
|
||||
|
||||
|
||||
#pragma mark - Update Feed Timer
|
||||
|
||||
|
||||
/**
|
||||
Get date of next up feed and start the timer.
|
||||
*/
|
||||
+ (void)scheduleNextFeed {
|
||||
if (![self allowNetworkConnection]) // timer will restart once connection exists
|
||||
return;
|
||||
NSDate *nextTime = [StoreCoordinator nextScheduledUpdate]; // if nextTime = nil, then no feeds to update
|
||||
if (nextTime && [nextTime timeIntervalSinceNow] < 1) { // mostly, if app was closed for a long time
|
||||
nextTime = [NSDate dateWithTimeIntervalSinceNow:1];
|
||||
}
|
||||
[self scheduleTimer:nextTime];
|
||||
}
|
||||
|
||||
/**
|
||||
Start download of all feeds (immediatelly) regardless of @c .scheduled property.
|
||||
*/
|
||||
+ (void)forceUpdateAllFeeds {
|
||||
if (![self allowNetworkConnection]) // timer will restart once connection exists
|
||||
return;
|
||||
_nextUpdateIsForced = YES;
|
||||
[self scheduleTimer:[NSDate dateWithTimeIntervalSinceNow:0.05]];
|
||||
}
|
||||
|
||||
/**
|
||||
Set new @c .fireDate and @c .tolerance for update timer.
|
||||
|
||||
@param nextTime If @c nil timer will be disabled with a @c .fireDate very far in the future.
|
||||
*/
|
||||
+ (void)scheduleTimer:(NSDate*)nextTime {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_timer = [NSTimer timerWithTimeInterval:NSTimeIntervalSince1970 target:[self class] selector:@selector(updateTimerCallback) userInfo:nil repeats:YES];
|
||||
[[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
|
||||
});
|
||||
if (!nextTime)
|
||||
nextTime = [NSDate distantFuture];
|
||||
NSTimeInterval tolerance = [nextTime timeIntervalSinceNow] * 0.15;
|
||||
_timer.tolerance = (tolerance < 1 ? 1 : tolerance); // at least 1 sec
|
||||
_timer.fireDate = nextTime;
|
||||
}
|
||||
|
||||
/**
|
||||
Called when schedule timer runs out (earliest @c .schedule date). Or if forced by user request.
|
||||
*/
|
||||
+ (void)updateTimerCallback {
|
||||
#ifdef DEBUG
|
||||
NSLog(@"fired");
|
||||
#endif
|
||||
BOOL updateAll = _nextUpdateIsForced;
|
||||
_nextUpdateIsForced = NO;
|
||||
if (updateAll)
|
||||
[WebFeed setRequestsAreUrgent:YES];
|
||||
|
||||
NSManagedObjectContext *moc = [StoreCoordinator createChildContext];
|
||||
NSArray<Feed*> *list = [StoreCoordinator getListOfFeedsThatNeedUpdate:updateAll inContext:moc];
|
||||
//NSAssert(list.count > 0, @"ERROR: Something went wrong, timer fired too early.");
|
||||
if (![self allowNetworkConnection]) {
|
||||
[WebFeed setRequestsAreUrgent:NO];
|
||||
[moc reset];
|
||||
return;
|
||||
}
|
||||
[WebFeed batchDownloadFeeds:list favicons:updateAll showErrorAlert:NO finally:^{
|
||||
[WebFeed setRequestsAreUrgent:NO];
|
||||
[StoreCoordinator saveContext:moc andParent:YES]; // save parents too ...
|
||||
[moc reset];
|
||||
[self resumeUpdates]; // always reset the timer
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Network Connection & Reachability
|
||||
|
||||
|
||||
/// Set callback on @c self to listen for network reachability changes.
|
||||
+ (void)registerNetworkChangeNotification {
|
||||
// https://stackoverflow.com/questions/11240196/notification-when-wifi-connected-os-x
|
||||
if (_reachability != NULL) return;
|
||||
_reachability = SCNetworkReachabilityCreateWithName(NULL, "1.1.1.1");
|
||||
if (_reachability == NULL) return;
|
||||
// If reachability information is available now, we don't get a callback later
|
||||
SCNetworkConnectionFlags flags;
|
||||
if (SCNetworkReachabilityGetFlags(_reachability, &flags))
|
||||
networkReachabilityCallback(_reachability, flags, NULL);
|
||||
if (!SCNetworkReachabilitySetCallback(_reachability, networkReachabilityCallback, NULL) ||
|
||||
!SCNetworkReachabilityScheduleWithRunLoop(_reachability, [[NSRunLoop currentRunLoop] getCFRunLoop], kCFRunLoopCommonModes))
|
||||
{
|
||||
CFRelease(_reachability);
|
||||
_reachability = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove @c self callback (network reachability changes).
|
||||
+ (void)unregisterNetworkChangeNotification {
|
||||
if (_reachability != NULL) {
|
||||
SCNetworkReachabilitySetCallback(_reachability, nil, nil);
|
||||
SCNetworkReachabilitySetDispatchQueue(_reachability, nil);
|
||||
CFRelease(_reachability);
|
||||
_reachability = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/// Called when network interface or reachability changes.
|
||||
static void networkReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkConnectionFlags flags, void *object) {
|
||||
if (_reachability == NULL) return;
|
||||
_isReachable = [UpdateScheduler hasConnectivity:flags];
|
||||
PostNotification(kNotificationNetworkStatusChanged, @(_isReachable));
|
||||
if (_isReachable) {
|
||||
[UpdateScheduler resumeUpdates];
|
||||
} else {
|
||||
[UpdateScheduler pauseUpdates];
|
||||
}
|
||||
}
|
||||
|
||||
/// @return @c YES if network connection established.
|
||||
+ (BOOL)hasConnectivity:(SCNetworkReachabilityFlags)flags {
|
||||
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
|
||||
return NO;
|
||||
if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
|
||||
return YES;
|
||||
if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0 &&
|
||||
((flags & kSCNetworkReachabilityFlagsConnectionOnDemand) != 0 ||
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))
|
||||
return YES; // no-intervention AND ( on-demand OR on-traffic )
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -20,23 +20,13 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <RSXML/RSXML.h>
|
||||
|
||||
@class Feed;
|
||||
|
||||
@interface FeedDownload : NSObject
|
||||
@property (class, readonly) NSDate *dateScheduled;
|
||||
@property (class, readonly) BOOL allowNetworkConnection;
|
||||
@property (class, readonly) BOOL isUpdating;
|
||||
@property (class, setter=setPaused:) BOOL isPaused;
|
||||
|
||||
// Register for network change notifications
|
||||
+ (void)registerNetworkChangeNotification;
|
||||
+ (void)unregisterNetworkChangeNotification;
|
||||
// Scheduling
|
||||
+ (void)scheduleUpdateForUpcomingFeeds;
|
||||
+ (void)forceUpdateAllFeeds;
|
||||
@interface WebFeed : NSObject
|
||||
+ (void)setRequestsAreUrgent:(BOOL)flag;
|
||||
// Downloading
|
||||
+ (void)newFeed:(NSString *)urlStr askUser:(nonnull NSString*(^)(RSHTMLMetadata *meta))askUser block:(nonnull void(^)(RSParsedFeed *parsed, NSError *error, NSHTTPURLResponse *response))block;
|
||||
+ (void)autoDownloadAndParseURL:(NSString*)urlStr addAnyway:(BOOL)flag modify:(nullable void(^)(Feed *feed))block;
|
||||
@@ -20,7 +20,8 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#import "FeedDownload.h"
|
||||
#import "WebFeed.h"
|
||||
#import "UpdateScheduler.h"
|
||||
#import "Constants.h"
|
||||
#import "StoreCoordinator.h"
|
||||
#import "Feed+Ext.h"
|
||||
@@ -28,124 +29,16 @@
|
||||
#import "FeedGroup+Ext.h"
|
||||
#import "NSDate+Ext.h"
|
||||
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
|
||||
static NSTimer *_timer;
|
||||
static SCNetworkReachabilityRef _reachability = NULL;
|
||||
static BOOL _isReachable = NO;
|
||||
static BOOL _isUpdating = NO;
|
||||
static BOOL _updatePaused = NO;
|
||||
static BOOL _nextUpdateIsForced = NO;
|
||||
static BOOL _requestsAreUrgent = NO;
|
||||
|
||||
|
||||
@implementation FeedDownload
|
||||
@implementation WebFeed
|
||||
|
||||
/// Disables @c NSURLNetworkServiceTypeBackground (ideally only temporarily)
|
||||
+ (void)setRequestsAreUrgent:(BOOL)flag { _requestsAreUrgent = flag; }
|
||||
|
||||
|
||||
#pragma mark - User Interaction -
|
||||
|
||||
/// @return Date when background update will fire. If updates are paused, date is @c distantFuture.
|
||||
+ (NSDate *)dateScheduled { return _timer.fireDate; }
|
||||
|
||||
/// @return @c YES if current network state is reachable and updates are not paused by user.
|
||||
+ (BOOL)allowNetworkConnection { return (_isReachable && !_updatePaused); }
|
||||
|
||||
/// @return @c YES if batch update is running
|
||||
+ (BOOL)isUpdating { return _isUpdating; }
|
||||
|
||||
/// @return @c YES if update is paused by user.
|
||||
+ (BOOL)isPaused { return _updatePaused; }
|
||||
|
||||
/// Set paused flag and cancel timer regardless of network connectivity.
|
||||
+ (void)setPaused:(BOOL)flag {
|
||||
_updatePaused = flag;
|
||||
if (_updatePaused)
|
||||
[self pauseUpdates];
|
||||
else
|
||||
[self resumeUpdates];
|
||||
}
|
||||
|
||||
/// Cancel current timer and stop any updates until enabled again.
|
||||
+ (void)pauseUpdates {
|
||||
[self scheduleTimer:nil];
|
||||
}
|
||||
|
||||
/// Start normal (non forced) schedule if network is reachable.
|
||||
+ (void)resumeUpdates {
|
||||
if (_isReachable)
|
||||
[self scheduleUpdateForUpcomingFeeds];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Update Feed Timer -
|
||||
|
||||
|
||||
/**
|
||||
Get date of next up feed and start the timer.
|
||||
*/
|
||||
+ (void)scheduleUpdateForUpcomingFeeds {
|
||||
if (![self allowNetworkConnection]) // timer will restart once connection exists
|
||||
return;
|
||||
NSDate *nextTime = [StoreCoordinator nextScheduledUpdate]; // if nextTime = nil, then no feeds to update
|
||||
if (nextTime && [nextTime timeIntervalSinceNow] < 1) { // mostly, if app was closed for a long time
|
||||
nextTime = [NSDate dateWithTimeIntervalSinceNow:1];
|
||||
}
|
||||
[self scheduleTimer:nextTime];
|
||||
}
|
||||
|
||||
/**
|
||||
Start download of all feeds (immediatelly) regardless of @c .scheduled property.
|
||||
*/
|
||||
+ (void)forceUpdateAllFeeds {
|
||||
if (![self allowNetworkConnection]) // timer will restart once connection exists
|
||||
return;
|
||||
_nextUpdateIsForced = YES;
|
||||
[self scheduleTimer:[NSDate dateWithTimeIntervalSinceNow:0.05]];
|
||||
}
|
||||
|
||||
/**
|
||||
Set new @c .fireDate and @c .tolerance for update timer.
|
||||
|
||||
@param nextTime If @c nil timer will be disabled with a @c .fireDate very far in the future.
|
||||
*/
|
||||
+ (void)scheduleTimer:(NSDate*)nextTime {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_timer = [NSTimer timerWithTimeInterval:NSTimeIntervalSince1970 target:[self class] selector:@selector(updateTimerCallback) userInfo:nil repeats:YES];
|
||||
[[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
|
||||
});
|
||||
if (!nextTime)
|
||||
nextTime = [NSDate distantFuture];
|
||||
NSTimeInterval tolerance = [nextTime timeIntervalSinceNow] * 0.15;
|
||||
_timer.tolerance = (tolerance < 1 ? 1 : tolerance); // at least 1 sec
|
||||
_timer.fireDate = nextTime;
|
||||
}
|
||||
|
||||
/**
|
||||
Called when schedule timer runs out (earliest @c .schedule date). Or if forced by user request.
|
||||
*/
|
||||
+ (void)updateTimerCallback {
|
||||
#ifdef DEBUG
|
||||
NSLog(@"fired");
|
||||
#endif
|
||||
BOOL updateAll = _nextUpdateIsForced;
|
||||
_nextUpdateIsForced = NO;
|
||||
|
||||
NSManagedObjectContext *moc = [StoreCoordinator createChildContext];
|
||||
NSArray<Feed*> *list = [StoreCoordinator getListOfFeedsThatNeedUpdate:updateAll inContext:moc];
|
||||
//NSAssert(list.count > 0, @"ERROR: Something went wrong, timer fired too early.");
|
||||
if (![self allowNetworkConnection]) {
|
||||
[moc reset];
|
||||
return;
|
||||
}
|
||||
[self batchDownloadFeeds:list favicons:updateAll showErrorAlert:NO finally:^{
|
||||
[StoreCoordinator saveContext:moc andParent:YES]; // save parents too ...
|
||||
[moc reset];
|
||||
[self resumeUpdates]; // always reset the timer
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Request Generator -
|
||||
#pragma mark - Request Generator
|
||||
|
||||
|
||||
/// @return Base URL part. E.g., https://stackoverflow.com/a/15897956/10616114 ==> https://stackoverflow.com/
|
||||
@@ -176,7 +69,7 @@ static BOOL _nextUpdateIsForced = NO;
|
||||
else if (meta.modified.length > 0)
|
||||
[req setValue:meta.modified forHTTPHeaderField:@"If-Modified-Since"];
|
||||
}
|
||||
if (!_nextUpdateIsForced) // any request that is not forced, is a background update
|
||||
if (!_requestsAreUrgent) // any request that is not forced, is a background update
|
||||
req.networkServiceType = NSURLNetworkServiceTypeBackground;
|
||||
return req;
|
||||
}
|
||||
@@ -229,7 +122,7 @@ static BOOL _nextUpdateIsForced = NO;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Download RSS Feed -
|
||||
#pragma mark - Download RSS Feed
|
||||
|
||||
|
||||
/**
|
||||
@@ -264,7 +157,7 @@ static BOOL _nextUpdateIsForced = NO;
|
||||
|
||||
/**
|
||||
Perform feed download request from URL alone. Not updating any @c Feed item.
|
||||
|
||||
|
||||
@note @c askUser will not be called if url is XML already.
|
||||
|
||||
@param urlStr XML URL or HTTP URL that will be parsed to find feed URLs.
|
||||
@@ -357,7 +250,7 @@ static BOOL _nextUpdateIsForced = NO;
|
||||
[moc reset];
|
||||
if (successful) {
|
||||
PostNotification(kNotificationGroupInserted, f.group.objectID);
|
||||
[self scheduleUpdateForUpcomingFeeds];
|
||||
[UpdateScheduler scheduleNextFeed];
|
||||
}
|
||||
}];
|
||||
}
|
||||
@@ -391,14 +284,14 @@ static BOOL _nextUpdateIsForced = NO;
|
||||
|
||||
/**
|
||||
Start download of all feeds in list. Either with or without favicons.
|
||||
|
||||
|
||||
@param list Download list using @c feed.meta.url as download url. (while reusing etag and modified headers)
|
||||
@param fav If @c YES continue with favicon download after xml download finished.
|
||||
@param alert If @c YES display Error Popup to user.
|
||||
@param block Called after all downloads finished.
|
||||
*/
|
||||
+ (void)batchDownloadFeeds:(NSArray<Feed*> *)list favicons:(BOOL)fav showErrorAlert:(BOOL)alert finally:(nullable os_block_t)block {
|
||||
_isUpdating = YES;
|
||||
[UpdateScheduler beginUpdate];
|
||||
PostNotification(kNotificationBackgroundUpdateInProgress, @(list.count));
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
for (Feed *f in list) {
|
||||
@@ -409,13 +302,13 @@ static BOOL _nextUpdateIsForced = NO;
|
||||
}
|
||||
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
|
||||
if (block) block();
|
||||
_isUpdating = NO;
|
||||
[UpdateScheduler endUpdate];
|
||||
PostNotification(kNotificationBackgroundUpdateInProgress, @(0));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Download Favicon -
|
||||
#pragma mark - Download Favicon
|
||||
|
||||
|
||||
/**
|
||||
@@ -457,7 +350,7 @@ static BOOL _nextUpdateIsForced = NO;
|
||||
}];
|
||||
}
|
||||
|
||||
/// Download html page and parse all icon urls. Starting a successive request on the url of the smallest icon.
|
||||
/// Download html page and parse all icon urls. Starting a successive request on the favicon url.
|
||||
+ (void)downloadFaviconByParsingHTML:(NSString*)hostURL finished:(void(^)(NSImage * _Nullable img))block {
|
||||
[self asyncRequest:[self newRequestURL:hostURL] block:^(NSData * _Nullable htmlData, NSError * _Nullable error, NSHTTPURLResponse *response) {
|
||||
if (htmlData) {
|
||||
@@ -524,61 +417,4 @@ static BOOL _nextUpdateIsForced = NO;
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Network Connection & Reachability -
|
||||
|
||||
|
||||
/// Set callback on @c self to listen for network reachability changes.
|
||||
+ (void)registerNetworkChangeNotification {
|
||||
// https://stackoverflow.com/questions/11240196/notification-when-wifi-connected-os-x
|
||||
if (_reachability != NULL) return;
|
||||
_reachability = SCNetworkReachabilityCreateWithName(NULL, "1.1.1.1");
|
||||
if (_reachability == NULL) return;
|
||||
// If reachability information is available now, we don't get a callback later
|
||||
SCNetworkConnectionFlags flags;
|
||||
if (SCNetworkReachabilityGetFlags(_reachability, &flags))
|
||||
networkReachabilityCallback(_reachability, flags, NULL);
|
||||
if (!SCNetworkReachabilitySetCallback(_reachability, networkReachabilityCallback, NULL) ||
|
||||
!SCNetworkReachabilityScheduleWithRunLoop(_reachability, [[NSRunLoop currentRunLoop] getCFRunLoop], kCFRunLoopCommonModes))
|
||||
{
|
||||
CFRelease(_reachability);
|
||||
_reachability = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove @c self callback (network reachability changes).
|
||||
+ (void)unregisterNetworkChangeNotification {
|
||||
if (_reachability != NULL) {
|
||||
SCNetworkReachabilitySetCallback(_reachability, nil, nil);
|
||||
SCNetworkReachabilitySetDispatchQueue(_reachability, nil);
|
||||
CFRelease(_reachability);
|
||||
_reachability = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/// Called when network interface or reachability changes.
|
||||
static void networkReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkConnectionFlags flags, void *object) {
|
||||
if (_reachability == NULL) return;
|
||||
_isReachable = [FeedDownload hasConnectivity:flags];
|
||||
PostNotification(kNotificationNetworkStatusChanged, @(_isReachable));
|
||||
if (_isReachable) {
|
||||
[FeedDownload resumeUpdates];
|
||||
} else {
|
||||
[FeedDownload pauseUpdates];
|
||||
}
|
||||
}
|
||||
|
||||
/// @return @c YES if network connection established.
|
||||
+ (BOOL)hasConnectivity:(SCNetworkReachabilityFlags)flags {
|
||||
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
|
||||
return NO;
|
||||
if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
|
||||
return YES;
|
||||
if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0 &&
|
||||
((flags & kSCNetworkReachabilityFlagsConnectionOnDemand) != 0 ||
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))
|
||||
return YES; // no-intervention AND ( on-demand OR on-traffic )
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -60,7 +60,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>10180</string>
|
||||
<string>10198</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.news</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
// SOFTWARE.
|
||||
|
||||
#import "ModalFeedEdit.h"
|
||||
#import "FeedDownload.h"
|
||||
#import "WebFeed.h"
|
||||
#import "StoreCoordinator.h"
|
||||
#import "Feed+Ext.h"
|
||||
#import "FeedMeta+Ext.h"
|
||||
@@ -158,8 +158,8 @@
|
||||
if (self.modalSheet.didCloseAndCancel)
|
||||
return;
|
||||
[self preDownload];
|
||||
[FeedDownload newFeed:self.previousURL askUser:^NSString *(RSHTMLMetadata *meta) {
|
||||
self.faviconURL = [FeedDownload faviconUrlForMetadata:meta]; // we can re-use favicon url if we find one
|
||||
[WebFeed newFeed:self.previousURL askUser:^NSString *(RSHTMLMetadata *meta) {
|
||||
self.faviconURL = [WebFeed faviconUrlForMetadata:meta]; // we can re-use favicon url if we find one
|
||||
return [self letUserChooseXmlUrlFromList:meta.feedLinks];
|
||||
} block:^(RSParsedFeed *result, NSError *error, NSHTTPURLResponse* response) {
|
||||
if (self.modalSheet.didCloseAndCancel)
|
||||
@@ -239,7 +239,7 @@
|
||||
self.faviconURL = self.feedResult.link;
|
||||
if (self.faviconURL.length == 0)
|
||||
self.faviconURL = responseURL;
|
||||
[FeedDownload downloadFavicon:self.faviconURL finished:^(NSImage * _Nullable img) {
|
||||
[WebFeed downloadFavicon:self.faviconURL finished:^(NSImage * _Nullable img) {
|
||||
if (self.modalSheet.didCloseAndCancel)
|
||||
return;
|
||||
self.view.favicon.image = img;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#import "SettingsFeeds+DragDrop.h"
|
||||
#import "StoreCoordinator.h"
|
||||
#import "Constants.h"
|
||||
#import "FeedDownload.h"
|
||||
#import "WebFeed.h"
|
||||
#import "FeedGroup+Ext.h"
|
||||
|
||||
// Pasteboard type used during internal row reordering
|
||||
@@ -160,7 +160,7 @@ const NSPasteboardType dragReorder = @"de.relikd.baRSS.drag-reorder";
|
||||
[StoreCoordinator saveContext:moc andParent:YES];
|
||||
if (selection.count > 0)
|
||||
[self.dataStore setSelectionIndexPaths:selection];
|
||||
[FeedDownload batchDownloadFeeds:feedsList favicons:YES showErrorAlert:YES finally:^{
|
||||
[WebFeed batchDownloadFeeds:feedsList favicons:YES showErrorAlert:YES finally:^{
|
||||
[self endCoreDataChangeUndoEmpty:NO forceUndo:NO];
|
||||
[self someDatesChangedScheduleUpdateTimer];
|
||||
}];
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#import "StoreCoordinator.h"
|
||||
#import "ModalFeedEdit.h"
|
||||
#import "FeedGroup+Ext.h"
|
||||
#import "FeedDownload.h"
|
||||
#import "UpdateScheduler.h"
|
||||
#import "SettingsFeedsView.h"
|
||||
#import "NSDate+Ext.h"
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
self.timerStatusInfo = [NSTimer timerWithTimeInterval:NSTimeIntervalSince1970 target:self selector:@selector(keepTimerRunning) userInfo:nil repeats:YES];
|
||||
[[NSRunLoop mainRunLoop] addTimer:self.timerStatusInfo forMode:NSRunLoopCommonModes];
|
||||
// start spinner if update is in progress when preferences open
|
||||
[self activateSpinner:([FeedDownload isUpdating] ? -1 : 0)];
|
||||
[self activateSpinner:([UpdateScheduler isUpdating] ? -1 : 0)];
|
||||
}
|
||||
|
||||
/// Timer cleanup
|
||||
@@ -147,7 +147,7 @@
|
||||
|
||||
/// Query core data for next update date and set bottom status message
|
||||
- (void)someDatesChangedScheduleUpdateTimer {
|
||||
[FeedDownload scheduleUpdateForUpcomingFeeds];
|
||||
[UpdateScheduler scheduleNextFeed];
|
||||
[self.timerStatusInfo fire];
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@
|
||||
|
||||
/// Callback method to update status info. Will be called more often when interval is getting shorter.
|
||||
- (void)keepTimerRunning {
|
||||
NSDate *date = [FeedDownload dateScheduled];
|
||||
NSDate *date = [UpdateScheduler dateScheduled];
|
||||
if (date) {
|
||||
double nextFire = fabs(date.timeIntervalSinceNow);
|
||||
if (nextFire > 1e9) { // distance future, over 31 years
|
||||
@@ -280,9 +280,9 @@
|
||||
/// Returning @c NO will result in a Action-Not-Available-Buzzer sound
|
||||
- (BOOL)respondsToSelector:(SEL)aSelector {
|
||||
if (aSelector == @selector(undo:))
|
||||
return [self.undoManager canUndo] && self.undoManager.groupingLevel == 0 && ![FeedDownload isUpdating];
|
||||
return [self.undoManager canUndo] && self.undoManager.groupingLevel == 0 && ![UpdateScheduler isUpdating];
|
||||
if (aSelector == @selector(redo:))
|
||||
return [self.undoManager canRedo] && self.undoManager.groupingLevel == 0 && ![FeedDownload isUpdating];
|
||||
return [self.undoManager canRedo] && self.undoManager.groupingLevel == 0 && ![UpdateScheduler isUpdating];
|
||||
if (aSelector == @selector(copy:) || aSelector == @selector(remove:))
|
||||
return ([self userSelectionFirst] != nil);
|
||||
if (aSelector == @selector(editSelectedItem)) {
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
#import "BarStatusItem.h"
|
||||
#import "Constants.h"
|
||||
#import "FeedDownload.h"
|
||||
#import "UpdateScheduler.h"
|
||||
#import "StoreCoordinator.h"
|
||||
#import "UserPrefs.h"
|
||||
#import "BarMenu.h"
|
||||
@@ -117,7 +117,7 @@
|
||||
/// Update menu bar icon and text according to unread count and user preferences.
|
||||
- (void)updateBarIcon {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
BOOL hasNet = [FeedDownload allowNetworkConnection];
|
||||
BOOL hasNet = [UpdateScheduler allowNetworkConnection];
|
||||
BOOL tint = (self.unreadCountTotal > 0 && hasNet && [UserPrefs defaultYES:@"globalTintMenuBarIcon"]);
|
||||
self.statusItem.image = [NSImage imageNamed:(hasNet ? RSSImageMenuBarIconActive : RSSImageMenuBarIconPaused)];
|
||||
self.statusItem.image.template = !tint;
|
||||
@@ -169,13 +169,13 @@
|
||||
// 'Pause Updates' item
|
||||
NSMenuItem *pause = [menu addItemWithTitle:NSLocalizedString(@"Pause Updates", nil) action:@selector(pauseUpdates) keyEquivalent:@""];
|
||||
pause.target = self;
|
||||
if ([FeedDownload isPaused])
|
||||
if ([UpdateScheduler isPaused])
|
||||
pause.title = NSLocalizedString(@"Resume Updates", nil);
|
||||
// 'Update all feeds' item
|
||||
if ([UserPrefs defaultYES:@"globalUpdateAll"]) {
|
||||
NSMenuItem *updateAll = [menu addItemWithTitle:NSLocalizedString(@"Update all feeds", nil) action:@selector(updateAllFeeds) keyEquivalent:@""];
|
||||
updateAll.target = self;
|
||||
updateAll.enabled = [FeedDownload allowNetworkConnection];
|
||||
updateAll.enabled = [UpdateScheduler allowNetworkConnection];
|
||||
self.updateAllItem = updateAll;
|
||||
}
|
||||
// Separator between main header and default header
|
||||
@@ -184,14 +184,14 @@
|
||||
|
||||
/// Called when user clicks on 'Pause Updates' (main menu only).
|
||||
- (void)pauseUpdates {
|
||||
[FeedDownload setPaused:![FeedDownload isPaused]];
|
||||
[UpdateScheduler setPaused:![UpdateScheduler isPaused]];
|
||||
[self updateBarIcon];
|
||||
}
|
||||
|
||||
/// Called when user clicks on 'Update all feeds' (main menu only).
|
||||
- (void)updateAllFeeds {
|
||||
// [self asyncReloadUnreadCount]; // should not be necessary
|
||||
[FeedDownload forceUpdateAllFeeds];
|
||||
[UpdateScheduler forceUpdateAllFeeds];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user