Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Notifications Refresh] Replace tabs with dropdown in notifications screen #23054

36 changes: 20 additions & 16 deletions WordPress/Classes/Extensions/UIButton+Extensions.swift
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
import UIKit

extension UIButton {

/// Creates a bar button item that looks like the native title menu
/// (see `navigationItem.titleMenuProvider`, iOS 16+).
static func makeMenu(title: String, menu: UIMenu) -> UIButton {
let button = UIButton(configuration: {
var configuration = UIButton.Configuration.plain()
configuration.title = title
configuration.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer {
var attributes = $0
attributes.font = WPStyleGuide.fontForTextStyle(.headline)
return attributes
}
configuration.image = UIImage(systemName: "chevron.down.circle.fill")?.withBaselineOffset(fromBottom: 4)
configuration.preferredSymbolConfigurationForImage = UIImage.SymbolConfiguration(paletteColors: [.secondaryLabel, .secondarySystemFill])
.applying(UIImage.SymbolConfiguration(font: WPStyleGuide.fontForTextStyle(.subheadline, fontWeight: .semibold)))
configuration.imagePlacement = .trailing
configuration.imagePadding = 4
configuration.baseForegroundColor = .label
return configuration
}())
let button = UIButton(configuration: menuButtonConfiguration(title: title))
button.menu = menu
button.showsMenuAsPrimaryAction = true
return button
}

/// The configuration of a bar button item that looks like the native title menu
static func menuButtonConfiguration(title: String) -> UIButton.Configuration {
var configuration = UIButton.Configuration.plain()
configuration.title = title
configuration.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer {
var attributes = $0
attributes.font = WPStyleGuide.fontForTextStyle(.headline)
return attributes
}
configuration.image = UIImage(systemName: "chevron.down.circle.fill")?.withBaselineOffset(fromBottom: 4)
configuration.preferredSymbolConfigurationForImage = UIImage.SymbolConfiguration(paletteColors: [.secondaryLabel, .secondarySystemFill])
.applying(UIImage.SymbolConfiguration(font: WPStyleGuide.fontForTextStyle(.subheadline, fontWeight: .semibold)))
configuration.imagePlacement = .trailing
configuration.imagePadding = 4
configuration.baseForegroundColor = .label
return configuration
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ extension NotificationsViewController {

enum Strings {
enum NavigationBar {
static let title = NSLocalizedString(
"notifications.navigationBar.title",
value: "Notifications",
comment: "Notifications View Controller title"
)
static let allNotificationsTitle = NSLocalizedString(
"notifications.navigationBar.filters.allNotifications",
value: "All Notifications",
comment: "The navigation bar title when 'All' filter is selected"
)
static let notificationSettingsActionTitle = NSLocalizedString(
"notificationsViewController.navigationBar.action.settings",
value: "Notification Settings",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ class NotificationsViewController: UIViewController, UITableViewDataSource, UITa
///
@IBOutlet var tableHeaderView: UIView!

/// Filtering Tab Bar
///
@IBOutlet weak var filterTabBar: FilterTabBar!

/// Jetpack Banner View
/// Only visible in WordPress
///
Expand Down Expand Up @@ -73,15 +69,41 @@ class NotificationsViewController: UIViewController, UITableViewDataSource, UITa
///
fileprivate var unreadNotificationIds = Set<NSManagedObjectID>()

/// Used to store (and restore) the currently selected filter segment.
///
fileprivate var restorableSelectedSegmentIndex: Int = 0

/// Used to keep track of the currently selected notification,
/// to restore it between table view reloads and state restoration.
///
fileprivate var selectedNotification: Notification? = nil

/// Menu listing the notifications filters.
///
fileprivate lazy var filtersMenuButton: UIButton = {
let firstSection = [Filter.none]
let secondSection = Filter.allCases.filter { $0 != .none }
let children: [UIMenuElement] = [firstSection, secondSection].map { section -> UIMenuElement in
let actions = section.map { filter in
UIAction(title: filter.title, image: nil) { [weak self] _ in
self?.filtersMenuTapped(filter: filter)
}
}
if actions.count == 1, let action = actions.first {
return action
} else {
return UIMenu(options: .displayInline, children: actions)
}
}
let menu = UIMenu(children: children)
return UIButton.makeMenu(title: filtersMenuButtonTitle(for: filter), menu: menu)
}()

/// Holds the current filter state, updating the filter's menu configuration and reloading results on change.
///
fileprivate var filter: Filter = .none {
didSet {
filtersMenuButton.configuration = UIButton.menuButtonConfiguration(title: filtersMenuButtonTitle(for: filter))
reloadResultsController()
}
}

/// JetpackLoginVC being presented.
///
internal var jetpackLoginViewController: JetpackLoginViewController? = nil
Expand Down Expand Up @@ -139,7 +161,6 @@ class NotificationsViewController: UIViewController, UITableViewDataSource, UITa
setupTableFooterView()
setupRefreshControl()
setupNoResultsView()
setupFilterBar()

tableView.tableHeaderView = tableHeaderView
setupConstraints()
Expand Down Expand Up @@ -452,10 +473,10 @@ private extension NotificationsViewController {
// we are using a space character because we need a non-empty string to ensure a smooth
// transition back, with large titles enabled.
navigationItem.backBarButtonItem = UIBarButtonItem(title: " ", style: .plain, target: nil, action: nil)
navigationItem.title = NSLocalizedString("Notifications", comment: "Notifications View Controller title")
navigationItem.title = Strings.NavigationBar.title
}

func updateNavigationItems() {
func configureNavigationItems() {
let moreMenuItems = UIDeferredMenuElement.uncached { [weak self] completion in
guard let self else {
completion([])
Expand All @@ -475,6 +496,13 @@ private extension NotificationsViewController {
}()
}

private func filtersMenuButtonTitle(for filter: Filter) -> String {
switch filter {
case .none: return Strings.NavigationBar.allNotificationsTitle
default: return filter.title
}
}

func makeMoreMenuElements() -> [UIAction] {
// Mark All As Read
let markAllAsRead: UIAction? = { () -> UIAction? in
Expand Down Expand Up @@ -516,7 +544,6 @@ private extension NotificationsViewController {
func setupConstraints() {
// Inline prompt is initially hidden!
inlinePromptView.translatesAutoresizingMaskIntoConstraints = false
filterTabBar.tabBarHeightConstraintPriority = 999

let leading = tableHeaderView.safeLeadingAnchor.constraint(equalTo: tableView.safeLeadingAnchor)
let trailing = tableHeaderView.safeTrailingAnchor.constraint(equalTo: tableView.safeTrailingAnchor)
Expand Down Expand Up @@ -575,15 +602,6 @@ private extension NotificationsViewController {
func setupNoResultsView() {
noResultsViewController.delegate = self
}

func setupFilterBar() {
WPStyleGuide.configureFilterTabBar(filterTabBar)
filterTabBar.superview?.backgroundColor = .systemBackground
filterTabBar.backgroundColor = .systemBackground

filterTabBar.items = Filter.allCases
filterTabBar.addTarget(self, action: #selector(selectedFilterDidChange(_:)), for: .valueChanged)
}
}

// MARK: - Jetpack banner UI Initialization
Expand Down Expand Up @@ -1282,21 +1300,19 @@ extension NotificationsViewController: NetworkStatusDelegate {
}
}

// MARK: - FilterTabBar Methods
// MARK: - Filters Menu Methods
//
extension NotificationsViewController {

@objc func selectedFilterDidChange(_ filterBar: FilterTabBar) {
selectedNotification = nil
private func filtersMenuTapped(filter: Filter) {
self.filter = filter
self.selectedNotification = nil

let properties = [Stats.selectedFilter: filter.analyticsTitle]
WPAnalytics.track(.notificationsTappedSegmentedControl, withProperties: properties)

updateUnreadNotificationsForFilterTabChange()

reloadResultsController()

selectFirstNotificationIfAppropriate()
self.updateUnreadNotificationsForFilterTabChange()
self.selectFirstNotificationIfAppropriate()
}

@objc func selectFirstNotificationIfAppropriate() {
Expand Down Expand Up @@ -1479,19 +1495,17 @@ extension NotificationsViewController: WPTableViewHandlerDelegate {
//
private extension NotificationsViewController {
func showFiltersSegmentedControlIfApplicable() {
guard filterTabBar.isHidden == true && shouldDisplayFilters == true else {
guard shouldDisplayFilters else {
return
}

UIView.animate(withDuration: WPAnimationDurationDefault, animations: {
self.filterTabBar.isHidden = false
})
self.navigationItem.titleView = filtersMenuButton
}

func hideFiltersSegmentedControlIfApplicable() {
if filterTabBar.isHidden == false && shouldDisplayFilters == false {
self.filterTabBar.isHidden = true
guard !shouldDisplayFilters else {
return
}
self.navigationItem.titleView = nil
}

var shouldDisplayFilters: Bool {
Expand All @@ -1508,7 +1522,7 @@ private extension NotificationsViewController {
func showNoResultsViewIfNeeded() {
noResultsViewController.removeFromView()
updateSplitViewAppearanceForNoResultsView()
updateNavigationItems()
configureNavigationItems()

// Hide the filter header if we're showing the Jetpack prompt
hideFiltersSegmentedControlIfApplicable()
Expand Down Expand Up @@ -1802,17 +1816,6 @@ private extension NotificationsViewController {
return UserPersistentStoreFactory.instance()
}

var filter: Filter {
get {
let selectedIndex = filterTabBar?.selectedIndex ?? Filter.none.rawValue
return Filter(rawValue: selectedIndex) ?? .none
}
set {
filterTabBar?.setSelectedIndex(newValue.rawValue)
reloadResultsController()
}
}

enum Filter: Int, FilterTabBarItem, CaseIterable {
case none = 0
case unread = 1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="aRr-Yu-ntD">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="aRr-Yu-ntD">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
Expand Down Expand Up @@ -47,7 +47,6 @@
</view>
<extendedEdge key="edgesForExtendedLayout" top="YES"/>
<connections>
<outlet property="filterTabBar" destination="XgY-0j-kFz" id="LlQ-Kc-Yyz"/>
<outlet property="inlinePromptView" destination="jc0-PX-EvN" id="yPU-Nj-hlp"/>
<outlet property="jetpackBannerView" destination="fx7-Fo-KUl" id="YYv-mK-dV3"/>
<outlet property="tableHeaderView" destination="9K7-oU-83Y" id="dac-fs-2oN"/>
Expand All @@ -66,23 +65,6 @@
<constraint firstAttribute="height" constant="100" placeholder="YES" id="pxu-yE-rHC"/>
</constraints>
</view>
<view contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" layoutMarginsFollowReadableWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rKm-gY-cFw" userLabel="Filters View">
<rect key="frame" x="0.0" y="100" width="600" height="44"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XgY-0j-kFz" customClass="FilterTabBar" customModule="WordPress" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="600" height="34"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="XgY-0j-kFz" secondAttribute="trailing" priority="999" id="7Mp-Sv-zuM"/>
<constraint firstItem="XgY-0j-kFz" firstAttribute="leading" secondItem="rKm-gY-cFw" secondAttribute="leading" priority="999" id="JVy-y6-rph"/>
<constraint firstAttribute="height" priority="999" constant="44" id="dte-R2-ISl"/>
<constraint firstAttribute="bottom" secondItem="XgY-0j-kFz" secondAttribute="bottom" constant="10" id="mxz-xp-47Y"/>
<constraint firstItem="XgY-0j-kFz" firstAttribute="top" secondItem="rKm-gY-cFw" secondAttribute="top" id="udW-ag-i42"/>
</constraints>
</view>
</subviews>
</stackView>
<placeholder placeholderIdentifier="IBFirstResponder" id="KJC-DS-ThZ" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
Expand Down