Storing of feed entries and fetched property bugfix

This commit is contained in:
relikd
2018-08-14 15:11:56 +02:00
parent d1afaef1de
commit 4f688e36a5
12 changed files with 186 additions and 77 deletions

View File

@@ -28,43 +28,46 @@
54E88320211B509D00064188 /* ModalFeedEdit.m in Sources */ = {isa = PBXBuildFile; fileRef = 54E8831E211B509D00064188 /* ModalFeedEdit.m */; }; 54E88320211B509D00064188 /* ModalFeedEdit.m in Sources */ = {isa = PBXBuildFile; fileRef = 54E8831E211B509D00064188 /* ModalFeedEdit.m */; };
54E88321211B509D00064188 /* ModalFeedEdit.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54E8831F211B509D00064188 /* ModalFeedEdit.xib */; }; 54E88321211B509D00064188 /* ModalFeedEdit.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54E8831F211B509D00064188 /* ModalFeedEdit.xib */; };
54F39C2E210BE1F700AEE730 /* DBv1.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 54ACC28221061B3B0020715F /* DBv1.xcdatamodeld */; }; 54F39C2E210BE1F700AEE730 /* DBv1.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 54ACC28221061B3B0020715F /* DBv1.xcdatamodeld */; };
54FE73D021220DEC003EAC65 /* StoreCoordinator.m in Sources */ = {isa = PBXBuildFile; fileRef = 54FE73CF21220DEC003EAC65 /* StoreCoordinator.m */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1968E7919BAA36F042FCB717 /* PyHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyHandler.h; sourceTree = "<group>"; }; 1968E7919BAA36F042FCB717 /* PyHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyHandler.h; sourceTree = "<group>"; };
1968EF7567E06D2A5BB3481A /* PyHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PyHandler.m; sourceTree = "<group>"; }; 1968EF7567E06D2A5BB3481A /* PyHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PyHandler.m; sourceTree = "<group>"; };
54209E922117325100F3B5EF /* DrawImage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DrawImage.h; sourceTree = "<group>"; }; 54209E922117325100F3B5EF /* DrawImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DrawImage.h; sourceTree = "<group>"; };
54209E932117325100F3B5EF /* DrawImage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DrawImage.m; sourceTree = "<group>"; }; 54209E932117325100F3B5EF /* DrawImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DrawImage.m; sourceTree = "<group>"; };
544B01182114B41200386E5C /* ModalSheet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ModalSheet.h; sourceTree = "<group>"; }; 544B01182114B41200386E5C /* ModalSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModalSheet.h; sourceTree = "<group>"; };
544B01192114B41200386E5C /* ModalSheet.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ModalSheet.m; sourceTree = "<group>"; }; 544B01192114B41200386E5C /* ModalSheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ModalSheet.m; sourceTree = "<group>"; };
544B011B2114EE9100386E5C /* AppHook.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppHook.h; sourceTree = "<group>"; }; 544B011B2114EE9100386E5C /* AppHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppHook.h; sourceTree = "<group>"; };
544B011C2114EE9100386E5C /* AppHook.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppHook.m; sourceTree = "<group>"; }; 544B011C2114EE9100386E5C /* AppHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppHook.m; sourceTree = "<group>"; };
544FBD4421064AEB008A260C /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Python.framework; path = System/Library/Frameworks/Python.framework; sourceTree = SDKROOT; }; 544FBD4421064AEB008A260C /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Python.framework; path = System/Library/Frameworks/Python.framework; sourceTree = SDKROOT; };
544FBD4621064B2F008A260C /* getFeed.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = getFeed.py; sourceTree = "<group>"; usesTabs = 0; }; 544FBD4621064B2F008A260C /* getFeed.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = getFeed.py; sourceTree = "<group>"; usesTabs = 0; };
544FBD4821064DF0008A260C /* feedparser521.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = feedparser521.py; sourceTree = "<group>"; usesTabs = 0; }; 544FBD4821064DF0008A260C /* feedparser521.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = feedparser521.py; sourceTree = "<group>"; usesTabs = 0; };
546FC43B21188AD5007CC3A3 /* SettingsFeeds.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingsFeeds.h; sourceTree = "<group>"; }; 546FC43B21188AD5007CC3A3 /* SettingsFeeds.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsFeeds.h; sourceTree = "<group>"; };
546FC43C21188AD5007CC3A3 /* SettingsFeeds.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SettingsFeeds.m; sourceTree = "<group>"; }; 546FC43C21188AD5007CC3A3 /* SettingsFeeds.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsFeeds.m; sourceTree = "<group>"; };
546FC43E21188C78007CC3A3 /* SettingsFeeds.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SettingsFeeds.xib; sourceTree = "<group>"; }; 546FC43E21188C78007CC3A3 /* SettingsFeeds.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SettingsFeeds.xib; sourceTree = "<group>"; };
546FC44021189975007CC3A3 /* SettingsGeneral.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingsGeneral.h; sourceTree = "<group>"; }; 546FC44021189975007CC3A3 /* SettingsGeneral.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsGeneral.h; sourceTree = "<group>"; };
546FC44121189975007CC3A3 /* SettingsGeneral.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SettingsGeneral.m; sourceTree = "<group>"; }; 546FC44121189975007CC3A3 /* SettingsGeneral.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsGeneral.m; sourceTree = "<group>"; };
546FC44221189975007CC3A3 /* SettingsGeneral.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SettingsGeneral.xib; sourceTree = "<group>"; }; 546FC44221189975007CC3A3 /* SettingsGeneral.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SettingsGeneral.xib; sourceTree = "<group>"; };
546FC4462118A8E6007CC3A3 /* Preferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Preferences.xib; sourceTree = "<group>"; }; 546FC4462118A8E6007CC3A3 /* Preferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Preferences.xib; sourceTree = "<group>"; };
54ACC27C21061B3B0020715F /* baRSS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = baRSS.app; sourceTree = BUILT_PRODUCTS_DIR; }; 54ACC27C21061B3B0020715F /* baRSS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = baRSS.app; sourceTree = BUILT_PRODUCTS_DIR; };
54ACC27F21061B3B0020715F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; }; 54ACC27F21061B3B0020715F /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
54ACC28021061B3B0020715F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; }; 54ACC28021061B3B0020715F /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
54ACC28321061B3B0020715F /* DBv1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = DBv1.xcdatamodel; sourceTree = "<group>"; }; 54ACC28321061B3B0020715F /* DBv1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = DBv1.xcdatamodel; sourceTree = "<group>"; };
54ACC28521061B3C0020715F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 54ACC28521061B3C0020715F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
54ACC28821061B3C0020715F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/Main.xib; sourceTree = "<group>"; }; 54ACC28821061B3C0020715F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/Main.xib; sourceTree = "<group>"; };
54ACC28A21061B3C0020715F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 54ACC28A21061B3C0020715F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
54ACC28B21061B3C0020715F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; 54ACC28B21061B3C0020715F /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
54ACC29321061E270020715F /* NewsController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NewsController.h; sourceTree = "<group>"; }; 54ACC29321061E270020715F /* NewsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NewsController.h; sourceTree = "<group>"; };
54ACC29421061E270020715F /* NewsController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NewsController.m; sourceTree = "<group>"; }; 54ACC29421061E270020715F /* NewsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NewsController.m; sourceTree = "<group>"; };
54ACC29621061FBA0020715F /* Preferences.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Preferences.h; sourceTree = "<group>"; }; 54ACC29621061FBA0020715F /* Preferences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Preferences.h; sourceTree = "<group>"; };
54ACC29721061FBA0020715F /* Preferences.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Preferences.m; sourceTree = "<group>"; }; 54ACC29721061FBA0020715F /* Preferences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Preferences.m; sourceTree = "<group>"; };
54E8831D211B509D00064188 /* ModalFeedEdit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ModalFeedEdit.h; sourceTree = "<group>"; }; 54E8831D211B509D00064188 /* ModalFeedEdit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModalFeedEdit.h; sourceTree = "<group>"; };
54E8831E211B509D00064188 /* ModalFeedEdit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ModalFeedEdit.m; sourceTree = "<group>"; }; 54E8831E211B509D00064188 /* ModalFeedEdit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ModalFeedEdit.m; sourceTree = "<group>"; };
54E8831F211B509D00064188 /* ModalFeedEdit.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ModalFeedEdit.xib; sourceTree = "<group>"; }; 54E8831F211B509D00064188 /* ModalFeedEdit.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ModalFeedEdit.xib; sourceTree = "<group>"; };
54EC3E1D211D03C100E314F4 /* FeedConfig+Print.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FeedConfig+Print.h"; sourceTree = "<group>"; }; 54EC3E1D211D03C100E314F4 /* FeedConfig+Print.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FeedConfig+Print.h"; sourceTree = "<group>"; };
54FE73CE21220DEC003EAC65 /* StoreCoordinator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StoreCoordinator.h; sourceTree = "<group>"; };
54FE73CF21220DEC003EAC65 /* StoreCoordinator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StoreCoordinator.m; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -148,6 +151,8 @@
544B011C2114EE9100386E5C /* AppHook.m */, 544B011C2114EE9100386E5C /* AppHook.m */,
54ACC27F21061B3B0020715F /* AppDelegate.h */, 54ACC27F21061B3B0020715F /* AppDelegate.h */,
54ACC28021061B3B0020715F /* AppDelegate.m */, 54ACC28021061B3B0020715F /* AppDelegate.m */,
54FE73CE21220DEC003EAC65 /* StoreCoordinator.h */,
54FE73CF21220DEC003EAC65 /* StoreCoordinator.m */,
54ACC29321061E270020715F /* NewsController.h */, 54ACC29321061E270020715F /* NewsController.h */,
54ACC29421061E270020715F /* NewsController.m */, 54ACC29421061E270020715F /* NewsController.m */,
54209E922117325100F3B5EF /* DrawImage.h */, 54209E922117325100F3B5EF /* DrawImage.h */,
@@ -271,6 +276,7 @@
54E88320211B509D00064188 /* ModalFeedEdit.m in Sources */, 54E88320211B509D00064188 /* ModalFeedEdit.m in Sources */,
1968E0AE14B8E8A90E194980 /* PyHandler.m in Sources */, 1968E0AE14B8E8A90E194980 /* PyHandler.m in Sources */,
54209E942117325100F3B5EF /* DrawImage.m in Sources */, 54209E942117325100F3B5EF /* DrawImage.m in Sources */,
54FE73D021220DEC003EAC65 /* StoreCoordinator.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@@ -25,7 +25,7 @@
@interface AppDelegate : NSObject <NSApplicationDelegate> @interface AppDelegate : NSObject <NSApplicationDelegate>
@property (readonly, strong) NSPersistentContainer *persistentContainer; @property (readonly, strong) NSPersistentContainer *persistentContainer;
- (IBAction)saveAction:(id)sender;
- (void)preferencesClosed; - (void)preferencesClosed;
@end @end

View File

@@ -24,6 +24,7 @@
#import "PyHandler.h" #import "PyHandler.h"
#import "DrawImage.h" #import "DrawImage.h"
#import "Preferences.h" #import "Preferences.h"
#import "StoreCoordinator.h"
@interface AppDelegate () @interface AppDelegate ()
@property (weak) IBOutlet NSMenu *statusMenu; @property (weak) IBOutlet NSMenu *statusMenu;
@@ -42,6 +43,7 @@
self.statusItem.image.template = YES; self.statusItem.image.template = YES;
[PyHandler prepare]; [PyHandler prepare];
printf("up and running\n"); printf("up and running\n");
// [StoreCoordinator deleteUnreferencedFeeds];
} }
- (void)applicationWillTerminate:(NSNotification *)aNotification { - (void)applicationWillTerminate:(NSNotification *)aNotification {
@@ -75,6 +77,7 @@
} }
}]; }];
NSUndoManager *um = [[NSUndoManager alloc] init]; NSUndoManager *um = [[NSUndoManager alloc] init];
um.groupsByEvent = NO;
um.levelsOfUndo = 30; um.levelsOfUndo = 30;
_persistentContainer.viewContext.undoManager = um; _persistentContainer.viewContext.undoManager = um;
} }

View File

@@ -12,9 +12,6 @@
<attribute name="title" optional="YES" attributeType="String" syncable="YES"/> <attribute name="title" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="config" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="FeedConfig" inverseName="feed" inverseEntity="FeedConfig" syncable="YES"/> <relationship name="config" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="FeedConfig" inverseName="feed" inverseEntity="FeedConfig" syncable="YES"/>
<relationship name="items" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="FeedItem" inverseName="feed" inverseEntity="FeedItem" syncable="YES"/> <relationship name="items" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="FeedItem" inverseName="feed" inverseEntity="FeedItem" syncable="YES"/>
<fetchedProperty name="tags" optional="YES" syncable="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="Feed" predicateString="(SELF.tags.feedItem=$FETCH_SOURCE.items)"/>
</fetchedProperty>
</entity> </entity>
<entity name="FeedConfig" representedClassName="FeedConfig" syncable="YES" codeGenerationType="class"> <entity name="FeedConfig" representedClassName="FeedConfig" syncable="YES" codeGenerationType="class">
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/> <attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
@@ -42,7 +39,7 @@
<relationship name="feedItem" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="FeedItem" inverseName="tags" inverseEntity="FeedItem" syncable="YES"/> <relationship name="feedItem" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="FeedItem" inverseName="tags" inverseEntity="FeedItem" syncable="YES"/>
</entity> </entity>
<elements> <elements>
<element name="Feed" positionX="-209" positionY="-3" width="128" height="239"/> <element name="Feed" positionX="-209" positionY="-3" width="128" height="210"/>
<element name="FeedConfig" positionX="-20" positionY="-126" width="128" height="180"/> <element name="FeedConfig" positionX="-20" positionY="-126" width="128" height="180"/>
<element name="FeedItem" positionX="-20" positionY="81" width="128" height="165"/> <element name="FeedItem" positionX="-20" positionY="81" width="128" height="165"/>
<element name="FeedTag" positionX="187" positionY="171" width="128" height="75"/> <element name="FeedTag" positionX="187" positionY="171" width="128" height="75"/>

View File

@@ -23,6 +23,5 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
@interface NewsController : NSObject @interface NewsController : NSObject
+ (void)downloadFeed:(NSString*)url withBlock:(nullable void (^)(NSDictionary* result, NSError* error))block; + (void)downloadFeed:(NSString*)url withBlock:(nullable void (^)(NSDictionary* result, NSError* error))block;
@end @end

View File

@@ -22,7 +22,6 @@
#import "NewsController.h" #import "NewsController.h"
#import "PyHandler.h" #import "PyHandler.h"
#import "DBv1+CoreDataModel.h"
@interface NewsController () @interface NewsController ()
@end @end
@@ -35,35 +34,6 @@
- (IBAction)updateAllFeeds:(NSMenuItem *)sender { - (IBAction)updateAllFeeds:(NSMenuItem *)sender {
NSLog(@"update all"); NSLog(@"update all");
NSDictionary * obj = [PyHandler getFeed:@"https://feeds.feedburner.com/simpledesktops" withEtag:nil andModified:nil];
NSLog(@"obj = %@", obj);
// TODO: check status code
/*
Feed *a = [[Feed alloc] initWithEntity:Feed.entity insertIntoManagedObjectContext:self.managedObjectContext];
a.title = obj[@"feed"][@"title"];
a.subtitle = obj[@"feed"][@"subtitle"];
a.author = obj[@"feed"][@"author"];
a.link = obj[@"feed"][@"link"];
a.published = obj[@"feed"][@"published"];
a.icon = obj[@"feed"][@"icon"];
a.etag = obj[@"header"][@"etag"];
a.date = obj[@"header"][@"date"];
a.modified = obj[@"header"][@"modified"];
for (NSDictionary *entry in obj[@"entries"]) {
FeedItem *b = [[FeedItem alloc] initWithEntity:FeedItem.entity insertIntoManagedObjectContext:self.managedObjectContext];
b.title = entry[@"title"];
b.subtitle = entry[@"subtitle"];
b.author = entry[@"author"];
b.link = entry[@"link"];
b.published = entry[@"published"];
b.summary = entry[@"summary"];
for (NSString *tag in entry[@"tags"]) {
FeedTag *c = [[FeedTag alloc] initWithEntity:FeedTag.entity insertIntoManagedObjectContext:self.managedObjectContext];
c.name = tag;
[b addTagsObject:c];
}
[a addItemsObject:b];
}*/
} }
- (IBAction)openAllUnread:(NSMenuItem *)sender { - (IBAction)openAllUnread:(NSMenuItem *)sender {

View File

@@ -23,7 +23,7 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
@protocol ModalFeedConfigEdit <NSObject> @protocol ModalFeedConfigEdit <NSObject>
- (void)updateRepresentedObject; // must call [item.managedObjectContext refreshAllObjects] - (void)updateRepresentedObject; // must call [item.managedObjectContext refreshObject:item mergeChanges:YES];
@end @end

View File

@@ -22,6 +22,7 @@
#import "ModalFeedEdit.h" #import "ModalFeedEdit.h"
#import "NewsController.h" #import "NewsController.h"
#import "StoreCoordinator.h"
#import "FeedConfig+CoreDataProperties.h" #import "FeedConfig+CoreDataProperties.h"
@interface ModalFeedEdit() @interface ModalFeedEdit()
@@ -38,8 +39,9 @@
@property (strong) NSError *feedError; @property (strong) NSError *feedError;
@property (strong) NSDictionary *feedResult; @property (strong) NSDictionary *feedResult;
@property (assign) BOOL shouldEvaluate; @property (assign) BOOL shouldSaveObject;
@property (assign) BOOL lateEvaluation; @property (assign) BOOL objectNeedsSaving;
@property (assign) BOOL objectIsModified;
@end @end
@implementation ModalFeedEdit @implementation ModalFeedEdit
@@ -48,7 +50,9 @@
[super viewDidLoad]; [super viewDidLoad];
self.previousURL = @""; self.previousURL = @"";
self.refreshNum.intValue = 30; self.refreshNum.intValue = 30;
self.shouldEvaluate = NO; self.shouldSaveObject = NO;
self.objectNeedsSaving = NO;
self.objectIsModified = NO;
FeedConfig *fc = [self feedConfigOrNil]; FeedConfig *fc = [self feedConfigOrNil];
if (fc) { if (fc) {
@@ -65,21 +69,30 @@
} }
- (void)dealloc { - (void)dealloc {
FeedConfig *item = [self feedConfigOrNil]; if (self.shouldSaveObject) {
if (self.shouldEvaluate && self.lateEvaluation && item) { if (self.objectNeedsSaving)
if (!item.name || [item.name isEqualToString:@""]) { [self updateRepresentedObject];
[self setTitleFromFeed]; NSUndoManager *um = [self feedConfigOrNil].managedObjectContext.undoManager;
if (![item.name isEqualToString: self.name.stringValue]) // only if result isnt empty as well [um endUndoGrouping];
item.name = self.name.stringValue; if (!self.objectIsModified) {
[item.managedObjectContext refreshAllObjects]; [um disableUndoRegistration];
[um undoNestedGroup];
[um enableUndoRegistration];
} else {
[StoreCoordinator save];
} }
} }
} }
- (void)updateRepresentedObject { - (void)updateRepresentedObject {
FeedConfig *item = [self feedConfigOrNil]; FeedConfig *item = [self feedConfigOrNil];
if (item) { if (item) {
if (!self.shouldSaveObject) { // first call to this method
[item.managedObjectContext.undoManager beginUndoGrouping];
}
self.shouldSaveObject = YES;
self.objectNeedsSaving = NO; // after this method it is saved
// if's to prevent unnecessary undo groups if nothing has changed // if's to prevent unnecessary undo groups if nothing has changed
if (![item.name isEqualToString: self.name.stringValue]) if (![item.name isEqualToString: self.name.stringValue])
item.name = self.name.stringValue; item.name = self.name.stringValue;
@@ -90,11 +103,17 @@
if (item.refreshUnit != self.refreshUnit.indexOfSelectedItem) if (item.refreshUnit != self.refreshUnit.indexOfSelectedItem)
item.refreshUnit = (int16_t)self.refreshUnit.indexOfSelectedItem; item.refreshUnit = (int16_t)self.refreshUnit.indexOfSelectedItem;
self.shouldEvaluate = YES;
self.lateEvaluation = NO; if (self.feedResult) {
// TODO: append feed result Feed *rss = [StoreCoordinator createFeedFromDictionary:self.feedResult];
NSLog(@"here i want to set it"); if (item.feed)
[item.managedObjectContext refreshAllObjects]; [item.managedObjectContext deleteObject:(NSManagedObject*)item.feed];
item.feed = rss;
}
if ([item.managedObjectContext hasChanges]) {
self.objectIsModified = YES;
[item.managedObjectContext refreshObject:item mergeChanges:YES];
}
} }
} }
@@ -118,17 +137,15 @@
if (obj.object == self.url && [self urlHasChanged]) { if (obj.object == self.url && [self urlHasChanged]) {
self.previousURL = self.url.stringValue; self.previousURL = self.url.stringValue;
self.feedResult = nil; self.feedResult = nil;
NSLog(@"setting result to nil");
self.feedError = nil; self.feedError = nil;
[self.spinnerURL startAnimation:nil]; [self.spinnerURL startAnimation:nil];
[self.spinnerName startAnimation:nil]; [self.spinnerName startAnimation:nil];
[NewsController downloadFeed:self.previousURL withBlock:^(NSDictionary *result, NSError *error) { [NewsController downloadFeed:self.previousURL withBlock:^(NSDictionary *result, NSError *error) {
self.feedResult = result; self.feedResult = result;
NSLog(@"got results back");
self.feedError = error; // warning indicator .hidden is bound to feedError self.feedError = error; // warning indicator .hidden is bound to feedError
// TODO: play error sound? // TODO: play error sound?
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
self.lateEvaluation = YES; // stays YES if this block runs after updateRepresentedObject: self.objectNeedsSaving = YES; // stays YES if this block runs after updateRepresentedObject:
[self setTitleFromFeed]; [self setTitleFromFeed];
[self.spinnerURL stopAnimation:nil]; [self.spinnerURL stopAnimation:nil];
[self.spinnerName stopAnimation:nil]; [self.spinnerName stopAnimation:nil];
@@ -186,7 +203,7 @@
NSString *name = ((NSTextField*)self.view).stringValue; NSString *name = ((NSTextField*)self.view).stringValue;
if (![item.name isEqualToString: name]) { if (![item.name isEqualToString: name]) {
item.name = name; item.name = name;
[item.managedObjectContext refreshAllObjects]; [item.managedObjectContext refreshObject:item mergeChanges:YES];
} }
} }
} }

View File

@@ -22,6 +22,7 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
/** Manages the NSOutlineView and Feed creation and editing */
@interface SettingsFeeds : NSViewController <NSOutlineViewDataSource, NSOutlineViewDelegate> @interface SettingsFeeds : NSViewController <NSOutlineViewDataSource, NSOutlineViewDelegate>
@end @end

View File

@@ -101,12 +101,14 @@ static NSString *dragNodeType = @"baRSS-feed-drag";
[self.view.window beginSheet:[ModalSheet modalWithView:self.modalController.view] completionHandler:^(NSModalResponse returnCode) { [self.view.window beginSheet:[ModalSheet modalWithView:self.modalController.view] completionHandler:^(NSModalResponse returnCode) {
if (returnCode == NSModalResponseOK) { if (returnCode == NSModalResponseOK) {
[self.undoManager beginUndoGrouping];
if (!existingItem) { // create new item if (!existingItem) { // create new item
FeedConfig *item = [self insertSortedItemAtSelection]; FeedConfig *item = [self insertSortedItemAtSelection];
item.type = (group ? 0 : 1); item.type = (group ? 0 : 1);
self.modalController.representedObject = item; self.modalController.representedObject = item;
} }
[self.modalController updateRepresentedObject]; [self.modalController updateRepresentedObject];
[self.undoManager endUndoGrouping];
} }
self.modalController = nil; self.modalController = nil;
}]; }];
@@ -328,6 +330,12 @@ static NSString *dragNodeType = @"baRSS-feed-drag";
NSLog(@"%@", str); NSLog(@"%@", str);
} }
/**
Go through all children recursively and prepend the string with spaces as nesting
@param obj Root Node or parent Node
@param str An initialized @c NSMutableString to append to
@param prefix Should be @c @@"" for the first call
*/
- (void)traverseChildren:(NSTreeNode*)obj appendString:(NSMutableString*)str prefix:(NSString*)prefix { - (void)traverseChildren:(NSTreeNode*)obj appendString:(NSMutableString*)str prefix:(NSString*)prefix {
[str appendFormat:@"%@%@\n", prefix, [obj.representedObject readableDescription]]; [str appendFormat:@"%@%@\n", prefix, [obj.representedObject readableDescription]];
prefix = [prefix stringByAppendingString:@" "]; prefix = [prefix stringByAppendingString:@" "];

31
baRSS/StoreCoordinator.h Normal file
View File

@@ -0,0 +1,31 @@
//
// The MIT License (MIT)
// Copyright (c) 2018 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 <Foundation/Foundation.h>
@class Feed;
@interface StoreCoordinator : NSObject
+ (void)save;
+ (void)deleteUnreferencedFeeds;
+ (Feed*)createFeedFromDictionary:(NSDictionary*)obj;
@end

77
baRSS/StoreCoordinator.m Normal file
View File

@@ -0,0 +1,77 @@
//
// The MIT License (MIT)
// Copyright (c) 2018 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 "StoreCoordinator.h"
#import "AppDelegate.h"
#import "DBv1+CoreDataModel.h"
@implementation StoreCoordinator
+ (NSManagedObjectContext*)getContext {
return [(AppDelegate*)[NSApp delegate] persistentContainer].viewContext;
}
+ (void)save {
[(AppDelegate*)[NSApp delegate] saveAction:nil];
}
+ (void)deleteUnreferencedFeeds {
NSManagedObjectContext *moc = [self getContext];
NSFetchRequest *fr = [NSFetchRequest fetchRequestWithEntityName:Feed.entity.name];
fr.predicate = [NSPredicate predicateWithFormat:@"config = NULL"];
NSBatchDeleteRequest *bdr = [[NSBatchDeleteRequest alloc] initWithFetchRequest:fr];
NSError *err;
[moc executeRequest:bdr error:&err];
if (err) NSLog(@"%@", err);
}
+ (Feed*)createFeedFromDictionary:(NSDictionary*)obj {
NSManagedObjectContext *moc = [self getContext];
Feed *a = [[Feed alloc] initWithEntity:Feed.entity insertIntoManagedObjectContext:moc];
a.title = obj[@"feed"][@"title"];
a.subtitle = obj[@"feed"][@"subtitle"];
a.author = obj[@"feed"][@"author"];
a.link = obj[@"feed"][@"link"];
a.published = obj[@"feed"][@"published"];
a.icon = obj[@"feed"][@"icon"];
a.etag = obj[@"header"][@"etag"];
a.date = obj[@"header"][@"date"];
a.modified = obj[@"header"][@"modified"];
for (NSDictionary *entry in obj[@"entries"]) {
FeedItem *b = [[FeedItem alloc] initWithEntity:FeedItem.entity insertIntoManagedObjectContext:moc];
b.title = entry[@"title"];
b.subtitle = entry[@"subtitle"];
b.author = entry[@"author"];
b.link = entry[@"link"];
b.published = entry[@"published"];
b.summary = entry[@"summary"];
for (NSString *tag in entry[@"tags"]) {
FeedTag *c = [[FeedTag alloc] initWithEntity:FeedTag.entity insertIntoManagedObjectContext:moc];
c.name = tag;
[b addTagsObject:c];
}
[a addItemsObject:b];
}
return a;
}
@end