Refactoring refresh interval handling

This commit is contained in:
relikd
2019-01-27 04:01:07 +01:00
parent f0258fb246
commit cd0a1a3fd7
15 changed files with 190 additions and 103 deletions

View File

@@ -26,12 +26,24 @@ All basic functionality is there. What's missing?
- Authenticated feeds
- Online sync with other services
- Automatic feed detection (e.g., YouTube)
- Text / UI localisation
- App icon & UI icons
All in all, the software is in a usable state. The remaining features will be added in the coming weeks.
Hidden options
--------------
1) When holding down the option key, the menu will show an item to open only a few unread items at a time. This number can be changed with the following Terminal command (default: 10):
defaults write de.relikd.baRSS openFewLinksLimit -int 10
2) In preferences you can choose to show 'Short article names'. This will limit the number of displayed characters to 60 (default). With this Terminal command you can customize this number:
defaults write de.relikd.baRSS shortArticleNamesLimit -int 50
ToDo
----

View File

@@ -29,6 +29,7 @@
54ACC28C21061B3C0020715F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 54ACC28B21061B3C0020715F /* main.m */; };
54ACC29521061E270020715F /* FeedDownload.m in Sources */ = {isa = PBXBuildFile; fileRef = 54ACC29421061E270020715F /* FeedDownload.m */; };
54ACC29821061FBA0020715F /* Preferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 54ACC29721061FBA0020715F /* Preferences.m */; };
54BB048921FD2AB500C303A5 /* NSDate+Ext.m in Sources */ = {isa = PBXBuildFile; fileRef = 54BB048821FD2AB500C303A5 /* NSDate+Ext.m */; };
54CC04382162532A00A48795 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 54CC04372162532A00A48795 /* main.m */; };
54CC043E2162566900A48795 /* baRSS-Helper.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 54CC042C2162532800A48795 /* baRSS-Helper.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
54E88320211B509D00064188 /* ModalFeedEdit.m in Sources */ = {isa = PBXBuildFile; fileRef = 54E8831E211B509D00064188 /* ModalFeedEdit.m */; };
@@ -115,6 +116,8 @@
54ACC29421061E270020715F /* FeedDownload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FeedDownload.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>"; };
54BB048721FD2AB500C303A5 /* NSDate+Ext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+Ext.h"; sourceTree = "<group>"; };
54BB048821FD2AB500C303A5 /* NSDate+Ext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDate+Ext.m"; sourceTree = "<group>"; };
54CC042C2162532800A48795 /* baRSS-Helper.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "baRSS-Helper.app"; sourceTree = BUILT_PRODUCTS_DIR; };
54CC04362162532A00A48795 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
54CC04372162532A00A48795 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
@@ -183,6 +186,8 @@
54209E932117325100F3B5EF /* DrawImage.m */,
544936F921F1E66100DEE9AA /* Statistics.h */,
544936FA21F1E66100DEE9AA /* Statistics.m */,
54BB048721FD2AB500C303A5 /* NSDate+Ext.h */,
54BB048821FD2AB500C303A5 /* NSDate+Ext.m */,
);
path = Helper;
sourceTree = "<group>";
@@ -411,6 +416,7 @@
544B011D2114EE9100386E5C /* AppHook.m in Sources */,
546FC44321189975007CC3A3 /* SettingsGeneral.m in Sources */,
54ACC29521061E270020715F /* FeedDownload.m in Sources */,
54BB048921FD2AB500C303A5 /* NSDate+Ext.m in Sources */,
5477D34E21233C62002BA27F /* FeedGroup+Ext.m in Sources */,
544936FB21F1E66100DEE9AA /* Statistics.m in Sources */,
540F704521B6C16C0022E69D /* FeedMeta+Ext.m in Sources */,

View File

@@ -29,7 +29,6 @@
#import "FeedArticle+CoreDataClass.h"
#import "StoreCoordinator.h"
#import <Cocoa/Cocoa.h>
#import <RSXML/RSXML.h>
@implementation Feed (Ext)
@@ -46,7 +45,7 @@
NSInteger lastIndex = [StoreCoordinator numberRootItemsInContext:moc];
FeedGroup *fg = [FeedGroup newGroup:FEED inContext:moc];
[fg setParent:nil andSortIndex:(int32_t)lastIndex];
[fg.feed.meta setRefresh:30 unit:RefreshUnitMinutes];
[fg.feed.meta setRefreshAndSchedule:kDefaultFeedRefreshInterval];
return fg.feed;
}

View File

@@ -44,4 +44,5 @@ typedef NS_ENUM(int16_t, FeedGroupType) {
- (BOOL)iterateSorted:(BOOL)ordered overDescendantFeeds:(void(^)(Feed *feed, BOOL* cancel))block;
// Printing
- (NSString*)readableDescription;
- (nonnull NSString*)refreshString;
@end

View File

@@ -23,8 +23,7 @@
#import "FeedGroup+Ext.h"
#import "FeedMeta+Ext.h"
#import "Feed+Ext.h"
#import <Cocoa/Cocoa.h>
#import "NSDate+Ext.h"
@implementation FeedGroup (Ext)
@@ -118,8 +117,19 @@
case SEPARATOR: return @"-------------";
case GROUP: return [NSString stringWithFormat:@"%@", self.name];
case FEED:
return [NSString stringWithFormat:@"%@ (%@) - %@", self.name, self.feed.meta.url, self.refreshStr];
return [NSString stringWithFormat:@"%@ (%@) - %@", self.name, self.feed.meta.url, [self refreshString]];
}
}
/// @return Formatted string for update interval ( e.g., @c 30m or @c 12h )
- (nonnull NSString*)refreshString {
if (self.type == FEED) {
int32_t refresh = self.feed.meta.refresh;
if (refresh <= 0)
return @"∞"; // ƒ Ø
return [NSDate stringForInterval:refresh rounded:NO];
}
return @"";
}
@end

View File

@@ -22,22 +22,14 @@
#import "FeedMeta+CoreDataClass.h"
/// Easy memorable @c int16_t enum for refresh unit index
typedef NS_ENUM(int16_t, RefreshUnitType) {
RefreshUnitSeconds = 0, RefreshUnitMinutes = 1, RefreshUnitHours = 2, RefreshUnitDays = 3, RefreshUnitWeeks = 4
};
static const int32_t kDefaultFeedRefreshInterval = 30 * 60;
@interface FeedMeta (Ext)
@property (readonly) BOOL refreshIntervalDisabled; // self.refreshNum <= 0
@property (readonly) int32_t refreshInterval; // self.refreshNum * RefreshUnitValue
// HTTP response
- (void)setErrorAndPostponeSchedule;
- (void)setSucessfulWithResponse:(NSHTTPURLResponse*)response;
// Setter
- (void)setUrlIfChanged:(NSString*)url;
- (void)setEtag:(NSString*)etag modified:(NSString*)modified;
- (BOOL)setRefresh:(int32_t)refresh unit:(RefreshUnitType)unit;
- (BOOL)setRefreshAndUnitFromInterval:(int32_t)interval;
- (BOOL)setRefreshAndSchedule:(int32_t)refresh;
@end

View File

@@ -24,30 +24,8 @@
#import "Feed+Ext.h"
#import "FeedGroup+Ext.h"
/// smhdw: [1, 60, 3600, 86400, 604800]
static const int32_t RefreshUnitValues[] = {1, 60, 3600, 86400, 604800}; // smhdw
@implementation FeedMeta (Ext)
#pragma mark - Getter
/// Check whether update interval is disabled by user (refresh interval is 0).
- (BOOL)refreshIntervalDisabled {
return (self.refreshNum <= 0);
}
/// @return Time interval respecting the selected unit. E.g., returns @c 180 for @c '3m'
- (int32_t)refreshInterval {
return self.refreshNum * RefreshUnitValues[self.refreshUnit % 5];
}
/// @return Formatted string for update interval ( e.g., @c 30m or @c 12h )
- (NSString*)readableRefreshString {
if (self.refreshIntervalDisabled)
return @"∞"; // ƒ Ø
return [NSString stringWithFormat:@"%d%c", self.refreshNum, [@"smhdw" characterAtIndex:self.refreshUnit % 5]];
}
#pragma mark - HTTP response
/// Increment @c errorCount and set new @c scheduled date (2^N minutes, max. 5.7 days).
@@ -68,7 +46,7 @@ static const int32_t RefreshUnitValues[] = {1, 60, 3600, 86400, 604800}; // smhd
self.errorCount = 0; // reset counter
NSDictionary *header = [response allHeaderFields];
[self setEtag:header[@"Etag"] modified:header[@"Date"]]; // @"Expires", @"Last-Modified"
[self scheduleNow:[self refreshInterval]];
[self scheduleNow:self.refresh];
}
#pragma mark - Setter
@@ -85,44 +63,22 @@ static const int32_t RefreshUnitValues[] = {1, 60, 3600, 86400, 604800}; // smhd
}
/**
Set @c refresh and @c unit from popup button selection. Only values that differ will be updated.
Also, calculate and set new @c scheduled date and update FeedGroup @c refreshStr (if changed).
Set @c refresh and calculate new @c scheduled date.
@return @c YES if refresh interval has changed
*/
- (BOOL)setRefresh:(int32_t)refresh unit:(RefreshUnitType)unit {
BOOL intervalChanged = (self.refreshNum != refresh || self.refreshUnit != unit);
if (self.refreshNum != refresh) self.refreshNum = refresh;
if (self.refreshUnit != unit) self.refreshUnit = unit;
if (intervalChanged) {
[self scheduleNow:[self refreshInterval]];
NSString *str = [self readableRefreshString];
if (![self.feed.group.refreshStr isEqualToString:str])
self.feed.group.refreshStr = str;
- (BOOL)setRefreshAndSchedule:(int32_t)refresh {
if (self.refresh != refresh) {
self.refresh = refresh;
[self scheduleNow:self.refresh];
return YES;
}
return intervalChanged;
return NO;
}
/**
Set properties @c refreshNum and @c refreshUnit to highest possible (integer-dividable-)unit.
Only values that differ will be updated.
Also, calculate and set new @c scheduled date and update FeedGroup @c refreshStr (if changed).
@return @c YES if refresh interval has changed
*/
- (BOOL)setRefreshAndUnitFromInterval:(int32_t)interval {
for (RefreshUnitType i = 4; i >= 0; i--) { // start with weeks
if (interval % RefreshUnitValues[i] == 0) { // find first unit that is dividable
return [self setRefresh:abs(interval) / RefreshUnitValues[i] unit:i];
}
}
return NO; // since loop didn't return, no value was changed
}
/// Calculate date from @c refreshNum and @c refreshUnit and set as next scheduled feed update.
/// Set next scheduled feed update or @c nil if @c refresh @c <= @c 0.
- (void)scheduleNow:(NSTimeInterval)future {
if (self.refreshIntervalDisabled) { // update deactivated; manually update with force update all
if (self.refresh <= 0) { // update deactivated; manually update with force update all
if (self.scheduled != nil) // already nil? Avoid unnecessary core data edits
self.scheduled = nil;
} else {

View File

@@ -26,8 +26,7 @@
// TODO: Add support for media player? image feed?
// <enclosure url="https://url.mp3" length="63274022" type="audio/mpeg" />
// TODO: Disable 'update all' menu item during update?
// TODO: List of hidden preferences for readme
// TODO: Do we need to search for favicon in places other than '../favicon.ico'?
/**
@c notification.object is @c NSNumber of type @c NSUInteger.

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14315.18" systemVersion="17G4015" minimumToolsVersion="Automatic" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="v1">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14315.18" systemVersion="17G5019" minimumToolsVersion="Automatic" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="v1">
<entity name="Feed" representedClassName="Feed" syncable="YES" codeGenerationType="class">
<attribute name="indexPath" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="link" optional="YES" attributeType="String" syncable="YES"/>
@@ -25,7 +25,6 @@
</entity>
<entity name="FeedGroup" representedClassName="FeedGroup" syncable="YES" codeGenerationType="class">
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="refreshStr" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="sortIndex" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="type" optional="YES" attributeType="Integer 16" defaultValueString="-1" usesScalarValueType="YES" syncable="YES"/>
<relationship name="children" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="FeedGroup" inverseName="parent" inverseEntity="FeedGroup" syncable="YES"/>
@@ -40,8 +39,7 @@
<attribute name="errorCount" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="etag" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="modified" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="refreshNum" optional="YES" attributeType="Integer 32" defaultValueString="-1" usesScalarValueType="YES" syncable="YES"/>
<attribute name="refreshUnit" optional="YES" attributeType="Integer 16" defaultValueString="-1" usesScalarValueType="YES" customClassName="NSUInteger" syncable="YES"/>
<attribute name="refresh" optional="YES" attributeType="Integer 32" defaultValueString="-1" usesScalarValueType="YES" syncable="YES"/>
<attribute name="scheduled" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
<attribute name="url" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="feed" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Feed" inverseName="meta" inverseEntity="Feed" syncable="YES"/>
@@ -49,8 +47,8 @@
<elements>
<element name="Feed" positionX="-278.84765625" positionY="-112.953125" width="128" height="180"/>
<element name="FeedArticle" positionX="-96.77734375" positionY="-113.83984375" width="128" height="195"/>
<element name="FeedGroup" positionX="-460.37890625" positionY="-111.62890625" width="130.52734375" height="150"/>
<element name="FeedGroup" positionX="-460.37890625" positionY="-111.62890625" width="130.52734375" height="135"/>
<element name="FeedIcon" positionX="-202.79296875" positionY="137.71875" width="128" height="75"/>
<element name="FeedMeta" positionX="-348.02734375" positionY="136.89453125" width="128" height="165"/>
<element name="FeedMeta" positionX="-348.02734375" positionY="136.89453125" width="128" height="150"/>
</elements>
</model>

24
baRSS/Helper/NSDate+Ext.h Normal file
View File

@@ -0,0 +1,24 @@
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
typedef int32_t Interval;
typedef NS_ENUM(int32_t, TimeUnitType) {
TimeUnitSeconds = 1,
TimeUnitMinutes = 60,
TimeUnitHours = 60 * 60,
TimeUnitDays = 24 * 60 * 60,
TimeUnitWeeks = 7 * 24 * 60 * 60,
TimeUnitYears = 365 * 24 * 60 * 60
};
@interface NSDate (Ext)
+ (nonnull NSString*)stringForInterval:(Interval)intv rounded:(BOOL)flag;
@end
@interface NSDate (RefreshControlsUI)
+ (Interval)intervalForPopup:(NSPopUpButton*)unit andField:(NSTextField*)value;
+ (void)setInterval:(Interval)intv forPopup:(NSPopUpButton*)popup andField:(NSTextField*)field;
+ (void)populateUnitsMenu:(NSPopUpButton*)popup selected:(TimeUnitType)unit;
@end

101
baRSS/Helper/NSDate+Ext.m Normal file
View File

@@ -0,0 +1,101 @@
#import "NSDate+Ext.h"
static const char _shortnames[] = {'y','w','d','h','m','s'};
static const char *_names[] = {"Years", "Weeks", "Days", "Hours", "Minutes", "Seconds"};
static const TimeUnitType _values[] = {
TimeUnitYears,
TimeUnitWeeks,
TimeUnitDays,
TimeUnitHours,
TimeUnitMinutes,
TimeUnitSeconds,
};
@implementation NSDate (Ext)
+ (nonnull NSString*)stringForInterval:(Interval)intv rounded:(BOOL)flag {
if (flag) {
unsigned short i = [self floatUnitIndexForInterval:abs(intv)];
return [NSString stringWithFormat:@"%1.1f%c", intv / (float)_values[i], _shortnames[i]];
}
unsigned short i = [self exactUnitIndexForInterval:abs(intv)];
return [NSString stringWithFormat:@"%d%c", intv / _values[i], _shortnames[i]];
}
/// @return Highest non-zero unit ( @c flag=YES ). Or highest integer-dividable unit ( @c flag=NO ).
+ (TimeUnitType)unitForInterval:(Interval)intv rounded:(BOOL)flag {
if (flag) {
return _values[[self floatUnitIndexForInterval:abs(intv)]];
}
return _values[[self exactUnitIndexForInterval:abs(intv)]];
}
/// @return Highest unit type that allows integer division. E.g., '61 minutes'.
+ (unsigned short)exactUnitIndexForInterval:(Interval)intv {
for (unsigned short i = 0; i < 5; i++)
if (intv % _values[i] == 0) return i;
return 5; // seconds
}
/// @return Highest non-zero unit type. Can be used with fractions e.g., '1.1 hours'.
+ (unsigned short)floatUnitIndexForInterval:(Interval)intv {
for (unsigned short i = 0; i < 5; i++)
if (intv > _values[i]) return i;
return 5; // seconds
}
/* NOT USED
/// Convert any unit to the next smaller one. Unit does not have to be exact.
+ (TimeUnitType)smallerUnit:(TimeUnitType)unit {
if (unit <= TimeUnitHours) return TimeUnitSeconds;
if (unit <= TimeUnitDays) return TimeUnitMinutes; // > hours
if (unit <= TimeUnitWeeks) return TimeUnitHours; // > days
if (unit <= TimeUnitYears) return TimeUnitDays; // > weeks
return TimeUnitWeeks; // > years
}
/// @return Formatted string from @c timeIntervalSinceNow.
- (nonnull NSString*)intervalStringWithDecimal:(BOOL)flag {
return [NSDate stringForInterval:(Interval)[self timeIntervalSinceNow] rounded:flag];
}
/// @return Highest non-zero unit ( @c flag=YES ). Or highest integer-dividable unit ( @c flag=NO ).
- (TimeUnitType)unitWithDecimal:(BOOL)flag {
Interval absIntv = abs((Interval)[self timeIntervalSinceNow]);
if (flag) {
return _values[ [NSDate floatUnitIndexForInterval:absIntv] ];
}
return _values[ [NSDate exactUnitIndexForInterval:absIntv] ];
}
*/
@end
@implementation NSDate (RefreshControlsUI)
/// @return Interval by multiplying the text field value with the currently selected popup unit.
+ (Interval)intervalForPopup:(NSPopUpButton*)unit andField:(NSTextField*)value {
return value.intValue * (Interval)unit.selectedTag;
}
/// Configure both @c NSControl elements based on the provided interval @c intv.
+ (void)setInterval:(Interval)intv forPopup:(NSPopUpButton*)popup andField:(NSTextField*)field {
TimeUnitType unit = [self unitForInterval:intv rounded:NO];
[popup selectItemWithTag:unit];
field.intValue = (int)(intv / unit);
}
/// Insert all @c TimeUnitType items into popup button. Save unit value into @c tag attribute.
+ (void)populateUnitsMenu:(NSPopUpButton*)popup selected:(TimeUnitType)unit {
[popup removeAllItems];
for (NSUInteger i = 0; i < 6; i++) {
[popup addItemWithTitle:[NSString stringWithUTF8String:_names[i]]];
NSMenuItem *item = popup.lastItem;
[item setKeyEquivalent:[[NSString stringWithFormat:@"%c", _shortnames[i]] uppercaseString]];
item.tag = _values[i];
}
[popup selectItemWithTag:unit];
}
@end

View File

@@ -27,6 +27,8 @@
#import "FeedMeta+Ext.h"
#import "FeedGroup+Ext.h"
#import "Statistics.h"
#import "NSDate+Ext.h"
#import <QuartzCore/QuartzCore.h>
@@ -89,6 +91,7 @@
[super viewDidLoad];
self.previousURL = @"";
self.refreshNum.intValue = 30;
[NSDate populateUnitsMenu:self.refreshUnit selected:TimeUnitMinutes];
self.warningIndicator.image = nil;
[self.warningIndicator.cell setHighlightsBy:NSNoCellMask];
[self populateTextFields:self.feedGroup];
@@ -102,12 +105,8 @@
self.name.objectValue = fg.name;
self.url.objectValue = fg.feed.meta.url;
self.previousURL = self.url.stringValue;
self.refreshNum.intValue = fg.feed.meta.refreshNum;
NSInteger unit = (NSInteger)fg.feed.meta.refreshUnit;
if (unit < 0 || unit > self.refreshUnit.numberOfItems - 1)
unit = self.refreshUnit.numberOfItems - 1;
[self.refreshUnit selectItemAtIndex:unit];
self.warningIndicator.image = [fg.feed iconImage16];
[NSDate setInterval:fg.feed.meta.refresh forPopup:self.refreshUnit andField:self.refreshNum];
[self statsForCoreDataObject];
}
@@ -122,7 +121,8 @@
[self.feedGroup setNameIfChanged:self.name.stringValue];
FeedMeta *meta = feed.meta;
[meta setUrlIfChanged:self.previousURL];
[meta setRefresh:self.refreshNum.intValue unit:(int16_t)self.refreshUnit.indexOfSelectedItem]; // updateTimer will be scheduled once preferences is closed
[meta setRefreshAndSchedule:[NSDate intervalForPopup:self.refreshUnit andField:self.refreshNum]];
// updateTimer will be scheduled once preferences is closed
if (self.didDownloadFeed) {
[meta setEtag:self.httpEtag modified:self.httpDate];
[feed updateWithRSS:self.feedResult postUnreadCountChange:YES];

View File

@@ -86,24 +86,12 @@
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TUi-VS-ge4">
<rect key="frame" x="198" y="-3" width="125" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Minutes" bezelStyle="rounded" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" autoenablesItems="NO" altersStateOfSelectedItem="NO" selectedItem="CsM-KR-zzs" id="O0p-Tc-KQ1">
<popUpButtonCell key="cell" type="push" title="-- list --" bezelStyle="rounded" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" autoenablesItems="NO" altersStateOfSelectedItem="NO" selectedItem="lQ1-ai-wYn" id="O0p-Tc-KQ1">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" showsStateColumn="NO" autoenablesItems="NO" id="7hX-7Y-rtT">
<items>
<menuItem title="Seconds" keyEquivalent="s" id="VD1-1h-Hdh">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Minutes" state="on" keyEquivalent="m" id="CsM-KR-zzs">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Hours" keyEquivalent="h" id="Nqd-L9-4V8">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Days" keyEquivalent="d" id="5c2-Mb-3aw">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Weeks" keyEquivalent="w" id="mJE-8n-iKF">
<menuItem title="-- list --" id="lQ1-ai-wYn">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
</items>

View File

@@ -170,9 +170,9 @@
meta.url = [item attributeForKey:OPMLXMLURLKey];
id refresh = [item attributeForKey:@"refreshInterval"]; // baRSS specific
if (refresh) {
[meta setRefreshAndUnitFromInterval:(int32_t)[refresh integerValue]];
[meta setRefreshAndSchedule:(int32_t)[refresh integerValue]];
} else {
[meta setRefresh:30 unit:RefreshUnitMinutes];
[meta setRefreshAndSchedule:kDefaultFeedRefreshInterval]; // TODO: set -1, then auto
}
}
[list addObject:newFeed.feed];
@@ -232,7 +232,7 @@
[outline addAttribute:[NSXMLNode attributeWithName:OPMLHMTLURLKey stringValue:item.feed.link]];
[outline addAttribute:[NSXMLNode attributeWithName:OPMLXMLURLKey stringValue:item.feed.meta.url]];
[outline addAttribute:[NSXMLNode attributeWithName:OPMLTypeKey stringValue:@"rss"]];
NSString *intervalStr = [NSString stringWithFormat:@"%d", item.feed.meta.refreshInterval];
NSString *intervalStr = [NSString stringWithFormat:@"%d", item.feed.meta.refresh];
[outline addAttribute:[NSXMLNode attributeWithName:@"refreshInterval" stringValue:intervalStr]]; // baRSS specific
// TODO: option to export unread state?
}

View File

@@ -408,8 +408,9 @@ static NSString *dragNodeType = @"baRSS-feed-drag";
NSTableCellView *cellView = [self.outlineView makeViewWithIdentifier:cellIdent owner:nil];
if (isRefreshColumn) {
cellView.textField.objectValue = fg.refreshStr;
cellView.textField.textColor = (fg.refreshStr.length > 1 ? [NSColor controlTextColor] : [NSColor disabledControlTextColor]);
NSString *str = [fg refreshString];
cellView.textField.stringValue = str;
cellView.textField.textColor = (str.length > 1 ? [NSColor controlTextColor] : [NSColor disabledControlTextColor]);
} else if (isSeperator) {
return cellView; // refresh cell already skipped with the above if condition
} else {