Make First Unread scroll to first unread in timeline.
Update Mark As Read and other unread dependent UI respond to unread count changing.
This commit is contained in:
parent
e54056ceac
commit
e1b031e6db
@ -99,7 +99,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</barButtonItem>
|
</barButtonItem>
|
||||||
</toolbarItems>
|
</toolbarItems>
|
||||||
<navigationItem key="navigationItem" id="mOI-FS-AaM"/>
|
<navigationItem key="navigationItem" largeTitleDisplayMode="never" id="mOI-FS-AaM"/>
|
||||||
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="actionBarButtonItem" destination="9Ut-5B-JKP" id="9bO-kz-cTz"/>
|
<outlet property="actionBarButtonItem" destination="9Ut-5B-JKP" id="9bO-kz-cTz"/>
|
||||||
@ -149,16 +149,17 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</barButtonItem>
|
</barButtonItem>
|
||||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="93y-8j-WBh"/>
|
<barButtonItem style="plain" systemItem="flexibleSpace" id="93y-8j-WBh"/>
|
||||||
<barButtonItem enabled="NO" title="First Unread" id="2v2-jD-C9k">
|
<barButtonItem title="First Unread" id="2v2-jD-C9k">
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="nextUnread:" destination="Kyk-vK-QRX" id="lwt-VU-zdW"/>
|
<action selector="firstUnread:" destination="Kyk-vK-QRX" id="d5y-x5-Qht"/>
|
||||||
</connections>
|
</connections>
|
||||||
</barButtonItem>
|
</barButtonItem>
|
||||||
</toolbarItems>
|
</toolbarItems>
|
||||||
<navigationItem key="navigationItem" title="Timeline" largeTitleDisplayMode="never" id="wcC-1L-ug4"/>
|
<navigationItem key="navigationItem" title="Timeline" largeTitleDisplayMode="never" id="wcC-1L-ug4"/>
|
||||||
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="nextUnreadButton" destination="2v2-jD-C9k" id="9rf-5I-18f"/>
|
<outlet property="firstUnreadButton" destination="2v2-jD-C9k" id="8NP-Uc-3Fn"/>
|
||||||
|
<outlet property="markAllAsReadButton" destination="fTv-eX-72r" id="12S-lN-Sxa"/>
|
||||||
</connections>
|
</connections>
|
||||||
</tableViewController>
|
</tableViewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="nzm-Gf-Xce" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="nzm-Gf-Xce" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
@ -244,6 +245,9 @@
|
|||||||
</barButtonItem>
|
</barButtonItem>
|
||||||
</navigationItem>
|
</navigationItem>
|
||||||
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="markAllAsReadButton" destination="ddj-Ya-Wol" id="jjr-OK-4zl"/>
|
||||||
|
</connections>
|
||||||
</tableViewController>
|
</tableViewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Rux-fX-hf1" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="Rux-fX-hf1" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
|
@ -26,14 +26,18 @@ class DetailViewController: UIViewController {
|
|||||||
weak var navState: NavigationStateController?
|
weak var navState: NavigationStateController?
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
|
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
self.navigationController?.navigationItem.largeTitleDisplayMode = .never
|
|
||||||
webView.navigationDelegate = self
|
webView.navigationDelegate = self
|
||||||
|
|
||||||
markAsRead()
|
markAsRead()
|
||||||
reloadUI()
|
updateUI()
|
||||||
reloadHTML()
|
reloadHTML()
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(articleSelectionDidChange(_:)), name: .ArticleSelectionDidChange, object: navState)
|
NotificationCenter.default.addObserver(self, selector: #selector(articleSelectionDidChange(_:)), name: .ArticleSelectionDidChange, object: navState)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func markAsRead() {
|
func markAsRead() {
|
||||||
@ -42,7 +46,7 @@ class DetailViewController: UIViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func reloadUI() {
|
func updateUI() {
|
||||||
|
|
||||||
guard let article = navState?.currentArticle else {
|
guard let article = navState?.currentArticle else {
|
||||||
nextUnreadBarButtonItem.isEnabled = false
|
nextUnreadBarButtonItem.isEnabled = false
|
||||||
@ -55,7 +59,7 @@ class DetailViewController: UIViewController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
nextUnreadBarButtonItem.isEnabled = navState?.isNextUnreadAvailable ?? false
|
nextUnreadBarButtonItem.isEnabled = navState?.isAnyUnreadAvailable ?? false
|
||||||
prevArticleBarButtonItem.isEnabled = navState?.isPrevArticleAvailable ?? false
|
prevArticleBarButtonItem.isEnabled = navState?.isPrevArticleAvailable ?? false
|
||||||
nextArticleBarButtonItem.isEnabled = navState?.isNextArticleAvailable ?? false
|
nextArticleBarButtonItem.isEnabled = navState?.isNextArticleAvailable ?? false
|
||||||
|
|
||||||
@ -83,18 +87,24 @@ class DetailViewController: UIViewController {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Notifications
|
||||||
|
|
||||||
|
@objc dynamic func unreadCountDidChange(_ notification: Notification) {
|
||||||
|
updateUI()
|
||||||
|
}
|
||||||
|
|
||||||
@objc func statusesDidChange(_ note: Notification) {
|
@objc func statusesDidChange(_ note: Notification) {
|
||||||
guard let articles = note.userInfo?[Account.UserInfoKey.articles] as? Set<Article> else {
|
guard let articles = note.userInfo?[Account.UserInfoKey.articles] as? Set<Article> else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if articles.count == 1 && articles.first?.articleID == navState?.currentArticle?.articleID {
|
if articles.count == 1 && articles.first?.articleID == navState?.currentArticle?.articleID {
|
||||||
reloadUI()
|
updateUI()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func articleSelectionDidChange(_ note: Notification) {
|
@objc func articleSelectionDidChange(_ note: Notification) {
|
||||||
markAsRead()
|
markAsRead()
|
||||||
reloadUI()
|
updateUI()
|
||||||
reloadHTML()
|
reloadHTML()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@ import RSTree
|
|||||||
|
|
||||||
class MasterViewController: UITableViewController, UndoableCommandRunner {
|
class MasterViewController: UITableViewController, UndoableCommandRunner {
|
||||||
|
|
||||||
|
@IBOutlet weak var markAllAsReadButton: UIBarButtonItem!
|
||||||
|
|
||||||
var undoableCommands = [UndoableCommand]()
|
var undoableCommands = [UndoableCommand]()
|
||||||
|
|
||||||
let navState = NavigationStateController()
|
let navState = NavigationStateController()
|
||||||
@ -29,18 +31,20 @@ class MasterViewController: UITableViewController, UndoableCommandRunner {
|
|||||||
|
|
||||||
tableView.register(MasterTableViewSectionHeader.self, forHeaderFooterViewReuseIdentifier: "SectionHeader")
|
tableView.register(MasterTableViewSectionHeader.self, forHeaderFooterViewReuseIdentifier: "SectionHeader")
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(backingStoresDidRebuild(_:)), name: .BackingStoresDidRebuild, object: nil)
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(feedSettingDidChange(_:)), name: .FeedSettingDidChange, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(feedSettingDidChange(_:)), name: .FeedSettingDidChange, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(userDidAddFeed(_:)), name: .UserDidAddFeed, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(userDidAddFeed(_:)), name: .UserDidAddFeed, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(progressDidChange(_:)), name: .AccountRefreshProgressDidChange, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(progressDidChange(_:)), name: .AccountRefreshProgressDidChange, object: nil)
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(backingStoresDidRebuild(_:)), name: .BackingStoresDidRebuild, object: navState)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(masterSelectionDidChange(_:)), name: .MasterSelectionDidChange, object: navState)
|
NotificationCenter.default.addObserver(self, selector: #selector(masterSelectionDidChange(_:)), name: .MasterSelectionDidChange, object: navState)
|
||||||
|
|
||||||
refreshControl = UIRefreshControl()
|
refreshControl = UIRefreshControl()
|
||||||
refreshControl!.addTarget(self, action: #selector(refreshAccounts(_:)), for: .valueChanged)
|
refreshControl!.addTarget(self, action: #selector(refreshAccounts(_:)), for: .valueChanged)
|
||||||
|
|
||||||
|
updateUI()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
@ -89,6 +93,7 @@ class MasterViewController: UITableViewController, UndoableCommandRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
configureUnreadCountForCellsForRepresentedObject(representedObject as AnyObject)
|
configureUnreadCountForCellsForRepresentedObject(representedObject as AnyObject)
|
||||||
|
updateUI()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,13 +143,11 @@ class MasterViewController: UITableViewController, UndoableCommandRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func masterSelectionDidChange(_ note: Notification) {
|
@objc func masterSelectionDidChange(_ note: Notification) {
|
||||||
|
|
||||||
if let indexPath = navState.currentMasterIndexPath {
|
if let indexPath = navState.currentMasterIndexPath {
|
||||||
if tableView.indexPathForSelectedRow != indexPath {
|
if tableView.indexPathForSelectedRow != indexPath {
|
||||||
tableView.selectRow(at: indexPath, animated: true, scrollPosition: .middle)
|
tableView.selectRow(at: indexPath, animated: true, scrollPosition: .middle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Table View
|
// MARK: Table View
|
||||||
@ -625,6 +628,10 @@ private extension MasterViewController {
|
|||||||
AccountManager.shared.refreshAll()
|
AccountManager.shared.refreshAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateUI() {
|
||||||
|
markAllAsReadButton.isEnabled = navState.isAnyUnreadAvailable
|
||||||
|
}
|
||||||
|
|
||||||
func configureCellsForRepresentedObject(_ representedObject: AnyObject) {
|
func configureCellsForRepresentedObject(_ representedObject: AnyObject) {
|
||||||
|
|
||||||
applyToCellsForRepresentedObject(representedObject, configure)
|
applyToCellsForRepresentedObject(representedObject, configure)
|
||||||
|
@ -114,6 +114,15 @@ class NavigationStateController {
|
|||||||
return IndexPath(row: indexPath.row + 1, section: indexPath.section)
|
return IndexPath(row: indexPath.row + 1, section: indexPath.section)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var firstUnreadArticleIndexPath: IndexPath? {
|
||||||
|
for (row, article) in articles.enumerated() {
|
||||||
|
if !article.status.read {
|
||||||
|
return IndexPath(row: row, section: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var currentArticle: Article? {
|
var currentArticle: Article? {
|
||||||
if let indexPath = currentArticleIndexPath {
|
if let indexPath = currentArticleIndexPath {
|
||||||
return articles[indexPath.row]
|
return articles[indexPath.row]
|
||||||
@ -145,7 +154,14 @@ class NavigationStateController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var isNextUnreadAvailable: Bool {
|
var isTimelineUnreadAvailable: Bool {
|
||||||
|
if let unreadProvider = timelineFetcher as? UnreadCountProvider {
|
||||||
|
return unreadProvider.unreadCount > 0
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var isAnyUnreadAvailable: Bool {
|
||||||
return appDelegate.unreadCount > 0
|
return appDelegate.unreadCount > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,8 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
|||||||
return navState?.showFeedNames ?? false ? rowHeightWithFeedName : rowHeightWithoutFeedName
|
return navState?.showFeedNames ?? false ? rowHeightWithFeedName : rowHeightWithoutFeedName
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBOutlet weak var nextUnreadButton: UIBarButtonItem!
|
@IBOutlet weak var markAllAsReadButton: UIBarButtonItem!
|
||||||
|
@IBOutlet weak var firstUnreadButton: UIBarButtonItem!
|
||||||
|
|
||||||
weak var navState: NavigationStateController?
|
weak var navState: NavigationStateController?
|
||||||
var undoableCommands = [UndoableCommand]()
|
var undoableCommands = [UndoableCommand]()
|
||||||
@ -34,6 +35,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
|||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
updateRowHeights()
|
updateRowHeights()
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(feedIconDidBecomeAvailable(_:)), name: .FeedIconDidBecomeAvailable, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(feedIconDidBecomeAvailable(_:)), name: .FeedIconDidBecomeAvailable, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(avatarDidBecomeAvailable(_:)), name: .AvatarDidBecomeAvailable, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(avatarDidBecomeAvailable(_:)), name: .AvatarDidBecomeAvailable, object: nil)
|
||||||
@ -49,11 +51,6 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
|||||||
refreshControl = UIRefreshControl()
|
refreshControl = UIRefreshControl()
|
||||||
refreshControl!.addTarget(self, action: #selector(refreshAccounts(_:)), for: .valueChanged)
|
refreshControl!.addTarget(self, action: #selector(refreshAccounts(_:)), for: .valueChanged)
|
||||||
|
|
||||||
if let splitViewController = splitViewController {
|
|
||||||
splitViewController.delegate = self
|
|
||||||
changeToDisplayMode(splitViewController.displayMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
resetUI()
|
resetUI()
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -107,8 +104,10 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func nextUnread(_ sender: Any) {
|
@IBAction func firstUnread(_ sender: Any) {
|
||||||
navState?.selectNextUnread()
|
if let indexPath = navState?.firstUnreadArticleIndexPath {
|
||||||
|
tableView.scrollToRow(at: indexPath, at: .middle, animated: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Table view
|
// MARK: - Table view
|
||||||
@ -193,6 +192,10 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc dynamic func unreadCountDidChange(_ notification: Notification) {
|
||||||
|
updateUI()
|
||||||
|
}
|
||||||
|
|
||||||
@objc func statusesDidChange(_ note: Notification) {
|
@objc func statusesDidChange(_ note: Notification) {
|
||||||
|
|
||||||
guard let articles = note.userInfo?[Account.UserInfoKey.articles] as? Set<Article> else {
|
guard let articles = note.userInfo?[Account.UserInfoKey.articles] as? Set<Article> else {
|
||||||
@ -276,7 +279,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadUI()
|
updateUI()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,14 +344,6 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MasterTimelineViewController: UISplitViewControllerDelegate {
|
|
||||||
|
|
||||||
func splitViewController(_ svc: UISplitViewController, willChangeTo displayMode: UISplitViewController.DisplayMode) {
|
|
||||||
changeToDisplayMode(displayMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private extension MasterTimelineViewController {
|
private extension MasterTimelineViewController {
|
||||||
@ -357,20 +352,6 @@ private extension MasterTimelineViewController {
|
|||||||
AccountManager.shared.refreshAll()
|
AccountManager.shared.refreshAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
func changeToDisplayMode(_ displayMode: UISplitViewController.DisplayMode) {
|
|
||||||
|
|
||||||
if UIDevice.current.userInterfaceIdiom == .pad && displayMode == .allVisible {
|
|
||||||
nextUnreadButton.isEnabled = false
|
|
||||||
nextUnreadButton.title = ""
|
|
||||||
} else {
|
|
||||||
nextUnreadButton.isEnabled = false
|
|
||||||
nextUnreadButton.title = NSLocalizedString("First Unread", comment: "First Unread")
|
|
||||||
}
|
|
||||||
|
|
||||||
reloadUI()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func resetUI() {
|
func resetUI() {
|
||||||
|
|
||||||
updateTableViewRowHeight()
|
updateTableViewRowHeight()
|
||||||
@ -380,15 +361,13 @@ private extension MasterTimelineViewController {
|
|||||||
tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: false)
|
tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadUI()
|
updateUI()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func reloadUI() {
|
func updateUI() {
|
||||||
// Since there is no hidden property on a bar button item, we just hide its title
|
markAllAsReadButton.isEnabled = navState?.isTimelineUnreadAvailable ?? false
|
||||||
if !(nextUnreadButton.title?.isEmpty ?? true) {
|
firstUnreadButton.isEnabled = navState?.isTimelineUnreadAvailable ?? false
|
||||||
nextUnreadButton.isEnabled = navState?.isNextUnreadAvailable ?? false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureTimelineCell(_ cell: MasterTimelineTableViewCell, article: Article) {
|
func configureTimelineCell(_ cell: MasterTimelineTableViewCell, article: Article) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user