Implement next unread UI functionality.
This commit is contained in:
parent
e60fb259c2
commit
b619b5f905
@ -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"/>
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user