diff --git a/WordPress/Classes/Apps/Reader/Discover/ReaderDiscoverTabViewController.swift b/WordPress/Classes/Apps/Reader/Discover/ReaderDiscoverTabViewController.swift index ced0e262fc72..2f9d776a1274 100644 --- a/WordPress/Classes/Apps/Reader/Discover/ReaderDiscoverTabViewController.swift +++ b/WordPress/Classes/Apps/Reader/Discover/ReaderDiscoverTabViewController.swift @@ -8,6 +8,16 @@ final class ReaderDiscoverTabViewController: ReaderDiscoverViewController { navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "reader-menu-search"), style: .plain, target: self, action: #selector(buttonSearchTapped)) } + override func setupNavigation() { + headerView.configureForReaderAppMode() + + navigationItem.largeTitleDisplayMode = .always + navigationController?.navigationBar.prefersLargeTitles = true + + title = SharedStrings.Reader.discover + navigationItem.titleView = nil + } + @objc private func buttonSearchTapped() { let searchVC = ReaderSearchViewController() searchVC.isStandaloneAppModeEnabled = true diff --git a/WordPress/Classes/Apps/Reader/Home/ReaderHomeViewController.swift b/WordPress/Classes/Apps/Reader/Home/ReaderHomeViewController.swift index 8ae6db97abc6..bb8102eeb346 100644 --- a/WordPress/Classes/Apps/Reader/Home/ReaderHomeViewController.swift +++ b/WordPress/Classes/Apps/Reader/Home/ReaderHomeViewController.swift @@ -7,22 +7,19 @@ final class ReaderHomeViewController: ReaderStreamViewController { override func viewDidLoad() { super.viewDidLoad() - setupNavigationItems() - } - - private func setupNavigationItems() { title = SharedStrings.Reader.home - titleView.textLabel.text = SharedStrings.Reader.home - navigationItem.titleView = titleView - navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "wpl-add-card")?.resized(to: CGSize(width: 28, height: 28)), style: .plain, target: self, action: #selector(buttonCreatePostTapped)) + + navigationItem.largeTitleDisplayMode = .always + navigationController?.navigationBar.prefersLargeTitles = true } - override func headerForStream(_ topic: ReaderAbstractTopic?, container: UITableViewController) -> UIView? { - let view = ReaderHeaderView() - view.titleView.titleLabel.text = SharedStrings.Reader.home - view.titleView.detailsTextView.text = Strings.homeDetails - return view + override func configureTitleForTopic() { + // Do nothing + } + + @objc override func headerForStream(_ topic: ReaderAbstractTopic?, container: UITableViewController) -> UIView? { + nil } @objc private func buttonCreatePostTapped() { @@ -48,7 +45,3 @@ final class ReaderHomeViewController: ReaderStreamViewController { present(editorVC, animated: true) } } - -private enum Strings { - static let homeDetails = NSLocalizedString("reader.home.header.details", value: "Stay current with the blogs you've subscribed to.", comment: "Screen header details") -} diff --git a/WordPress/Classes/Apps/Reader/ReaderTabViewController.swift b/WordPress/Classes/Apps/Reader/ReaderTabViewController.swift index c935b45c855e..a5b7eef8d938 100644 --- a/WordPress/Classes/Apps/Reader/ReaderTabViewController.swift +++ b/WordPress/Classes/Apps/Reader/ReaderTabViewController.swift @@ -39,12 +39,118 @@ final class ReaderTabViewController: UITabBarController, UITabBarControllerDeleg } private func setupViewControllers() { + if #available(iOS 18, *) { + setupModernTabBarItems() + } else { + setupLegacyTabBarItems() + } + } + + @available(iOS 18, *) + private func setupModernTabBarItems() { + let home = UITab(title: SharedStrings.Reader.home, image: UIImage(named: "reader-menu-home"), identifier: "home") { [unowned self] _ in + self.makeHomeViewController() + } + let library = UITab(title: SharedStrings.Reader.library, image: UIImage(named: "reader-menu-subscriptions"), identifier: "library") { [unowned self] _ in + self.makeLibraryViewController() + } + let discover = UITab(title: SharedStrings.Reader.discover, image: UIImage(named: "reader-menu-explorer"), identifier: "discover") { [unowned self] _ in + self.makeDiscoverViewController() + } + + let notifications = UITab(title: SharedStrings.Reader.activity, image: UIImage(named: "tab-bar-notifications"), identifier: "activity") { [unowned self] _ in + self.makeActivityViewController() + } + notificationsButtonViewModel.$counter.sink { [weak notifications] count in + notifications?.badgeValue = count == 0 ? nil : "1" + }.store(in: &cancellables) + + let me = UITab(title: SharedStrings.Reader.me, image: UIImage(named: "tab-bar-me"), identifier: "me") { [unowned self] _ in + self.makeMeViewController() + } + // TODO: (reader) observe gravatar updates + if let account = try? WPAccount.lookupDefaultWordPressComAccount(in: ContextManager.shared.mainContext), + let avatarURL = account.avatarURL.flatMap(URL.init) { + Task { @MainActor [weak me] in + do { + let image = try await ImageDownloader.shared.image(from: avatarURL) + me?.image = image.gravatarIcon(size: 26.0) +// meVC?.tabBarItem.configureGravatarImage(image) + } catch { + // Do nothing + } + } + } + + tabs = [ + home, + library, + discover, + notifications, + me + ] + } + + private func setupLegacyTabBarItems() { + let homeVC = makeHomeViewController() + homeVC.tabBarItem = UITabBarItem( + title: SharedStrings.Reader.home, + image: UIImage(named: "reader-menu-home"), + selectedImage: nil + ) + + let libraryVC = makeLibraryViewController() + libraryVC.tabBarItem = UITabBarItem( + title: SharedStrings.Reader.library, + image: UIImage(named: "reader-menu-subscriptions"), + selectedImage: nil + ) + + let discoverVC = makeDiscoverViewController() + discoverVC.tabBarItem = UITabBarItem( + title: SharedStrings.Reader.discover, + image: UIImage(named: "reader-menu-explorer"), + selectedImage: nil + ) + + let activityVC = makeActivityViewController() + activityVC.tabBarItem = UITabBarItem( + title: SharedStrings.Reader.activity, + image: UIImage(named: "tab-bar-notifications"), + selectedImage: UIImage(named: "tab-bar-notifications") + ) + + notificationsButtonViewModel.$counter.sink { [weak activityVC] count in + let image = UIImage(named: count == 0 ? "tab-bar-notifications" : "tab-bar-notifications-unread") + activityVC?.tabBarItem.image = image + activityVC?.tabBarItem.selectedImage = image + }.store(in: &cancellables) + + let meVC = makeMeViewController() + meVC.tabBarItem = UITabBarItem( + title: SharedStrings.Reader.me, + image: UIImage(named: "tab-bar-me"), + selectedImage: UIImage(named: "tab-bar-me") + ) + // TODO: (reader) observe gravatar updates + if let account = try? WPAccount.lookupDefaultWordPressComAccount(in: ContextManager.shared.mainContext), + let avatarURL = account.avatarURL.flatMap(URL.init) { + Task { @MainActor [weak meVC] in + do { + let image = try await ImageDownloader.shared.image(from: avatarURL) + meVC?.tabBarItem.configureGravatarImage(image) + } catch { + // Do nothing + } + } + } + self.viewControllers = [ - makeHomeViewController(), - makeLibraryViewController(), - makeDiscoverViewController(), - makeNotificationsViewController(), - makeMeViewController() + homeVC, + libraryVC, + discoverVC, + activityVC, + meVC ] } @@ -54,11 +160,7 @@ final class ReaderTabViewController: UITabBarController, UITabBarControllerDeleg let homeVC = ReaderHomeViewController() // TODO: (reader) refactor to not require `topic` homeVC.readerTopic = ReaderSidebarViewModel().getTopic(for: .following) - homeVC.tabBarItem = UITabBarItem( - title: SharedStrings.Reader.home, - image: UIImage(named: "reader-menu-home"), - selectedImage: nil - ) + // TODO: (reader) remove it; had to use due to how ghosts are implemented (separate table) homeVC.tabBarItem.scrollEdgeAppearance = { let appearance = UITabBarAppearance() @@ -70,11 +172,7 @@ final class ReaderTabViewController: UITabBarController, UITabBarControllerDeleg private func makeLibraryViewController() -> UIViewController { let libraryVC = library.sidebar - libraryVC.tabBarItem = UITabBarItem( - title: SharedStrings.Reader.library, - image: UIImage(named: "reader-menu-subscriptions"), - selectedImage: nil - ) + libraryVC.tabBarItem.scrollEdgeAppearance = { let appearance = UITabBarAppearance() appearance.configureWithOpaqueBackground() @@ -92,11 +190,7 @@ final class ReaderTabViewController: UITabBarController, UITabBarControllerDeleg UIViewController() } }() - discoverVC.tabBarItem = UITabBarItem( - title: Strings.discover, - image: UIImage(named: "reader-menu-explorer"), - selectedImage: nil - ) + discoverVC.tabBarItem.scrollEdgeAppearance = { let appearance = UITabBarAppearance() appearance.configureWithOpaqueBackground() @@ -107,47 +201,20 @@ final class ReaderTabViewController: UITabBarController, UITabBarControllerDeleg return navigationVC } - private func makeNotificationsViewController() -> UIViewController { + private func makeActivityViewController() -> UIViewController { let notificationsVC = UIStoryboard(name: "Notifications", bundle: nil) .instantiateInitialViewController() as! NotificationsViewController - notificationsVC.tabBarItem = UITabBarItem( - title: Strings.notifications, - image: UIImage(named: "tab-bar-notifications"), - selectedImage: UIImage(named: "tab-bar-notifications") - ) + + notificationsVC.title = SharedStrings.Reader.activity notificationsVC.isReaderAppModeEnabled = true let navigationVC = UINavigationController(rootViewController: notificationsVC) notificationsVC.enableLargeTitles() - notificationsButtonViewModel.$counter.sink { [weak notificationsVC] count in - let image = UIImage(named: count == 0 ? "tab-bar-notifications" : "tab-bar-notifications-unread") - notificationsVC?.tabBarItem.image = image - notificationsVC?.tabBarItem.selectedImage = image - }.store(in: &cancellables) - return navigationVC } private func makeMeViewController() -> UIViewController { let meVC = ReaderProfileViewController() - // TODO: (reader) display your profile icons - meVC.tabBarItem = UITabBarItem( - title: Strings.me, - image: UIImage(named: "tab-bar-me"), - selectedImage: UIImage(named: "tab-bar-me") - ) - // TODO: (reader) observe gravatar updates - if let account = try? WPAccount.lookupDefaultWordPressComAccount(in: ContextManager.shared.mainContext), - let avatarURL = account.avatarURL.flatMap(URL.init) { - Task { @MainActor [weak meVC] in - do { - let image = try await ImageDownloader.shared.image(from: avatarURL) - meVC?.tabBarItem.configureGravatarImage(image) - } catch { - // Do nothing - } - } - } return UINavigationController(rootViewController: meVC) } @@ -168,9 +235,3 @@ private extension UIViewController { navigationController?.navigationBar.prefersLargeTitles = true } } - -private enum Strings { - static let discover = NSLocalizedString("readerApp.tabBar.discover", value: "Discover", comment: "Reader app primary navigation tab bar") - static let notifications = NSLocalizedString("readerApp.tabBar.notifications", value: "Notifications", comment: "Reader app primary navigation tab bar") - static let me = NSLocalizedString("readerApp.tabBar.me", value: "Me", comment: "Reader app primary navigation tab bar") -} diff --git a/WordPress/Classes/Utility/SharedStrings.swift b/WordPress/Classes/Utility/SharedStrings.swift index f44d8549c193..69899e52ccf1 100644 --- a/WordPress/Classes/Utility/SharedStrings.swift +++ b/WordPress/Classes/Utility/SharedStrings.swift @@ -45,5 +45,7 @@ enum SharedStrings { static let tags = NSLocalizedString("reader.tags.title", value: "Tags", comment: "Used in multiple contexts, usually as a screen title") static let lists = NSLocalizedString("reader.lists.title", value: "Lists", comment: "Used in multiple contexts, usually as a screen title") static let search = NSLocalizedString("reader.search.title", value: "Search", comment: "Used in multiple contexts, usually as a screen title") + static let activity = NSLocalizedString("reader.activity.title", value: "Activity", comment: "Used in multiple contexts, usually as a screen title") + static let me = NSLocalizedString("reader.me.title", value: "Me", comment: "Used in multiple contexts, usually as a screen title") } } diff --git a/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderDiscoverViewController.swift b/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderDiscoverViewController.swift index 3c5f77f32ddb..133e5cd9d640 100644 --- a/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderDiscoverViewController.swift +++ b/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderDiscoverViewController.swift @@ -5,7 +5,7 @@ import WordPressKit import WordPressShared class ReaderDiscoverViewController: UIViewController, ReaderDiscoverHeaderViewDelegate { - private let headerView = ReaderDiscoverHeaderView() + let headerView = ReaderDiscoverHeaderView() private var selectedChannel: ReaderDiscoverChannel = .recommended private let topic: ReaderAbstractTopic private var streamVC: ReaderStreamViewController? @@ -50,7 +50,7 @@ class ReaderDiscoverViewController: UIViewController, ReaderDiscoverHeaderViewDe setupNotificationsBarButtonItem() } - private func setupNavigation() { + func setupNavigation() { navigationItem.largeTitleDisplayMode = .never setupNotificationsBarButtonItem() } diff --git a/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderStreamViewController.swift b/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderStreamViewController.swift index 2f5a59e2547e..84a969fe1a45 100644 --- a/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderStreamViewController.swift +++ b/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderStreamViewController.swift @@ -563,7 +563,7 @@ import AutomatticTracks } } - private func configureTitleForTopic() { + func configureTitleForTopic() { guard let topic = readerTopic else { if contentType == .saved { title = SharedStrings.Reader.saved diff --git a/WordPress/Classes/ViewRelated/Reader/Headers/ReaderDiscoverHeaderView.swift b/WordPress/Classes/ViewRelated/Reader/Headers/ReaderDiscoverHeaderView.swift index d8c5c17d7605..ebace3a4669a 100644 --- a/WordPress/Classes/ViewRelated/Reader/Headers/ReaderDiscoverHeaderView.swift +++ b/WordPress/Classes/ViewRelated/Reader/Headers/ReaderDiscoverHeaderView.swift @@ -56,6 +56,11 @@ final class ReaderDiscoverHeaderView: ReaderBaseHeaderView, UITextViewDelegate { fatalError("init(coder:) has not been implemented") } + func configureForReaderAppMode() { + titleView.removeFromSuperview() + channelsStackView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true + } + override func layoutSubviews() { super.layoutSubviews()