Implement next unread UI functionality.

This commit is contained in:
Maurice Parker 2019-04-22 18:00:26 -05:00
parent e60fb259c2
commit b619b5f905
5 changed files with 155 additions and 32 deletions

View File

@ -149,7 +149,11 @@
</connections>
</barButtonItem>
<barButtonItem style="plain" systemItem="flexibleSpace" id="93y-8j-WBh"/>
<barButtonItem enabled="NO" title="First Unread" id="2v2-jD-C9k"/>
<barButtonItem enabled="NO" title="First Unread" id="2v2-jD-C9k">
<connections>
<action selector="nextUnread:" destination="Kyk-vK-QRX" id="lwt-VU-zdW"/>
</connections>
</barButtonItem>
</toolbarItems>
<navigationItem key="navigationItem" title="Timeline" largeTitleDisplayMode="never" id="wcC-1L-ug4"/>
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>

View File

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

View File

@ -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
}

View File

@ -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..<articles.count {
let article = articles[i]
if !article.status.read {
currentArticleIndexPath = IndexPath(row: i, section: 0)
return true
}
}
return false
}
func selectNextUnreadFeedFetcher() {
guard let indexPath = currentMasterIndexPath else {
assertionFailure()
return
}
// Increment or wrap around the IndexPath
let nextIndexPath: IndexPath = {
if indexPath.row + 1 >= 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..<shadowTable.count {
for j in indexPath.row..<shadowTable[indexPath.section].count {
let nextIndexPath = IndexPath(row: j, section: i)
guard let node = nodeFor(nextIndexPath), let unreadCountProvider = node.representedObject as? UnreadCountProvider else {
assertionFailure()
return true
}
if unreadCountProvider.unreadCount > 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
}
}

View File

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