From 6572631866c64fc281bc2da58888ec9011b07f64 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Sun, 8 Oct 2017 21:06:25 -0700 Subject: [PATCH] =?UTF-8?q?Update=20the=20timeline=20cell=20when=20an=20ar?= =?UTF-8?q?ticle=E2=80=99s=20status=20changes.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Evergreen/Data/ArticleUtilities.swift | 19 ++---- .../Timeline/TimelineViewController.swift | 65 ++++++++++-------- Frameworks/Account/Account.swift | 15 +++-- Frameworks/Data/Article.swift | 14 ++++ Frameworks/Data/ArticleStatus.swift | 66 +++++++++++-------- Frameworks/Database/ArticlesTable.swift | 4 +- Frameworks/Database/Database.swift | 4 +- .../Extensions/Article+Database.swift | 5 -- .../Extensions/ArticleStatus+Database.swift | 7 -- Frameworks/Database/StatusesTable.swift | 9 +-- ToDo.opml | 7 +- 11 files changed, 118 insertions(+), 97 deletions(-) diff --git a/Evergreen/Data/ArticleUtilities.swift b/Evergreen/Data/ArticleUtilities.swift index 846484485..e97a8b3fd 100644 --- a/Evergreen/Data/ArticleUtilities.swift +++ b/Evergreen/Data/ArticleUtilities.swift @@ -12,13 +12,13 @@ import Account // These handle multiple accounts. -func markArticles(_ articles: Set
, statusKey: String, flag: Bool) { +func markArticles(_ articles: Set
, statusKey: ArticleStatus.Key, flag: Bool) { let d: [String: Set
] = accountAndArticlesDictionary(articles) - d.keys.forEach { (accountID) in + for (accountID, accountArticles) in d { - guard let accountArticles = d[accountID], let account = AccountManager.shared.existingAccount(with: accountID) else { + guard let account = AccountManager.shared.existingAccount(with: accountID) else { return } @@ -27,18 +27,9 @@ func markArticles(_ articles: Set
, statusKey: String, flag: Bool) { } private func accountAndArticlesDictionary(_ articles: Set
) -> [String: Set
] { - - var d = [String: Set
]() - articles.forEach { (article) in - - let accountID = article.accountID - var articleSet: Set
= d[accountID] ?? Set
() - articleSet.insert(article) - d[accountID] = articleSet - } - - return d + let d = Dictionary(grouping: articles, by: { $0.accountID }) + return d.mapValues{ Set($0) } } extension Article { diff --git a/Evergreen/MainWindow/Timeline/TimelineViewController.swift b/Evergreen/MainWindow/Timeline/TimelineViewController.swift index 0725f118e..ba7fa6db6 100644 --- a/Evergreen/MainWindow/Timeline/TimelineViewController.swift +++ b/Evergreen/MainWindow/Timeline/TimelineViewController.swift @@ -76,7 +76,7 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView if !didRegisterForNotifications { NotificationCenter.default.addObserver(self, selector: #selector(sidebarSelectionDidChange(_:)), name: .SidebarSelectionDidChange, object: nil) -// NotificationCenter.default.addObserver(self, selector: #selector(articleStatusesDidChange(_:)), name: .ArticleStatusesDidChange, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil) NSUserDefaultsController.shared.addObserver(self, forKeyPath: timelineFontSizeKVOKey, options: NSKeyValueObservingOptions(rawValue: 0), context: nil) @@ -115,7 +115,7 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView } } - // MARK: API + // MARK: - API func markAllAsRead() { @@ -123,12 +123,12 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView return } - markArticles(Set(articles), statusKey: ArticleStatusKey.read.rawValue, flag: true) + markArticles(Set(articles), statusKey: .read, flag: true) reloadCellsForArticles(articles) } - // MARK: Actions + // MARK: - Actions @objc func openArticleInBrowser(_ sender: AnyObject) { @@ -146,20 +146,20 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView let status = articles.first!.status let markAsRead = !status.read - markArticles(Set(articles), statusKey: ArticleStatusKey.read.rawValue, flag: markAsRead) + markArticles(Set(articles), statusKey: .read, flag: markAsRead) } @IBAction func markSelectedArticlesAsRead(_ sender: AnyObject) { - markArticles(Set(selectedArticles), statusKey: ArticleStatusKey.read.rawValue, flag: true) + markArticles(Set(selectedArticles), statusKey: .read, flag: true) } @IBAction func markSelectedArticlesAsUnread(_ sender: AnyObject) { - markArticles(Set(selectedArticles), statusKey: ArticleStatusKey.read.rawValue, flag: false) + markArticles(Set(selectedArticles), statusKey: .read, flag: false) } - // MARK: Navigation + // MARK: - Navigation func goToNextUnread() { @@ -212,7 +212,7 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView return nil } - // MARK: Notifications + // MARK: - Notifications @objc func sidebarSelectionDidChange(_ note: Notification) { @@ -223,12 +223,12 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView } } - @objc func articleStatusesDidChange(_ note: Notification) { + @objc func statusesDidChange(_ note: Notification) { - guard let articles = note.appInfo?.articles else { + guard let statuses = note.userInfo?[Account.UserInfoKey.statuses] as? Set else { return } - reloadCellsForArticles(Array(articles)) + reloadCellsForArticleIDs(statuses.articleIDs()) } func fontSizeInDefaultsDidChange() { @@ -243,7 +243,7 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView } } - // MARK: KeyboardDelegate + // MARK: - KeyboardDelegate func handleKeydownEvent(_ event: NSEvent, sender: AnyObject) -> Bool { @@ -302,7 +302,7 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView return keyHandled } - // MARK: Reloading Data + // MARK: - Reloading Data private func cellForRowView(_ rowView: NSView) -> NSView? { @@ -314,23 +314,28 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView private func reloadCellsForArticles(_ articles: [Article]) { - let indexes = indexesForArticles(articles) + reloadCellsForArticleIDs(Set(articles.articleIDs())) + } + + private func reloadCellsForArticleIDs(_ articleIDs: Set) { + + let indexes = indexesForArticleIDs(articleIDs) tableView.reloadData(forRowIndexes: indexes, columnIndexes: NSIndexSet(index: 0) as IndexSet) } - // MARK: Articles + // MARK: - Articles - private func indexesForArticles(_ articles: [Article]) -> IndexSet { + private func indexesForArticleIDs(_ articleIDs: Set) -> IndexSet { var indexes = IndexSet() - articles.forEach { (article) in - let oneIndex = rowForArticle(article) + articleIDs.forEach { (articleID) in + let oneIndex = rowForArticleID(articleID) if oneIndex != NSNotFound { indexes.insert(oneIndex) } } - + return indexes } @@ -351,13 +356,18 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView private func rowForArticle(_ article: Article) -> Int { - if let index = articles.index(where: { $0.articleID == article.articleID }) { + return rowForArticleID(article.articleID) + } + + private func rowForArticleID(_ articleID: String) -> Int { + + if let index = articles.index(where: { $0.articleID == articleID }) { return index } return NSNotFound } - + func selectedArticle() -> Article? { return articleAtRow(tableView.selectedRow) @@ -409,7 +419,7 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView } } - // MARK: Cell Configuring + // MARK: - Cell Configuring private func calculateRowHeight() -> CGFloat { @@ -435,7 +445,7 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView cell.cellData = emptyCellData } - // MARK: NSTableViewDataSource + // MARK: - NSTableViewDataSource func numberOfRows(in tableView: NSTableView) -> Int { @@ -447,7 +457,7 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView return articleAtRow(row) } - // MARK: NSTableViewDelegate + // MARK: - NSTableViewDelegate func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? { @@ -495,7 +505,7 @@ class TimelineViewController: NSViewController, NSTableViewDelegate, NSTableView if let selectedArticle = articleAtRow(selectedRow) { if (!selectedArticle.status.read) { - markArticles(Set([selectedArticle]), statusKey: ArticleStatusKey.read.rawValue, flag: true) + markArticles(Set([selectedArticle]), statusKey: .read, flag: true) } postTimelineSelectionDidChangeNotification(selectedArticle) } @@ -536,6 +546,8 @@ private extension TimelineViewController { } } +// MARK: - NSTableView extension + private extension NSTableView { func scrollTo(row: Int) { @@ -593,3 +605,4 @@ private extension NSTableView { visibleRowViews()?.forEach { $0.invalidateGridRect() } } } + diff --git a/Frameworks/Account/Account.swift b/Frameworks/Account/Account.swift index 33b56ce47..a433f80ea 100644 --- a/Frameworks/Account/Account.swift +++ b/Frameworks/Account/Account.swift @@ -19,6 +19,8 @@ public extension Notification.Name { public static let AccountRefreshDidFinish = Notification.Name(rawValue: "AccountRefreshDidFinish") public static let AccountRefreshProgressDidChange = Notification.Name(rawValue: "AccountRefreshProgressDidChange") public static let AccountDidDownloadArticles = Notification.Name(rawValue: "AccountDidDownloadArticles") + + public static let StatusesDidChange = Notification.Name(rawValue: "StatusesDidChange") } public enum AccountType: Int { @@ -34,9 +36,10 @@ public enum AccountType: Int { public final class Account: DisplayNameProvider, Container, Hashable { - public struct UserInfoKey { // Used by AccountDidDownloadArticles. - public static let newArticles = "newArticles" - public static let updatedArticles = "updatedArticles" + public struct UserInfoKey { + public static let newArticles = "newArticles" // AccountDidDownloadArticles + public static let updatedArticles = "updatedArticles" // AccountDidDownloadArticles + public static let statuses = "statuses" // ArticleStatusesDidChange } public let accountID: String @@ -151,9 +154,11 @@ public final class Account: DisplayNameProvider, Container, Hashable { } } - public func markArticles(_ articles: Set
, statusKey: String, flag: Bool) { + public func markArticles(_ articles: Set
, statusKey: ArticleStatus.Key, flag: Bool) { - database.mark(articles, statusKey: statusKey, flag: flag) + if let updatedStatuses = database.mark(articles, statusKey: statusKey, flag: flag) { + NotificationCenter.default.post(name: .StatusesDidChange, object: self, userInfo: [UserInfoKey.statuses: updatedStatuses]) + } } public func ensureFolder(with name: String) -> Folder? { diff --git a/Frameworks/Data/Article.swift b/Frameworks/Data/Article.swift index fcef1345a..1ed81f85a 100644 --- a/Frameworks/Data/Article.swift +++ b/Frameworks/Data/Article.swift @@ -71,4 +71,18 @@ public struct Article: Hashable { } } +public extension Set where Element == Article { + + public func articleIDs() -> Set { + + return Set(map { $0.articleID }) + } +} +public extension Array where Element == Article { + + public func articleIDs() -> [String] { + + return map { $0.articleID } + } +} diff --git a/Frameworks/Data/ArticleStatus.swift b/Frameworks/Data/ArticleStatus.swift index 5c87c8518..6bdd03620 100644 --- a/Frameworks/Data/ArticleStatus.swift +++ b/Frameworks/Data/ArticleStatus.swift @@ -14,15 +14,14 @@ import Foundation // Which is safe, because at creation time it’t not yet shared, // and it won’t be mutated ever on a background thread. -public enum ArticleStatusKey: String { - - case read = "read" - case starred = "starred" - case userDeleted = "userDeleted" -} - public final class ArticleStatus: Hashable { + public enum Key: String { + case read = "read" + case starred = "starred" + case userDeleted = "userDeleted" + } + public let articleID: String public let dateArrived: Date public let hashValue: Int @@ -46,32 +45,27 @@ public final class ArticleStatus: Hashable { self.init(articleID: articleID, read: false, starred: false, userDeleted: false, dateArrived: dateArrived) } - public func boolStatus(forKey key: String) -> Bool { + public func boolStatus(forKey key: ArticleStatus.Key) -> Bool { - if let articleStatusKey = ArticleStatusKey(rawValue: key) { - switch articleStatusKey { - case .read: - return read - case .starred: - return starred - case .userDeleted: - return userDeleted - } + switch key { + case .read: + return read + case .starred: + return starred + case .userDeleted: + return userDeleted } - return false } - public func setBoolStatus(_ status: Bool, forKey key: String) { + public func setBoolStatus(_ status: Bool, forKey key: ArticleStatus.Key) { - if let articleStatusKey = ArticleStatusKey(rawValue: key) { - switch articleStatusKey { - case .read: - read = status - case .starred: - starred = status - case .userDeleted: - userDeleted = status - } + switch key { + case .read: + read = status + case .starred: + starred = status + case .userDeleted: + userDeleted = status } } @@ -80,3 +74,19 @@ public final class ArticleStatus: Hashable { return lhs.hashValue == rhs.hashValue && lhs.articleID == rhs.articleID && lhs.dateArrived == rhs.dateArrived && lhs.read == rhs.read && lhs.starred == rhs.starred && lhs.userDeleted == rhs.userDeleted } } + +public extension Set where Element == ArticleStatus { + + public func articleIDs() -> Set { + + return Set(map { $0.articleID }) + } +} + +public extension Array where Element == ArticleStatus { + + public func articleIDs() -> [String] { + + return map { $0.articleID } + } +} diff --git a/Frameworks/Database/ArticlesTable.swift b/Frameworks/Database/ArticlesTable.swift index acefc7a31..0dbdba075 100644 --- a/Frameworks/Database/ArticlesTable.swift +++ b/Frameworks/Database/ArticlesTable.swift @@ -144,9 +144,9 @@ final class ArticlesTable: DatabaseTable { // MARK: Status - func mark(_ articles: Set
, _ statusKey: String, _ flag: Bool) { + func mark(_ articles: Set
, _ statusKey: ArticleStatus.Key, _ flag: Bool) -> Set? { - statusesTable.mark(articles.statuses(), statusKey, flag) + return statusesTable.mark(articles.statuses(), statusKey, flag) } } diff --git a/Frameworks/Database/Database.swift b/Frameworks/Database/Database.swift index 10faebb03..5f9c8d339 100644 --- a/Frameworks/Database/Database.swift +++ b/Frameworks/Database/Database.swift @@ -70,9 +70,9 @@ public final class Database { // MARK: - Status - public func mark(_ articles: Set
, statusKey: String, flag: Bool) { + public func mark(_ articles: Set
, statusKey: ArticleStatus.Key, flag: Bool) -> Set? { - articlesTable.mark(articles, statusKey, flag) + return articlesTable.mark(articles, statusKey, flag) } } diff --git a/Frameworks/Database/Extensions/Article+Database.swift b/Frameworks/Database/Extensions/Article+Database.swift index 2e4af5147..ef977b975 100644 --- a/Frameworks/Database/Extensions/Article+Database.swift +++ b/Frameworks/Database/Extensions/Article+Database.swift @@ -118,11 +118,6 @@ extension Article: DatabaseObject { extension Set where Element == Article { - func articleIDs() -> Set { - - return Set(map { $0.databaseID }) - } - func statuses() -> Set { return Set(map { $0.status }) diff --git a/Frameworks/Database/Extensions/ArticleStatus+Database.swift b/Frameworks/Database/Extensions/ArticleStatus+Database.swift index 4769a271a..b7f791f87 100644 --- a/Frameworks/Database/Extensions/ArticleStatus+Database.swift +++ b/Frameworks/Database/Extensions/ArticleStatus+Database.swift @@ -45,10 +45,3 @@ extension ArticleStatus: DatabaseObject { } } -extension Set where Element == ArticleStatus { - - func articleIDs() -> Set { - - return Set(map { $0.articleID }) - } -} diff --git a/Frameworks/Database/StatusesTable.swift b/Frameworks/Database/StatusesTable.swift index 313c62587..bda6a63d4 100644 --- a/Frameworks/Database/StatusesTable.swift +++ b/Frameworks/Database/StatusesTable.swift @@ -50,7 +50,7 @@ final class StatusesTable: DatabaseTable { // MARK: Marking - func mark(_ statuses: Set, _ statusKey: String, _ flag: Bool) { + func mark(_ statuses: Set, _ statusKey: ArticleStatus.Key, _ flag: Bool) -> Set? { // Sets flag in both memory and in database. @@ -66,13 +66,14 @@ final class StatusesTable: DatabaseTable { } if updatedStatuses.isEmpty { - return + return nil } let articleIDs = updatedStatuses.articleIDs() queue.update { (database) in self.markArticleIDs(articleIDs, statusKey, flag, database) } + return updatedStatuses } // MARK: Fetching @@ -150,9 +151,9 @@ private extension StatusesTable { // MARK: Marking - func markArticleIDs(_ articleIDs: Set, _ statusKey: String, _ flag: Bool, _ database: FMDatabase) { + func markArticleIDs(_ articleIDs: Set, _ statusKey: ArticleStatus.Key, _ flag: Bool, _ database: FMDatabase) { - updateRowsWithValue(NSNumber(value: flag), valueKey: statusKey, whereKey: DatabaseKey.articleID, matches: Array(articleIDs), database: database) + updateRowsWithValue(NSNumber(value: flag), valueKey: statusKey.rawValue, whereKey: DatabaseKey.articleID, matches: Array(articleIDs), database: database) } } diff --git a/ToDo.opml b/ToDo.opml index d0ae83b69..031993071 100644 --- a/ToDo.opml +++ b/ToDo.opml @@ -6,7 +6,7 @@ --> ToDo Tue, 12 Sep 2017 20:15:17 GMT - 0,1,23,24,27,31,37,45,46,48,63,68 + 0,23,24,27,31,37,45,46,48,63,68 0 637 42 @@ -15,9 +15,8 @@ - - - + +