diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index a682de3d6fad..6b176a366550 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -19,6 +19,8 @@ * [*] Show selected filter in the Discover navigation bar [#23956] * [*] Enable fast deceleration for filters on the Discover tab [#23954] * [*] Disable universal links support for QR code login. You can only scan the codes using the app now. [#23953] +* [*] Add scroll-to-top button to Reader streams [#23957] + 25.6 ----- diff --git a/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift b/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift index 5fece8a12885..0e1f26a7c723 100644 --- a/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift +++ b/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift @@ -5,7 +5,6 @@ import Foundation case createSheetShown case createSheetActionTapped - case createAnnouncementModalShown // Media Editor case mediaEditorShown @@ -117,6 +116,7 @@ import Foundation case readerCommentTextCopied case readerPostContextMenuButtonTapped case readerAddSiteToFavoritesTapped + case readerButtonScrollToTopTapped // Stats - Empty Stats nudges case statsPublicizeNudgeShown @@ -625,8 +625,6 @@ import Foundation return "create_sheet_shown" case .createSheetActionTapped: return "create_sheet_action_tapped" - case .createAnnouncementModalShown: - return "create_announcement_modal_shown" // Media Editor case .mediaEditorShown: return "media_editor_shown" @@ -819,6 +817,8 @@ import Foundation return "reader_post_context_menu_button_tapped" case .readerAddSiteToFavoritesTapped: return "reader_add_site_to_favorites_tapped" + case .readerButtonScrollToTopTapped: + return "reader_button_scroll_to_top_tapped" // Stats - Empty Stats nudges case .statsPublicizeNudgeShown: diff --git a/WordPress/Classes/ViewRelated/Reader/Cards/ReaderPostCell.swift b/WordPress/Classes/ViewRelated/Reader/Cards/ReaderPostCell.swift index 27922ffe48ee..811b48aab76a 100644 --- a/WordPress/Classes/ViewRelated/Reader/Cards/ReaderPostCell.swift +++ b/WordPress/Classes/ViewRelated/Reader/Cards/ReaderPostCell.swift @@ -168,7 +168,7 @@ private final class ReaderPostCellView: UIView { avatarView.widthAnchor.constraint(equalToConstant: ReaderPostCell.avatarSize), avatarView.heightAnchor.constraint(equalToConstant: ReaderPostCell.avatarSize), avatarView.centerYAnchor.constraint(equalTo: timeLabel.centerYAnchor), - avatarView.trailingAnchor.constraint(equalTo: headerView.leadingAnchor, constant: -8), + avatarView.trailingAnchor.constraint(equalTo: headerView.leadingAnchor, constant: -9), headerView.topAnchor.constraint(equalTo: topAnchor, constant: 6), headerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: insets.left), diff --git a/WordPress/Classes/ViewRelated/Reader/Cards/ReaderStreamBaseCell.swift b/WordPress/Classes/ViewRelated/Reader/Cards/ReaderStreamBaseCell.swift index 931aa1a20032..149264301cbd 100644 --- a/WordPress/Classes/ViewRelated/Reader/Cards/ReaderStreamBaseCell.swift +++ b/WordPress/Classes/ViewRelated/Reader/Cards/ReaderStreamBaseCell.swift @@ -1,7 +1,7 @@ import UIKit class ReaderStreamBaseCell: UITableViewCell { - static let insets = UIEdgeInsets(top: 0, left: 44, bottom: 0, right: 16) + static let insets = UIEdgeInsets(top: 0, left: 46, bottom: 0, right: 16) var isCompact: Bool = true { didSet { diff --git a/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderStreamViewController.swift b/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderStreamViewController.swift index 3d0ebcca28e1..295854a3c0c1 100644 --- a/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderStreamViewController.swift +++ b/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderStreamViewController.swift @@ -65,13 +65,16 @@ import AutomatticTracks return refreshControl }() + private lazy var buttonScrollToTop = ReaderButtonScrollToTop.make { [weak self] in + self?.tableView.scrollToTop(animated: true) + } + let titleView = ReaderNavigationCustomTitleView() private let loadMoreThreashold = 5 private let refreshInterval = 300 private var cleanupAndRefreshAfterScrolling = false private let recentlyBlockedSitePostObjectIDs = NSMutableArray() - private let heightForFooterView = CGFloat(44) private let estimatedHeightsCache = NSCache() private var isFeed = false private var syncIsFillingGap = false @@ -303,6 +306,7 @@ import AutomatticTracks setupTableView() setupFooterView() setupContentHandler() + setupButtonScrollToTop() observeNetworkStatus() @@ -493,9 +497,14 @@ import AutomatticTracks content.initializeContent(tableView: tableView, delegate: self) } + private func setupButtonScrollToTop() { + view.addSubview(buttonScrollToTop) + buttonScrollToTop.pinEdges([.leading, .bottom], to: view.safeAreaLayoutGuide, insets: isCompact ? UIEdgeInsets(horizontal: 8, vertical: 16) : UIEdgeInsets(.all, 20)) + } + private func setupFooterView() { var frame = footerView.frame - frame.size.height = heightForFooterView + frame.size.height = 44 footerView.frame = frame tableView.tableFooterView = footerView footerView.isHidden = true @@ -1661,6 +1670,7 @@ extension ReaderStreamViewController: UITableViewDelegate, JPScrollViewDelegate layoutEmptyStateView() processJetpackBannerVisibility(scrollView) titleView.updateAlpha(in: scrollView) + buttonScrollToTop.setButtonHidden(scrollView.contentOffset.y < view.bounds.height / 3, animated: true) } } diff --git a/WordPress/Classes/ViewRelated/Reader/ReaderButtonScrollToTop.swift b/WordPress/Classes/ViewRelated/Reader/ReaderButtonScrollToTop.swift new file mode 100644 index 000000000000..3ad73d91e0b7 --- /dev/null +++ b/WordPress/Classes/ViewRelated/Reader/ReaderButtonScrollToTop.swift @@ -0,0 +1,34 @@ +import UIKit + +final class ReaderButtonScrollToTop: UIButton { + private var isButtonHidden = false + + static func make(closure: @escaping () -> Void) -> ReaderButtonScrollToTop { + var configuration = UIButton.Configuration.bordered() + configuration.image = UIImage(systemName: "arrow.up")? + .withConfiguration(UIImage.SymbolConfiguration(pointSize: 12, weight: .regular)) + configuration.cornerStyle = .capsule + configuration.baseBackgroundColor = .secondarySystemBackground + configuration.baseForegroundColor = .label + configuration.contentInsets = .init(top: 10, leading: 10, bottom: 10, trailing: 10) + + return ReaderButtonScrollToTop(configuration: configuration, primaryAction: .init { _ in + closure() + WPAnalytics.track(.readerButtonScrollToTopTapped) + }) + } + + func setButtonHidden(_ isHidden: Bool, animated: Bool) { + guard isButtonHidden != isHidden else { return } + isButtonHidden = isHidden + + UIView.animate(withDuration: animated ? 0.33 : 0.0) { + self.alpha = isHidden ? 0 : 1 + self.isUserInteractionEnabled = !isHidden + } + } + + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + bounds.insetBy(dx: -8, dy: -10).contains(point) + } +} diff --git a/WordPress/Classes/ViewRelated/System/Floating Create Button/FancyAlertViewController+CreateButtonAnnouncement.swift b/WordPress/Classes/ViewRelated/System/Floating Create Button/FancyAlertViewController+CreateButtonAnnouncement.swift deleted file mode 100644 index 361ebdebc599..000000000000 --- a/WordPress/Classes/ViewRelated/System/Floating Create Button/FancyAlertViewController+CreateButtonAnnouncement.swift +++ /dev/null @@ -1,62 +0,0 @@ -import WordPressUI - -extension FancyAlertViewController { - private struct Strings { - static let titleText = NSLocalizedString("Streamlined navigation", comment: "Title of alert announcing new Create Button feature.") - static let bodyText = NSLocalizedString("Now there are fewer and better-organized tabs, posting shortcuts, and more, so you can find what you need fast.", comment: "Body text of alert announcing new Create Button feature.") - static let okayButtonText = NSLocalizedString("Got it!", comment: "Okay button title shown in alert announcing new Create Button feature.") - static let readMoreButtonText = NSLocalizedString("Learn more", comment: "Read more button title shown in alert announcing new Create Button feature.") - } - - private struct Analytics { - static let locationKey = "location" - static let alertKey = "alert" - } - - /// Create the fancy alert controller for the notification primer - /// - /// - Parameter approveAction: block to call when approve is tapped - /// - Returns: FancyAlertViewController of the primer - @objc static func makeCreateButtonAnnouncementAlertController(readMoreAction: @escaping ((_ controller: FancyAlertViewController) -> Void)) -> FancyAlertViewController { - - let okayButton = ButtonConfig(Strings.okayButtonText) { controller, _ in - controller.dismiss(animated: true) - } - - let readMoreButton = ButtonConfig(Strings.readMoreButtonText) { controller, _ in - readMoreAction(controller) - } - - let image = UIImage(named: "wp-illustration-ia-announcement") - - let config = FancyAlertViewController.Config(titleText: Strings.titleText, - bodyText: Strings.bodyText, - headerImage: image, - dividerPosition: .bottom, - defaultButton: readMoreButton, - cancelButton: okayButton, - appearAction: { - WPAnalytics.track(WPAnalyticsEvent.createAnnouncementModalShown, properties: [Analytics.locationKey: Analytics.alertKey]) - }, - dismissAction: {}) - - let controller = FancyAlertViewController.controllerWithConfiguration(configuration: config) - return controller - } -} - -@objc -extension UserDefaults { - private enum Keys: String { - case createButtonAlertWasDisplayed = "CreateButtonAlertWasDisplayed" - } - - var createButtonAlertWasDisplayed: Bool { - get { - return bool(forKey: Keys.createButtonAlertWasDisplayed.rawValue) - } - set { - set(newValue, forKey: Keys.createButtonAlertWasDisplayed.rawValue) - } - } -} diff --git a/WordPress/Resources/AppImages.xcassets/Illustrations/wp-illustration-ia-announcement.imageset/Contents.json b/WordPress/Resources/AppImages.xcassets/Illustrations/wp-illustration-ia-announcement.imageset/Contents.json deleted file mode 100644 index 45756b8638b6..000000000000 --- a/WordPress/Resources/AppImages.xcassets/Illustrations/wp-illustration-ia-announcement.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "wp-illustration-ia-announcement.pdf", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/WordPress/Resources/AppImages.xcassets/Illustrations/wp-illustration-ia-announcement.imageset/wp-illustration-ia-announcement.pdf b/WordPress/Resources/AppImages.xcassets/Illustrations/wp-illustration-ia-announcement.imageset/wp-illustration-ia-announcement.pdf deleted file mode 100644 index 24964d3bdb3d..000000000000 Binary files a/WordPress/Resources/AppImages.xcassets/Illustrations/wp-illustration-ia-announcement.imageset/wp-illustration-ia-announcement.pdf and /dev/null differ