Add refresh indicator to the Timeline. Issue #15
This commit is contained in:
parent
5a7863d447
commit
5cd163e1e4
|
@ -119,13 +119,20 @@
|
|||
</connections>
|
||||
</tableView>
|
||||
<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>
|
||||
<action selector="markAllAsRead:" destination="Kyk-vK-QRX" id="4nd-Gg-APm"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="53V-wq-bat"/>
|
||||
<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>
|
||||
<action selector="firstUnread:" destination="Kyk-vK-QRX" id="d5y-x5-Qht"/>
|
||||
</connections>
|
||||
|
@ -360,6 +367,7 @@
|
|||
</scene>
|
||||
</scenes>
|
||||
<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.circle" catalog="system" width="64" height="60"/>
|
||||
<image name="chevron.up" catalog="system" width="64" height="36"/>
|
||||
|
|
|
@ -669,10 +669,16 @@ private extension MasterFeedViewController {
|
|||
|
||||
self.refreshProgressView = refreshProgressView
|
||||
|
||||
let spaceItemButton1 = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
|
||||
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(_:)))
|
||||
setToolbarItems([refreshProgressItemButton, spaceItemButton, addNewItemButton], animated: false)
|
||||
|
||||
setToolbarItems([spaceItemButton1,
|
||||
refreshProgressItemButton,
|
||||
spaceItemButton2,
|
||||
addNewItemButton
|
||||
], animated: false)
|
||||
}
|
||||
|
||||
func updateUI() {
|
||||
|
|
|
@ -14,33 +14,40 @@ class RefreshProgressView: UIView {
|
|||
@IBOutlet weak var progressView: UIProgressView!
|
||||
@IBOutlet weak var label: UILabel!
|
||||
private lazy var progressWidth = progressView.widthAnchor.constraint(equalToConstant: 100.0)
|
||||
private var lastLabelDisplayedTime: Date? = nil
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
func commonInit() {
|
||||
override func awakeFromNib() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(progressDidChange(_:)), name: .AccountRefreshProgressDidChange, object: nil)
|
||||
|
||||
if !AccountManager.shared.combinedRefreshProgress.isComplete {
|
||||
progressChanged()
|
||||
} else {
|
||||
updateRefreshLabel()
|
||||
}
|
||||
}
|
||||
|
||||
func updateRefreshLabel() {
|
||||
if let accountLastArticleFetchEndTime = AccountManager.shared.lastArticleFetchEndTime {
|
||||
|
||||
if let lastLabelDisplayedTime = lastLabelDisplayedTime, lastLabelDisplayedTime.addingTimeInterval(2) > Date() {
|
||||
return
|
||||
}
|
||||
|
||||
lastLabelDisplayedTime = Date()
|
||||
|
||||
if Date() > accountLastArticleFetchEndTime.addingTimeInterval(1) {
|
||||
|
||||
let relativeDateTimeFormatter = RelativeDateTimeFormatter()
|
||||
relativeDateTimeFormatter.dateTimeStyle = .named
|
||||
let refreshed = relativeDateTimeFormatter.localizedString(for: accountLastArticleFetchEndTime, relativeTo: Date())
|
||||
let localizedRefreshText = NSLocalizedString("Updated %@", comment: "Updated")
|
||||
let refreshText = NSString.localizedStringWithFormat(localizedRefreshText as NSString, refreshed) as String
|
||||
label.text = refreshText
|
||||
|
||||
} else {
|
||||
label.text = NSLocalizedString("Updated just now", comment: "Updated Just Now")
|
||||
}
|
||||
|
||||
} else {
|
||||
label.text = ""
|
||||
}
|
||||
|
@ -48,7 +55,20 @@ class RefreshProgressView: UIView {
|
|||
}
|
||||
|
||||
@objc func progressDidChange(_ note: Notification) {
|
||||
progressChanged()
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private extension RefreshProgressView {
|
||||
|
||||
func progressChanged() {
|
||||
let progress = AccountManager.shared.combinedRefreshProgress
|
||||
|
||||
if progress.isComplete {
|
||||
|
@ -60,18 +80,12 @@ class RefreshProgressView: UIView {
|
|||
self.progressWidth.isActive = false
|
||||
}
|
||||
} else {
|
||||
lastLabelDisplayedTime = nil
|
||||
label.isHidden = true
|
||||
progressView.isHidden = false
|
||||
self.progressWidth.isActive = true
|
||||
let percent = Float(progress.numberCompleted) / Float(progress.numberOfTasks)
|
||||
progressView.progress = percent
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
|||
private var iconSize = IconSize.medium
|
||||
private lazy var feedTapGestureRecognizer = UITapGestureRecognizer(target: self, action:#selector(showFeedInspector(_:)))
|
||||
|
||||
private var refreshProgressView: RefreshProgressView?
|
||||
|
||||
@IBOutlet weak var filterButton: UIBarButtonItem!
|
||||
@IBOutlet weak var markAllAsReadButton: UIBarButtonItem!
|
||||
@IBOutlet weak var firstUnreadButton: UIBarButtonItem!
|
||||
|
@ -73,6 +75,10 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
|||
navigationItem.titleView = titleView
|
||||
}
|
||||
|
||||
refreshControl = UIRefreshControl()
|
||||
refreshControl!.addTarget(self, action: #selector(refreshAccounts(_:)), for: .valueChanged)
|
||||
|
||||
configureToolbar()
|
||||
resetUI(resetScroll: true)
|
||||
|
||||
// Load the table and then scroll to the saved position if available
|
||||
|
@ -130,6 +136,15 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
|||
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
|
||||
|
||||
@objc func selectNextUp(_ sender: Any?) {
|
||||
|
@ -516,6 +531,22 @@ extension MasterTimelineViewController: UISearchBarDelegate {
|
|||
|
||||
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) {
|
||||
|
||||
title = coordinator.timelineFeed?.nameForDisplay ?? "Timeline"
|
||||
|
@ -558,6 +589,7 @@ private extension MasterTimelineViewController {
|
|||
}
|
||||
|
||||
func updateUI() {
|
||||
refreshProgressView?.updateRefreshLabel()
|
||||
updateTitleUnreadCount()
|
||||
updateToolbar()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue