From b619b5f905ff44f68a1dbb5d4647e1c9db0039be Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 22 Apr 2019 18:00:26 -0500 Subject: [PATCH] Implement next unread UI functionality. --- iOS/Base.lproj/Main.storyboard | 6 +- iOS/Detail/DetailViewController.swift | 3 +- iOS/Master/MasterViewController.swift | 5 +- iOS/NavigationStateController.swift | 148 +++++++++++++++--- .../MasterTimelineViewController.swift | 25 ++- 5 files changed, 155 insertions(+), 32 deletions(-) diff --git a/iOS/Base.lproj/Main.storyboard b/iOS/Base.lproj/Main.storyboard index 5b8bd465f..fa5928103 100644 --- a/iOS/Base.lproj/Main.storyboard +++ b/iOS/Base.lproj/Main.storyboard @@ -149,7 +149,11 @@ - + + + + + diff --git a/iOS/Detail/DetailViewController.swift b/iOS/Detail/DetailViewController.swift index 7ea48f819..9bcde3608 100644 --- a/iOS/Detail/DetailViewController.swift +++ b/iOS/Detail/DetailViewController.swift @@ -55,7 +55,7 @@ class DetailViewController: UIViewController { return } - nextArticleBarButtonItem.isEnabled = false + nextUnreadBarButtonItem.isEnabled = navState?.isNextUnreadAvailable ?? false prevArticleBarButtonItem.isEnabled = navState?.isPrevArticleAvailable ?? false nextArticleBarButtonItem.isEnabled = navState?.isNextArticleAvailable ?? false @@ -101,6 +101,7 @@ class DetailViewController: UIViewController { // MARK: Actions @IBAction func nextUnread(_ sender: Any) { + navState?.selectNextUnread() } @IBAction func prevArticle(_ sender: Any) { diff --git a/iOS/Master/MasterViewController.swift b/iOS/Master/MasterViewController.swift index 13d87df26..0752bcaac 100644 --- a/iOS/Master/MasterViewController.swift +++ b/iOS/Master/MasterViewController.swift @@ -81,8 +81,9 @@ class MasterViewController: UITableViewController, UndoableCommandRunner { if let account = representedObject as? Account { if let node = navState.rootNode.childNodeRepresentingObject(account) { let sectionIndex = navState.rootNode.indexOfChild(node)! - let headerView = tableView.headerView(forSection: sectionIndex) as! MasterTableViewSectionHeader - headerView.unreadCount = account.unreadCount + if let headerView = tableView.headerView(forSection: sectionIndex) as? MasterTableViewSectionHeader { + headerView.unreadCount = account.unreadCount + } } return } diff --git a/iOS/NavigationStateController.swift b/iOS/NavigationStateController.swift index 25bebf136..b29853184 100644 --- a/iOS/NavigationStateController.swift +++ b/iOS/NavigationStateController.swift @@ -61,6 +61,7 @@ class NavigationStateController { if let fetcher = node.representedObject as? ArticleFetcher { timelineFetcher = fetcher } + NotificationCenter.default.post(name: .MasterSelectionDidChange, object: self, userInfo: nil) } } @@ -144,6 +145,10 @@ class NavigationStateController { } } + var isNextUnreadAvailable: Bool { + return appDelegate.unreadCount > 0 + } + init() { for section in treeController.rootNode.childNodes { @@ -374,8 +379,25 @@ class NavigationStateController { return indexes } + func selectNextUnread() { + + // This should never happen, but I don't want to risk throwing us + // into an infinate loop searching for an unread that isn't there. + if appDelegate.unreadCount < 1 { + return + } + + if selectNextUnreadArticleInTimeline() { + return + } + + selectNextUnreadFeedFetcher() + selectNextUnreadArticleInTimeline() + + } + } - + private extension NavigationStateController { func rebuildBackingStores() { @@ -386,6 +408,105 @@ private extension NavigationStateController { } } + func updateShowAvatars() { + + if showFeedNames { + self.showAvatars = true + return + } + + for article in articles { + if let authors = article.authors { + for author in authors { + if author.avatarURL != nil { + self.showAvatars = true + return + } + } + } + } + + self.showAvatars = false + } + + // MARK: Select Next Unread + + @discardableResult + func selectNextUnreadArticleInTimeline() -> Bool { + + let startingRow: Int = { + if let indexPath = currentArticleIndexPath { + return indexPath.row + } else { + return 0 + } + }() + + for i in startingRow..= shadowTable[indexPath.section].count { + if indexPath.section + 1 >= shadowTable.count { + return IndexPath(row: 0, section: 0) + } else { + return IndexPath(row: 0, section: indexPath.section + 1) + } + } else { + return IndexPath(row: indexPath.row + 1, section: indexPath.section) + } + }() + + if selectNextUnreadFeedFetcher(startingWith: nextIndexPath) { + return + } + selectNextUnreadFeedFetcher(startingWith: IndexPath(row: 0, section: 0)) + + } + + @discardableResult + func selectNextUnreadFeedFetcher(startingWith indexPath: IndexPath) -> Bool { + + for i in indexPath.section.. 0 { + currentMasterIndexPath = nextIndexPath + return true + } + + } + + } + + return false + + } + // MARK: Fetching Articles func fetchArticles() { @@ -486,32 +607,9 @@ private extension NavigationStateController { } } } - + return false } - - // MARK: Misc - - func updateShowAvatars() { - - if showFeedNames { - self.showAvatars = true - return - } - - for article in articles { - if let authors = article.authors { - for author in authors { - if author.avatarURL != nil { - self.showAvatars = true - return - } - } - } - } - - self.showAvatars = false - } } diff --git a/iOS/Timeline/MasterTimelineViewController.swift b/iOS/Timeline/MasterTimelineViewController.swift index fce07ce15..5599004aa 100644 --- a/iOS/Timeline/MasterTimelineViewController.swift +++ b/iOS/Timeline/MasterTimelineViewController.swift @@ -54,7 +54,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner changeToDisplayMode(splitViewController.displayMode) } - reloadUI() + resetUI() } @@ -107,6 +107,10 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner } + @IBAction func nextUnread(_ sender: Any) { + navState?.selectNextUnread() + } + // MARK: - Table view override func numberOfSections(in tableView: UITableView) -> Int { @@ -251,7 +255,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner } @objc func articlesReinitialized(_ note: Notification) { - reloadUI() + resetUI() } @objc func articleDataDidChange(_ note: Notification) { @@ -270,6 +274,8 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner } } + reloadUI() + } // MARK: Reloading @@ -350,6 +356,7 @@ private extension MasterTimelineViewController { } func changeToDisplayMode(_ displayMode: UISplitViewController.DisplayMode) { + if displayMode == .allVisible { nextUnreadButton.isEnabled = false nextUnreadButton.title = "" @@ -357,9 +364,12 @@ private extension MasterTimelineViewController { nextUnreadButton.isEnabled = false nextUnreadButton.title = NSLocalizedString("First Unread", comment: "First Unread") } + + reloadUI() + } - func reloadUI() { + func resetUI() { updateTableViewRowHeight() title = navState?.timelineName @@ -368,6 +378,15 @@ private extension MasterTimelineViewController { tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: false) } + reloadUI() + + } + + func reloadUI() { + // Since there is no hidden property on a bar button item, we just hide its title + if !(nextUnreadButton.title?.isEmpty ?? true) { + nextUnreadButton.isEnabled = navState?.isNextUnreadAvailable ?? false + } } func configureTimelineCell(_ cell: MasterTimelineTableViewCell, article: Article) {