From b3651d7b09dd7aeef05f6db2345400dd752c89fa Mon Sep 17 00:00:00 2001 From: Eitot Date: Fri, 20 Dec 2024 12:50:00 +0100 Subject: [PATCH 1/3] Replace initSmartfoldersDict method with lazily-initialised property --- Vienna/Sources/Database/Database.h | 1 - Vienna/Sources/Database/Database.m | 22 ++++++++-------------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/Vienna/Sources/Database/Database.h b/Vienna/Sources/Database/Database.h index 3ea0b2a4c..67d852639 100644 --- a/Vienna/Sources/Database/Database.h +++ b/Vienna/Sources/Database/Database.h @@ -100,7 +100,6 @@ extern NSNotificationName const VNADatabaseDidDeleteFolderNotification; subscriptionURL:(NSString *)url remoteId:(NSString *)remoteId; // Smart folder functions --(void)initSmartfoldersDict; -(NSInteger)addSmartFolder:(NSString *)folderName underParent:(NSInteger)parentId withQuery:(CriteriaTree *)criteriaTree; -(void)updateSearchFolder:(NSInteger)folderId withFolder:(NSString *)folderName withQuery:(CriteriaTree *)criteriaTree; -(CriteriaTree *)searchStringForSmartFolder:(NSInteger)folderId; diff --git a/Vienna/Sources/Database/Database.m b/Vienna/Sources/Database/Database.m index 1330510f6..2b96197bf 100644 --- a/Vienna/Sources/Database/Database.m +++ b/Vienna/Sources/Database/Database.m @@ -38,7 +38,6 @@ @interface Database () @property (nonatomic) BOOL initializedfoldersDict; -@property (nonatomic) BOOL initializedSmartfoldersDict; @property (nonatomic) NSMutableArray *fieldsOrdered; @property (nonatomic) NSMutableDictionary *fieldsByName; @property (nonatomic) NSMutableDictionary *foldersDict; @@ -77,12 +76,10 @@ - (instancetype)initWithDatabaseAtPath:(NSString *)dbPath self = [super init]; if (self) { _initializedfoldersDict = NO; - _initializedSmartfoldersDict = NO; _countOfUnread = 0; _trashFolder = nil; _searchFolder = nil; _searchString = @""; - _smartfoldersDict = [[NSMutableDictionary alloc] init]; _foldersDict = [[NSMutableDictionary alloc] init]; [self initaliseFields]; _databaseQueue = [FMDatabaseQueue databaseQueueWithPath:dbPath]; @@ -1775,12 +1772,12 @@ -(BOOL)deleteArticle:(Article *)article return NO; } -/* initSmartfoldersDict - * Preloads all the smart folders into the smartfoldersDict dictionary. - */ --(void)initSmartfoldersDict +- (NSMutableDictionary *)smartfoldersDict { - if (!self.initializedSmartfoldersDict) { + if (!_smartfoldersDict) { + _smartfoldersDict = [[NSMutableDictionary alloc] init]; + + // Preload all the smart folders into the dictionary. FMDatabaseQueue *queue = self.databaseQueue; // Make sure we have a database queue. NSAssert(queue, @"Database queue not assigned for this item"); @@ -1792,12 +1789,12 @@ -(void)initSmartfoldersDict NSString * search_string = [results stringForColumnIndex:1]; CriteriaTree * criteriaTree = [[CriteriaTree alloc] initWithString:search_string]; - self.smartfoldersDict[@(folderId)] = criteriaTree; + _smartfoldersDict[@(folderId)] = criteriaTree; } [results close]; }]; - self.initializedSmartfoldersDict = YES; } + return _smartfoldersDict; } /* searchStringForSmartFolder @@ -1806,7 +1803,6 @@ -(void)initSmartfoldersDict */ -(CriteriaTree *)searchStringForSmartFolder:(NSInteger)folderId { - [self initSmartfoldersDict]; return self.smartfoldersDict[@(folderId)]; } @@ -2154,7 +2150,6 @@ -(CriteriaTree *)criteriaForFolder:(NSInteger)folderId } if (folder.type == VNAFolderTypeSmart) { - [self initSmartfoldersDict]; return self.smartfoldersDict[@(folderId)]; } @@ -2556,11 +2551,10 @@ -(void)close { [[NSNotificationCenter defaultCenter] removeObserver:self]; [self.foldersDict removeAllObjects]; - [self.smartfoldersDict removeAllObjects]; + self.smartfoldersDict = nil; self.trashFolder = nil; self.searchFolder = nil; self.initializedfoldersDict = NO; - self.initializedSmartfoldersDict = NO; _countOfUnread = 0; [self.databaseQueue close]; } From 158336a997aa46fbc5b046c1cf8065e776d99e1c Mon Sep 17 00:00:00 2001 From: Eitot Date: Fri, 20 Dec 2024 14:31:24 +0100 Subject: [PATCH 2/3] Fix smart-folder lookup and selection The name-based lookup is fragile; the folder name is set when Vienna is first launched, whereas the default name depends on the user's current localisation (which may have changed) and/or on the user keeping that default name. This approach uses the underlying predicate format string instead. --- .../Application/AppController+Notifications.m | 12 ++++++--- Vienna/Sources/Application/AppController.m | 26 ++++++++++++------- Vienna/Sources/Database/Database.h | 4 +++ Vienna/Sources/Database/Database.m | 21 +++++++++++++-- 4 files changed, 49 insertions(+), 14 deletions(-) diff --git a/Vienna/Sources/Application/AppController+Notifications.m b/Vienna/Sources/Application/AppController+Notifications.m index 5bba1c8d8..d6d5da502 100644 --- a/Vienna/Sources/Application/AppController+Notifications.m +++ b/Vienna/Sources/Application/AppController+Notifications.m @@ -21,6 +21,7 @@ #import "Database.h" #import "Folder.h" +#import "Vienna-Swift.h" @implementation AppController (Notifications) @@ -59,9 +60,14 @@ - (void)openWindowAndShowUnreadArticles { } [self showMainWindow:self]; - Folder *unreadArticles = [db folderFromName:NSLocalizedString(@"Unread Articles", nil)]; - if (unreadArticles) { - [self selectFolder:unreadArticles.itemId]; + Criteria *unreadCriteria = + [[Criteria alloc] initWithField:MA_Field_Read + operatorType:VNACriteriaOperatorEqualTo + value:@"No"]; + NSString *predicateFormat = unreadCriteria.predicate.predicateFormat; + Folder *smartFolder = [db folderForPredicateFormat:predicateFormat]; + if (smartFolder) { + [self selectFolder:smartFolder.itemId]; } } diff --git a/Vienna/Sources/Application/AppController.m b/Vienna/Sources/Application/AppController.m index 6800b4c2a..3c6bd389a 100644 --- a/Vienna/Sources/Application/AppController.m +++ b/Vienna/Sources/Application/AppController.m @@ -1606,13 +1606,13 @@ -(void)doEditFolder:(Folder *)folder */ -(void)handleFolderSelection:(NSNotification *)nc { - NSInteger newFolderId = ((TreeNode *)nc.object).nodeId; + TreeNode *treeNode = nc.object; // We don't filter when we switch folders. self.filterString = @""; // Call through the controller to display the new folder. - [self.articleController displayFolder:newFolderId]; + [self.articleController displayFolder:treeNode.nodeId]; [self updateSearchPlaceholderAndSearchMethod]; // Make sure article viewer is active @@ -1624,13 +1624,21 @@ -(void)handleFolderSelection:(NSNotification *)nc // If the user selects the unread-articles smart folder, then clear the // relevant user notifications. - if (newFolderId == [db folderFromName:NSLocalizedString(@"Unread Articles", nil)].itemId) { - NSUserNotificationCenter *center = NSUserNotificationCenter.defaultUserNotificationCenter; - [center.deliveredNotifications enumerateObjectsUsingBlock:^(NSUserNotification * notification, NSUInteger idx, BOOL *stop) { - if ([notification.userInfo[UserNotificationContextKey] isEqualToString:UserNotificationContextFetchCompleted]) { - [center removeDeliveredNotification:notification]; - } - }]; + if (treeNode.folder.type == VNAFolderTypeSmart) { + Criteria *unreadCriteria = + [[Criteria alloc] initWithField:MA_Field_Read + operatorType:VNACriteriaOperatorEqualTo + value:@"No"]; + NSString *predicateFormat = unreadCriteria.predicate.predicateFormat; + Folder *smartFolder = [db folderForPredicateFormat:predicateFormat]; + if ([smartFolder isEqual:treeNode.folder]) { + NSUserNotificationCenter *center = NSUserNotificationCenter.defaultUserNotificationCenter; + [center.deliveredNotifications enumerateObjectsUsingBlock:^(NSUserNotification *notification, NSUInteger idx, BOOL *stop) { + if ([notification.userInfo[UserNotificationContextKey] isEqualToString:UserNotificationContextFetchCompleted]) { + [center removeDeliveredNotification:notification]; + } + }]; + } } } diff --git a/Vienna/Sources/Database/Database.h b/Vienna/Sources/Database/Database.h index 67d852639..a54571613 100644 --- a/Vienna/Sources/Database/Database.h +++ b/Vienna/Sources/Database/Database.h @@ -68,6 +68,10 @@ extern NSNotificationName const VNADatabaseDidDeleteFolderNotification; -(Folder *)folderFromFeedURL:(NSString *)wantedFeedURL; -(Folder *)folderFromRemoteId:(NSString *)wantedRemoteId; -(Folder *)folderFromName:(NSString *)wantedName; +/// Returns a smart folder for the predicate format string. +/// - Parameter predicateFormat: An NSPredicate format string. +/// - Returns: A Folder of type `VNAFolderTypeSmart` or `nil`. +- (Folder *)folderForPredicateFormat:(NSString *)predicateFormat; -(NSString *)sqlScopeForFolder:(Folder *)folder flags:(VNAQueryScope)scopeFlags field:(NSString *)field; -(NSInteger)addFolder:(NSInteger)parentId afterChild:(NSInteger)predecessorId folderName:(NSString *)name type:(NSInteger)type canAppendIndex:(BOOL)canAppendIndex; -(BOOL)deleteFolder:(NSInteger)folderId; diff --git a/Vienna/Sources/Database/Database.m b/Vienna/Sources/Database/Database.m index 2b96197bf..65345caa2 100644 --- a/Vienna/Sources/Database/Database.m +++ b/Vienna/Sources/Database/Database.m @@ -41,7 +41,7 @@ @interface Database () @property (nonatomic) NSMutableArray *fieldsOrdered; @property (nonatomic) NSMutableDictionary *fieldsByName; @property (nonatomic) NSMutableDictionary *foldersDict; -@property (nonatomic) NSMutableDictionary *smartfoldersDict; +@property (nonatomic) NSMutableDictionary *smartfoldersDict; @property (readwrite, nonatomic) BOOL readOnly; @property (readwrite, nonatomic) NSInteger countOfUnread; @@ -1468,6 +1468,23 @@ -(Folder *)folderFromRemoteId:(NSString *)wantedRemoteId return folder; } +- (Folder *)folderForPredicateFormat:(NSString *)predicateFormat +{ + __block Folder *folder; + [self.smartfoldersDict enumerateKeysAndObjectsUsingBlock:^( + NSNumber *folderID, CriteriaTree *criteriaTree, BOOL *stop) { + // Different predicate types that cannot be compared with -isEqual: can + // have the same format string. For example, a comparison predicate and + // an all/any compound predicate with a comparison predicate can result + // in the same format string. + if ([criteriaTree.predicate.predicateFormat isEqualToString:predicateFormat]) { + folder = [self folderFromID:folderID.integerValue]; + *stop = YES; + } + }]; + return folder; +} + /* handleAutoSortFoldersTreeChange * Called when preference changes for sorting folders tree. * Store the new method in the database. @@ -1772,7 +1789,7 @@ -(BOOL)deleteArticle:(Article *)article return NO; } -- (NSMutableDictionary *)smartfoldersDict +- (NSMutableDictionary *)smartfoldersDict { if (!_smartfoldersDict) { _smartfoldersDict = [[NSMutableDictionary alloc] init]; From 4cd12b9e7c816fe064dc7eadfd5d20ae1d9844e9 Mon Sep 17 00:00:00 2001 From: Eitot Date: Sun, 22 Dec 2024 20:46:50 +0100 Subject: [PATCH 3/3] Move two documentation comments to the header file --- Vienna/Sources/Database/Database.h | 14 ++++++++++++++ Vienna/Sources/Database/Database.m | 16 ---------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/Vienna/Sources/Database/Database.h b/Vienna/Sources/Database/Database.h index a54571613..da183dd3d 100644 --- a/Vienna/Sources/Database/Database.h +++ b/Vienna/Sources/Database/Database.h @@ -65,7 +65,21 @@ extern NSNotificationName const VNADatabaseDidDeleteFolderNotification; -(NSArray *)arrayOfAllFolders; -(NSArray *)arrayOfFolders:(NSInteger)parentId; -(Folder *)folderFromID:(NSInteger)wantedId; +/*! + * folderFromFeedURL + * + * @param wantedFeedURL The feed URL the folder is wanted for + * + * @return An RSSFolder that is subscribed to the specified feed URL. + */ -(Folder *)folderFromFeedURL:(NSString *)wantedFeedURL; +/*! + * folderFromRemoteId + * + * @param wantedRemoteId The remote identifier the folder is wanted for + * + * @return An OpenReaderFolder that corresponds + */ -(Folder *)folderFromRemoteId:(NSString *)wantedRemoteId; -(Folder *)folderFromName:(NSString *)wantedName; /// Returns a smart folder for the predicate format string. diff --git a/Vienna/Sources/Database/Database.m b/Vienna/Sources/Database/Database.m index 65345caa2..e6153f3a6 100644 --- a/Vienna/Sources/Database/Database.m +++ b/Vienna/Sources/Database/Database.m @@ -1428,14 +1428,6 @@ -(Folder *)folderFromName:(NSString *)wantedName return folder; } - -/*! - * folderFromFeedURL - * - * @param wantedFeedURL The feed URL the folder is wanted for - * - * @return An RSSFolder that is subscribed to the specified feed URL. - */ -(Folder *)folderFromFeedURL:(NSString *)wantedFeedURL { Folder * folder; @@ -1448,14 +1440,6 @@ -(Folder *)folderFromFeedURL:(NSString *)wantedFeedURL return folder; } -/*! - * folderFromRemoteId - * - * @param wantedRemoteId The remote identifier the folder is wanted for - * - * @return An OpenReaderFolder that corresponds - */ - -(Folder *)folderFromRemoteId:(NSString *)wantedRemoteId { Folder * folder;