diff --git a/iOS/Base.lproj/Main.storyboard b/iOS/Base.lproj/Main.storyboard index c5e435a59..e579a5ab0 100644 --- a/iOS/Base.lproj/Main.storyboard +++ b/iOS/Base.lproj/Main.storyboard @@ -119,13 +119,20 @@ - + + + + + - + + + + @@ -360,6 +367,7 @@ + diff --git a/iOS/MasterFeed/MasterFeedViewController.swift b/iOS/MasterFeed/MasterFeedViewController.swift index 6a1995f86..33702af2a 100644 --- a/iOS/MasterFeed/MasterFeedViewController.swift +++ b/iOS/MasterFeed/MasterFeedViewController.swift @@ -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() { diff --git a/iOS/MasterFeed/RefreshProgressView.swift b/iOS/MasterFeed/RefreshProgressView.swift index c41a95dfe..f3b22cd3e 100644 --- a/iOS/MasterFeed/RefreshProgressView.swift +++ b/iOS/MasterFeed/RefreshProgressView.swift @@ -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) - - override init(frame: CGRect) { - super.init(frame: frame) - commonInit() - } + private var lastLabelDisplayedTime: Date? = nil - 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) - } - } - diff --git a/iOS/MasterTimeline/MasterTimelineViewController.swift b/iOS/MasterTimeline/MasterTimelineViewController.swift index 0bc0c2cbc..f6584928c 100644 --- a/iOS/MasterTimeline/MasterTimelineViewController.swift +++ b/iOS/MasterTimeline/MasterTimelineViewController.swift @@ -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! @@ -72,7 +74,11 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner if let titleView = Bundle.main.loadNibNamed("MasterTimelineTitleView", owner: self, options: nil)?[0] as? MasterTimelineTitleView { 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() }