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) {