Add refresh indicator to the Timeline. Issue #15
This commit is contained in:
parent
5a7863d447
commit
5cd163e1e4
|
@ -119,13 +119,20 @@
|
||||||
</connections>
|
</connections>
|
||||||
</tableView>
|
</tableView>
|
||||||
<toolbarItems>
|
<toolbarItems>
|
||||||
<barButtonItem title="Mark All as Read" id="fTv-eX-72r">
|
<barButtonItem image="asterisk.circle" catalog="system" id="fTv-eX-72r">
|
||||||
|
<userDefinedRuntimeAttributes>
|
||||||
|
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Mark All as Read"/>
|
||||||
|
</userDefinedRuntimeAttributes>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="markAllAsRead:" destination="Kyk-vK-QRX" id="4nd-Gg-APm"/>
|
<action selector="markAllAsRead:" destination="Kyk-vK-QRX" id="4nd-Gg-APm"/>
|
||||||
</connections>
|
</connections>
|
||||||
</barButtonItem>
|
</barButtonItem>
|
||||||
|
<barButtonItem style="plain" systemItem="flexibleSpace" id="53V-wq-bat"/>
|
||||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="93y-8j-WBh"/>
|
<barButtonItem style="plain" systemItem="flexibleSpace" id="93y-8j-WBh"/>
|
||||||
<barButtonItem title="First Unread" id="2v2-jD-C9k">
|
<barButtonItem image="chevron.down.circle" catalog="system" id="2v2-jD-C9k">
|
||||||
|
<userDefinedRuntimeAttributes>
|
||||||
|
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="First Unread"/>
|
||||||
|
</userDefinedRuntimeAttributes>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="firstUnread:" destination="Kyk-vK-QRX" id="d5y-x5-Qht"/>
|
<action selector="firstUnread:" destination="Kyk-vK-QRX" id="d5y-x5-Qht"/>
|
||||||
</connections>
|
</connections>
|
||||||
|
@ -360,6 +367,7 @@
|
||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
|
<image name="asterisk.circle" catalog="system" width="64" height="60"/>
|
||||||
<image name="chevron.down" catalog="system" width="64" height="36"/>
|
<image name="chevron.down" catalog="system" width="64" height="36"/>
|
||||||
<image name="chevron.down.circle" catalog="system" width="64" height="60"/>
|
<image name="chevron.down.circle" catalog="system" width="64" height="60"/>
|
||||||
<image name="chevron.up" catalog="system" width="64" height="36"/>
|
<image name="chevron.up" catalog="system" width="64" height="36"/>
|
||||||
|
|
|
@ -669,10 +669,16 @@ private extension MasterFeedViewController {
|
||||||
|
|
||||||
self.refreshProgressView = refreshProgressView
|
self.refreshProgressView = refreshProgressView
|
||||||
|
|
||||||
|
let spaceItemButton1 = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
|
||||||
let refreshProgressItemButton = UIBarButtonItem(customView: refreshProgressView)
|
let refreshProgressItemButton = UIBarButtonItem(customView: refreshProgressView)
|
||||||
let spaceItemButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
|
let spaceItemButton2 = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
|
||||||
addNewItemButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(add(_:)))
|
addNewItemButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(add(_:)))
|
||||||
setToolbarItems([refreshProgressItemButton, spaceItemButton, addNewItemButton], animated: false)
|
|
||||||
|
setToolbarItems([spaceItemButton1,
|
||||||
|
refreshProgressItemButton,
|
||||||
|
spaceItemButton2,
|
||||||
|
addNewItemButton
|
||||||
|
], animated: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUI() {
|
func updateUI() {
|
||||||
|
|
|
@ -14,33 +14,40 @@ class RefreshProgressView: UIView {
|
||||||
@IBOutlet weak var progressView: UIProgressView!
|
@IBOutlet weak var progressView: UIProgressView!
|
||||||
@IBOutlet weak var label: UILabel!
|
@IBOutlet weak var label: UILabel!
|
||||||
private lazy var progressWidth = progressView.widthAnchor.constraint(equalToConstant: 100.0)
|
private lazy var progressWidth = progressView.widthAnchor.constraint(equalToConstant: 100.0)
|
||||||
|
private var lastLabelDisplayedTime: Date? = nil
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override func awakeFromNib() {
|
||||||
super.init(frame: frame)
|
|
||||||
commonInit()
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
super.init(coder: coder)
|
|
||||||
commonInit()
|
|
||||||
}
|
|
||||||
|
|
||||||
func commonInit() {
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(progressDidChange(_:)), name: .AccountRefreshProgressDidChange, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(progressDidChange(_:)), name: .AccountRefreshProgressDidChange, object: nil)
|
||||||
|
|
||||||
|
if !AccountManager.shared.combinedRefreshProgress.isComplete {
|
||||||
|
progressChanged()
|
||||||
|
} else {
|
||||||
|
updateRefreshLabel()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateRefreshLabel() {
|
func updateRefreshLabel() {
|
||||||
if let accountLastArticleFetchEndTime = AccountManager.shared.lastArticleFetchEndTime {
|
if let accountLastArticleFetchEndTime = AccountManager.shared.lastArticleFetchEndTime {
|
||||||
|
|
||||||
|
if let lastLabelDisplayedTime = lastLabelDisplayedTime, lastLabelDisplayedTime.addingTimeInterval(2) > Date() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lastLabelDisplayedTime = Date()
|
||||||
|
|
||||||
if Date() > accountLastArticleFetchEndTime.addingTimeInterval(1) {
|
if Date() > accountLastArticleFetchEndTime.addingTimeInterval(1) {
|
||||||
|
|
||||||
let relativeDateTimeFormatter = RelativeDateTimeFormatter()
|
let relativeDateTimeFormatter = RelativeDateTimeFormatter()
|
||||||
relativeDateTimeFormatter.dateTimeStyle = .named
|
relativeDateTimeFormatter.dateTimeStyle = .named
|
||||||
let refreshed = relativeDateTimeFormatter.localizedString(for: accountLastArticleFetchEndTime, relativeTo: Date())
|
let refreshed = relativeDateTimeFormatter.localizedString(for: accountLastArticleFetchEndTime, relativeTo: Date())
|
||||||
let localizedRefreshText = NSLocalizedString("Updated %@", comment: "Updated")
|
let localizedRefreshText = NSLocalizedString("Updated %@", comment: "Updated")
|
||||||
let refreshText = NSString.localizedStringWithFormat(localizedRefreshText as NSString, refreshed) as String
|
let refreshText = NSString.localizedStringWithFormat(localizedRefreshText as NSString, refreshed) as String
|
||||||
label.text = refreshText
|
label.text = refreshText
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
label.text = NSLocalizedString("Updated just now", comment: "Updated Just Now")
|
label.text = NSLocalizedString("Updated just now", comment: "Updated Just Now")
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
label.text = ""
|
label.text = ""
|
||||||
}
|
}
|
||||||
|
@ -48,7 +55,20 @@ class RefreshProgressView: UIView {
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func progressDidChange(_ note: Notification) {
|
@objc func progressDidChange(_ note: Notification) {
|
||||||
|
progressChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
NotificationCenter.default.removeObserver(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private extension RefreshProgressView {
|
||||||
|
|
||||||
|
func progressChanged() {
|
||||||
let progress = AccountManager.shared.combinedRefreshProgress
|
let progress = AccountManager.shared.combinedRefreshProgress
|
||||||
|
|
||||||
if progress.isComplete {
|
if progress.isComplete {
|
||||||
|
@ -60,18 +80,12 @@ class RefreshProgressView: UIView {
|
||||||
self.progressWidth.isActive = false
|
self.progressWidth.isActive = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
lastLabelDisplayedTime = nil
|
||||||
label.isHidden = true
|
label.isHidden = true
|
||||||
progressView.isHidden = false
|
progressView.isHidden = false
|
||||||
self.progressWidth.isActive = true
|
self.progressWidth.isActive = true
|
||||||
let percent = Float(progress.numberCompleted) / Float(progress.numberOfTasks)
|
let percent = Float(progress.numberCompleted) / Float(progress.numberOfTasks)
|
||||||
progressView.progress = percent
|
progressView.progress = percent
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
|
||||||
NotificationCenter.default.removeObserver(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||||
private var iconSize = IconSize.medium
|
private var iconSize = IconSize.medium
|
||||||
private lazy var feedTapGestureRecognizer = UITapGestureRecognizer(target: self, action:#selector(showFeedInspector(_:)))
|
private lazy var feedTapGestureRecognizer = UITapGestureRecognizer(target: self, action:#selector(showFeedInspector(_:)))
|
||||||
|
|
||||||
|
private var refreshProgressView: RefreshProgressView?
|
||||||
|
|
||||||
@IBOutlet weak var filterButton: UIBarButtonItem!
|
@IBOutlet weak var filterButton: UIBarButtonItem!
|
||||||
@IBOutlet weak var markAllAsReadButton: UIBarButtonItem!
|
@IBOutlet weak var markAllAsReadButton: UIBarButtonItem!
|
||||||
@IBOutlet weak var firstUnreadButton: UIBarButtonItem!
|
@IBOutlet weak var firstUnreadButton: UIBarButtonItem!
|
||||||
|
@ -73,6 +75,10 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||||
navigationItem.titleView = titleView
|
navigationItem.titleView = titleView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshControl = UIRefreshControl()
|
||||||
|
refreshControl!.addTarget(self, action: #selector(refreshAccounts(_:)), for: .valueChanged)
|
||||||
|
|
||||||
|
configureToolbar()
|
||||||
resetUI(resetScroll: true)
|
resetUI(resetScroll: true)
|
||||||
|
|
||||||
// Load the table and then scroll to the saved position if available
|
// Load the table and then scroll to the saved position if available
|
||||||
|
@ -130,6 +136,15 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||||
coordinator.selectFirstUnread()
|
coordinator.selectFirstUnread()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func refreshAccounts(_ sender: Any) {
|
||||||
|
refreshControl?.endRefreshing()
|
||||||
|
// This is a hack to make sure that an error dialog doesn't interfere with dismissing the refreshControl.
|
||||||
|
// If the error dialog appears too closely to the call to endRefreshing, then the refreshControl never disappears.
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||||
|
AccountManager.shared.refreshAll(errorHandler: ErrorHandler.present(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Keyboard shortcuts
|
// MARK: Keyboard shortcuts
|
||||||
|
|
||||||
@objc func selectNextUp(_ sender: Any?) {
|
@objc func selectNextUp(_ sender: Any?) {
|
||||||
|
@ -516,6 +531,22 @@ extension MasterTimelineViewController: UISearchBarDelegate {
|
||||||
|
|
||||||
private extension MasterTimelineViewController {
|
private extension MasterTimelineViewController {
|
||||||
|
|
||||||
|
func configureToolbar() {
|
||||||
|
|
||||||
|
if coordinator.isThreePanelMode {
|
||||||
|
firstUnreadButton.isHidden = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let refreshProgressView = Bundle.main.loadNibNamed("RefreshProgressView", owner: self, options: nil)?[0] as? RefreshProgressView else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.refreshProgressView = refreshProgressView
|
||||||
|
let refreshProgressItemButton = UIBarButtonItem(customView: refreshProgressView)
|
||||||
|
toolbarItems?.insert(refreshProgressItemButton, at: 2)
|
||||||
|
}
|
||||||
|
|
||||||
func resetUI(resetScroll: Bool) {
|
func resetUI(resetScroll: Bool) {
|
||||||
|
|
||||||
title = coordinator.timelineFeed?.nameForDisplay ?? "Timeline"
|
title = coordinator.timelineFeed?.nameForDisplay ?? "Timeline"
|
||||||
|
@ -558,6 +589,7 @@ private extension MasterTimelineViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUI() {
|
func updateUI() {
|
||||||
|
refreshProgressView?.updateRefreshLabel()
|
||||||
updateTitleUnreadCount()
|
updateTitleUnreadCount()
|
||||||
updateToolbar()
|
updateToolbar()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue