diff --git a/Frameworks/Account/Account.swift b/Frameworks/Account/Account.swift index 5e0e642b1..0c025c1d1 100644 --- a/Frameworks/Account/Account.swift +++ b/Frameworks/Account/Account.swift @@ -263,6 +263,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, opmlFile.load() DispatchQueue.main.async { + self.database.cleanupDatabaseAtStartup(subscribedToFeedIDs: self.flattenedFeeds().feedIDs()) self.fetchAllUnreadCounts() } diff --git a/Frameworks/ArticlesDatabase/ArticlesDatabase.swift b/Frameworks/ArticlesDatabase/ArticlesDatabase.swift index 555ff05b7..0c7111fa1 100644 --- a/Frameworks/ArticlesDatabase/ArticlesDatabase.swift +++ b/Frameworks/ArticlesDatabase/ArticlesDatabase.swift @@ -160,6 +160,15 @@ public final class ArticlesDatabase { public func emptyCaches() { articlesTable.emptyCaches() } + + // MARK: - Cleanup + + // These are to be used only at startup. These are to prevent the database from growing forever. + + /// Calls the various clean-up functions. + public func cleanupDatabaseAtStartup(subscribedToFeedIDs: Set) { + articlesTable.deleteArticlesNotInSubscribedToFeedIDs(subscribedToFeedIDs) + } } // MARK: - Private diff --git a/Frameworks/ArticlesDatabase/ArticlesTable.swift b/Frameworks/ArticlesDatabase/ArticlesTable.swift index e3d5455bb..c2553dffc 100644 --- a/Frameworks/ArticlesDatabase/ArticlesTable.swift +++ b/Frameworks/ArticlesDatabase/ArticlesTable.swift @@ -279,65 +279,6 @@ final class ArticlesTable: DatabaseTable { } } -// func update(_ feedID: String, _ parsedItems: Set, _ read: Bool, _ completion: @escaping UpdateArticlesCompletionBlock) { -// if parsedItems.isEmpty { -// completion(nil, nil) -// return -// } -// -// // 1. Ensure statuses for all the incoming articles. -// // 2. Create incoming articles with parsedItems. -// // 3. Ignore incoming articles that are userDeleted || (!starred and really old) -// // 4. Fetch all articles for the feed. -// // 5. Create array of Articles not in database and save them. -// // 6. Create array of updated Articles and save what’s changed. -// // 7. Call back with new and updated Articles. -// // 8. Update search index. -// -// let articleIDs = Set(parsedItems.map { $0.articleID }) -// -// self.queue.update { (database) in -// let statusesDictionary = self.statusesTable.ensureStatusesForArticleIDs(articleIDs, read, database) //1 -// assert(statusesDictionary.count == articleIDs.count) -// -// let allIncomingArticles = Article.articlesWithParsedItems(parsedItems, self.accountID, feedID, statusesDictionary) //2 -// if allIncomingArticles.isEmpty { -// self.callUpdateArticlesCompletionBlock(nil, nil, completion) -// return -// } -// -// let incomingArticles = self.filterIncomingArticles(allIncomingArticles) //3 -// if incomingArticles.isEmpty { -// self.callUpdateArticlesCompletionBlock(nil, nil, completion) -// return -// } -// -// let fetchedArticles = self.fetchArticlesForFeedID(feedID, withLimits: false, database) //4 -// let fetchedArticlesDictionary = fetchedArticles.dictionary() -// -// let newArticles = self.findAndSaveNewArticles(incomingArticles, fetchedArticlesDictionary, database) //5 -// let updatedArticles = self.findAndSaveUpdatedArticles(incomingArticles, fetchedArticlesDictionary, database) //6 -// -// self.callUpdateArticlesCompletionBlock(newArticles, updatedArticles, completion) //7 -// -// // 8. Update search index. -// var articlesToIndex = Set
() -// if let newArticles = newArticles { -// articlesToIndex.formUnion(newArticles) -// } -// if let updatedArticles = updatedArticles { -// articlesToIndex.formUnion(updatedArticles) -// } -// let articleIDs = articlesToIndex.articleIDs() -// if articleIDs.isEmpty { -// return -// } -// DispatchQueue.main.async { -// self.searchTable.ensureIndexedArticles(for: articleIDs) -// } -// } -// } - func ensureStatuses(_ articleIDs: Set, _ defaultRead: Bool, _ statusKey: ArticleStatus.Key, _ flag: Bool) { self.queue.update { (database) in let statusesDictionary = self.statusesTable.ensureStatusesForArticleIDs(articleIDs, defaultRead, database) @@ -489,6 +430,31 @@ final class ArticlesTable: DatabaseTable { self.databaseArticlesCache = [String: DatabaseArticle]() } } + + // MARK: - Cleanup + + /// Delete articles from feeds that are no longer in the current set of subscribed-to feeds. + /// This deletes from the articles and articleStatuses tables, + /// and, via a trigger, it also deletes from the search index. + func deleteArticlesNotInSubscribedToFeedIDs(_ feedIDs: Set) { + if feedIDs.isEmpty { + return + } + queue.run { (database) in + let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(feedIDs.count))! + let sql = "select articleID from articles where feedID not in \(placeholders);" + let parameters = Array(feedIDs) as [Any] + guard let resultSet = database.executeQuery(sql, withArgumentsIn: parameters) else { + return + } + let articleIDs = resultSet.mapToSet{ $0.string(forColumn: DatabaseKey.articleID) } + if articleIDs.isEmpty { + return + } + self.removeArticles(articleIDs, database) + self.statusesTable.removeStatuses(articleIDs, database) + } + } } // MARK: - Private @@ -796,6 +762,10 @@ private extension ArticlesTable { // Drop Articles that we can ignore. return Set(articles.filter{ !statusIndicatesArticleIsIgnorable($0.status) }) } + + func removeArticles(_ articleIDs: Set, _ database: FMDatabase) { + deleteRowsWhere(key: DatabaseKey.articleID, equalsAnyValue: Array(articleIDs), in: database) + } } private extension Set where Element == ParsedItem { diff --git a/Frameworks/ArticlesDatabase/StatusesTable.swift b/Frameworks/ArticlesDatabase/StatusesTable.swift index fa5a741ab..65615c1b0 100644 --- a/Frameworks/ArticlesDatabase/StatusesTable.swift +++ b/Frameworks/ArticlesDatabase/StatusesTable.swift @@ -134,6 +134,12 @@ final class StatusesTable: DatabaseTable { return d } + + // MARK: - Cleanup + + func removeStatuses(_ articleIDs: Set, _ database: FMDatabase) { + deleteRowsWhere(key: DatabaseKey.articleID, equalsAnyValue: Array(articleIDs), in: database) + } } // MARK: - Private diff --git a/Mac/Base.lproj/AddFolderSheet.xib b/Mac/Base.lproj/AddFolderSheet.xib index 169d02cdf..b56a31726 100644 --- a/Mac/Base.lproj/AddFolderSheet.xib +++ b/Mac/Base.lproj/AddFolderSheet.xib @@ -1,47 +1,51 @@ - + - + - + + - + - + - + - - + + - - + + - + + + + - - + + @@ -49,7 +53,7 @@ - + @@ -64,7 +68,7 @@