diff --git a/WordPress/Classes/Utility/ContentCoordinator.swift b/WordPress/Classes/Utility/ContentCoordinator.swift index e81195f70b7d..3f1d16e2e44d 100644 --- a/WordPress/Classes/Utility/ContentCoordinator.swift +++ b/WordPress/Classes/Utility/ContentCoordinator.swift @@ -77,10 +77,8 @@ struct DefaultContentCoordinator: ContentCoordinator { let matches = matcher.routesMatching(url) if let match = matches.first, let action = match.action as? StatsRoute, - let timePeriod = action.timePeriod { - // Initializing a StatsPeriodType to ensure we have a valid period - let key = SiteStatsDashboardViewController.lastSelectedStatsPeriodTypeKey(forSiteID: siteID) - UserPersistentStoreFactory.instance().set(timePeriod.rawValue, forKey: key) + let tab = action.tab { + SiteStatsDashboardPreferences.setSelected(tabType: tab, siteID: siteID) } } diff --git a/WordPress/Classes/Utility/InteractiveNotificationsManager.swift b/WordPress/Classes/Utility/InteractiveNotificationsManager.swift index 9696c55bd64c..62e3a0095870 100644 --- a/WordPress/Classes/Utility/InteractiveNotificationsManager.swift +++ b/WordPress/Classes/Utility/InteractiveNotificationsManager.swift @@ -228,7 +228,7 @@ final class InteractiveNotificationsManager: NSObject { RootViewCoordinator.sharedPresenter.mySitesCoordinator.showStats( for: targetBlog, source: .notification, - timePeriod: .weeks, + tab: .weeks, date: targetDate) } diff --git a/WordPress/Classes/Utility/Universal Links/Routes+Stats.swift b/WordPress/Classes/Utility/Universal Links/Routes+Stats.swift index 4085e5046c61..bb28b92a6830 100644 --- a/WordPress/Classes/Utility/Universal Links/Routes+Stats.swift +++ b/WordPress/Classes/Utility/Universal Links/Routes+Stats.swift @@ -12,7 +12,7 @@ enum StatsRoute { case annualStats case activityLog - var timePeriod: StatsPeriodType? { + var tab: StatsTabType? { switch self { case .daySite: return .days @@ -85,19 +85,19 @@ extension StatsRoute: NavigationAction { showStatsForDefaultBlog(from: values, with: coordinator) } case .daySite: - showStatsForBlog(from: values, timePeriod: .days, using: coordinator) + showStatsForBlog(from: values, tab: .days, using: coordinator) case .weekSite: - showStatsForBlog(from: values, timePeriod: .weeks, using: coordinator) + showStatsForBlog(from: values, tab: .weeks, using: coordinator) case .monthSite: - showStatsForBlog(from: values, timePeriod: .months, using: coordinator) + showStatsForBlog(from: values, tab: .months, using: coordinator) case .yearSite: - showStatsForBlog(from: values, timePeriod: .years, using: coordinator) + showStatsForBlog(from: values, tab: .years, using: coordinator) case .insights: - showStatsForBlog(from: values, timePeriod: .insights, using: coordinator) + showStatsForBlog(from: values, tab: .insights, using: coordinator) case .dayCategory: - showStatsForBlog(from: values, timePeriod: .days, using: coordinator) + showStatsForBlog(from: values, tab: .days, using: coordinator) case .annualStats: - showStatsForBlog(from: values, timePeriod: .years, using: coordinator) + showStatsForBlog(from: values, tab: .years, using: coordinator) case .activityLog: if let blog = blog(from: values) { coordinator.showActivityLog(for: blog) @@ -109,12 +109,12 @@ extension StatsRoute: NavigationAction { } private func showStatsForBlog(from values: [String: String], - timePeriod: StatsPeriodType, + tab: StatsTabType, using coordinator: MySitesCoordinator) { if let blog = blog(from: values) { coordinator.showStats(for: blog, source: source(from: values), - timePeriod: timePeriod) + tab: tab) } else { showMySitesAndFailureNotice(using: coordinator, values: values) @@ -139,12 +139,12 @@ extension StatsRoute: NavigationAction { // In this case, we'll check whether the last component is actually a // time period, and if so we'll show that time period for the default site. guard let component = values["domain"], - let timePeriod = StatsPeriodType(from: component), + let timePeriod = StatsTabType(from: component), let blog = defaultBlog() else { return } - coordinator.showStats(for: blog, source: source(from: values), timePeriod: timePeriod) + coordinator.showStats(for: blog, source: source(from: values), tab: timePeriod) } private func source(from values: [String: String]) -> BlogDetailsNavigationSource { diff --git a/WordPress/Classes/ViewRelated/Blog/My Site/MySiteViewController+OnboardingPrompt.swift b/WordPress/Classes/ViewRelated/Blog/My Site/MySiteViewController+OnboardingPrompt.swift index 3294c3ae4498..aa2676c2d660 100644 --- a/WordPress/Classes/ViewRelated/Blog/My Site/MySiteViewController+OnboardingPrompt.swift +++ b/WordPress/Classes/ViewRelated/Blog/My Site/MySiteViewController+OnboardingPrompt.swift @@ -17,7 +17,7 @@ extension MySiteViewController { case .stats: // Show the stats view for the current blog if let blog = blog { - RootViewCoordinator.sharedPresenter.mySitesCoordinator.showStats(for: blog, source: .onboarding, timePeriod: .insights) + RootViewCoordinator.sharedPresenter.mySitesCoordinator.showStats(for: blog, source: .onboarding, tab: .insights) } case .writing: // Open the editor diff --git a/WordPress/Classes/ViewRelated/Stats/Period Stats/SiteStatsPeriodViewModel.swift b/WordPress/Classes/ViewRelated/Stats/Period Stats/SiteStatsPeriodViewModel.swift index dc18a6d6de95..ac33f13f4137 100644 --- a/WordPress/Classes/ViewRelated/Stats/Period Stats/SiteStatsPeriodViewModel.swift +++ b/WordPress/Classes/ViewRelated/Stats/Period Stats/SiteStatsPeriodViewModel.swift @@ -11,7 +11,11 @@ final class SiteStatsPeriodViewModel: Observable { private weak var referrerDelegate: SiteStatsReferrerDelegate? private let store: any StatsPeriodStoreProtocol private var lastRequestedDate: Date - private var lastRequestedPeriod: StatsPeriodUnit + private var lastRequestedPeriod: StatsPeriodUnit { + didSet { + SiteStatsDashboardPreferences.setSelected(periodUnit: lastRequestedPeriod) + } + } private var periodReceipt: Receipt? private var changeReceipt: Receipt? private typealias Style = WPStyleGuide.Stats diff --git a/WordPress/Classes/ViewRelated/Stats/SiteStatsDashboardViewController.swift b/WordPress/Classes/ViewRelated/Stats/SiteStatsDashboardViewController.swift index 6c3bad1377bf..372aa108c538 100644 --- a/WordPress/Classes/ViewRelated/Stats/SiteStatsDashboardViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/SiteStatsDashboardViewController.swift @@ -1,6 +1,6 @@ import UIKit -enum StatsPeriodType: Int, FilterTabBarItem, CaseIterable { +enum StatsTabType: Int, FilterTabBarItem, CaseIterable { case insights = 0 case days case weeks @@ -38,10 +38,25 @@ enum StatsPeriodType: Int, FilterTabBarItem, CaseIterable { return nil } } + + var unit: StatsPeriodUnit? { + switch self { + case .days: + return .day + case .weeks: + return .week + case .months: + return .month + case .years: + return .year + default: + return nil + } + } } -fileprivate extension StatsPeriodType { - static var displayedPeriods: [StatsPeriodType] { +fileprivate extension StatsTabType { + static var displayedTabs: [StatsTabType] { if RemoteFeatureFlag.statsTrafficTab.enabled() { return [.traffic, .insights] } else { @@ -62,13 +77,6 @@ fileprivate extension StatsPeriodType { } class SiteStatsDashboardViewController: UIViewController { - - // MARK: - Keys - - static func lastSelectedStatsPeriodTypeKey(forSiteID siteID: Int) -> String { - return "LastSelectedStatsPeriodType-\(siteID)" - } - static let lastSelectedStatsDateKey = "LastSelectedStatsDate" // MARK: - Properties @@ -80,18 +88,18 @@ class SiteStatsDashboardViewController: UIViewController { private lazy var periodTableViewControllerDeprecated = SiteStatsPeriodTableViewControllerDeprecated.loadFromStoryboard() private lazy var trafficTableViewController = { let date: Date - if let selectedDate = getLastSelectedDateFromUserDefaults() { + if let selectedDate = SiteStatsDashboardPreferences.getLastSelectedDateFromUserDefaults() { date = selectedDate } else { date = StatsDataHelper.currentDateForSite() } - let currentPeriod = StatsPeriodUnit(rawValue: currentSelectedPeriod.rawValue - 1) ?? .day + let currentPeriod = SiteStatsDashboardPreferences.getSelectedPeriodUnit() ?? .day return SiteStatsPeriodTableViewController(date: date, period: currentPeriod) }() private var pageViewController: UIPageViewController? - private lazy var displayedPeriods: [StatsPeriodType] = StatsPeriodType.displayedPeriods + private lazy var displayedTabs: [StatsTabType] = StatsTabType.displayedTabs @objc lazy var manageInsightsButton: UIBarButtonItem = { let button = UIBarButtonItem( @@ -113,7 +121,7 @@ class SiteStatsDashboardViewController: UIViewController { configureTrafficTableViewController() setupFilterBar() restoreSelectedDateFromUserDefaults() - restoreSelectedPeriodFromUserDefaults() + restoreSelectedTabFromUserDefaults() addWillEnterForegroundObserver() configureNavBar() view.accessibilityIdentifier = "stats-dashboard" @@ -138,7 +146,7 @@ class SiteStatsDashboardViewController: UIViewController { } func configureNavBar() { - parent?.navigationItem.rightBarButtonItem = currentSelectedPeriod == .insights ? manageInsightsButton : nil + parent?.navigationItem.rightBarButtonItem = currentSelectedTab == .insights ? manageInsightsButton : nil } func configureJetpackBanner() { @@ -173,30 +181,30 @@ class SiteStatsDashboardViewController: UIViewController { override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { if traitCollection.verticalSizeClass == .regular, traitCollection.horizontalSizeClass == .compact { - updatePeriodView(oldSelectedPeriod: currentSelectedPeriod) + updatePeriodView(oldSelectedTab: currentSelectedTab) } } } extension SiteStatsDashboardViewController: StatsForegroundObservable { func reloadStatsData() { - updatePeriodView(oldSelectedPeriod: currentSelectedPeriod) + updatePeriodView(oldSelectedTab: currentSelectedTab) } } // MARK: - Private Extension private extension SiteStatsDashboardViewController { - var currentSelectedPeriod: StatsPeriodType { + var currentSelectedTab: StatsTabType { get { let selectedIndex = filterTabBar?.selectedIndex ?? 0 - return displayedPeriods[selectedIndex] + return displayedTabs[selectedIndex] } set { - let index = displayedPeriods.firstIndex(of: newValue) ?? 0 + let index = displayedTabs.firstIndex(of: newValue) ?? 0 filterTabBar?.setSelectedIndex(index) - let oldSelectedPeriod = getSelectedPeriodFromUserDefaults() - updatePeriodView(oldSelectedPeriod: oldSelectedPeriod) + let oldSelectedPeriod = getSelectedTabFromUserDefaults() + updatePeriodView(oldSelectedTab: oldSelectedPeriod) saveSelectedPeriodToUserDefaults() trackAccessEvent() } @@ -209,13 +217,13 @@ private extension SiteStatsDashboardViewController { func setupFilterBar() { WPStyleGuide.Stats.configureFilterTabBar(filterTabBar) - filterTabBar.items = displayedPeriods + filterTabBar.items = displayedTabs filterTabBar.addTarget(self, action: #selector(selectedFilterDidChange(_:)), for: .valueChanged) filterTabBar.accessibilityIdentifier = "site-stats-dashboard-filter-bar" } @objc func selectedFilterDidChange(_ filterBar: FilterTabBar) { - currentSelectedPeriod = displayedPeriods[filterBar.selectedIndex] + currentSelectedTab = displayedTabs[filterBar.selectedIndex] configureNavBar() } @@ -231,50 +239,38 @@ private extension SiteStatsDashboardViewController { return } - let key = Self.lastSelectedStatsPeriodTypeKey(forSiteID: siteID) - guard !insightsTableViewController.isGrowAudienceShowing else { - UserPersistentStoreFactory.instance().set(StatsPeriodType.insights.rawValue, forKey: key) + SiteStatsDashboardPreferences.setSelected(tabType: .insights, siteID: siteID) return } - UserPersistentStoreFactory.instance().set(currentSelectedPeriod.rawValue, forKey: key) + SiteStatsDashboardPreferences.setSelected(tabType: currentSelectedTab, siteID: siteID) } - func getSelectedPeriodFromUserDefaults() -> StatsPeriodType { - - guard let siteID = SiteStatsInformation.sharedInstance.siteID?.intValue, - let periodType = StatsPeriodType(rawValue: UserPersistentStoreFactory.instance().integer(forKey: Self.lastSelectedStatsPeriodTypeKey(forSiteID: siteID))) else { - return displayedPeriods[0] + func getSelectedTabFromUserDefaults() -> StatsTabType { + guard let tabType = SiteStatsDashboardPreferences.getSelectedTabType() else { + return displayedTabs[0] } - return periodType - } - - func getLastSelectedDateFromUserDefaults() -> Date? { - UserPersistentStoreFactory.instance().object(forKey: Self.lastSelectedStatsDateKey) as? Date - } - - func removeLastSelectedDateFromUserDefaults() { - UserPersistentStoreFactory.instance().removeObject(forKey: Self.lastSelectedStatsDateKey) + return tabType } func restoreSelectedDateFromUserDefaults() { - periodTableViewControllerDeprecated.selectedDate = getLastSelectedDateFromUserDefaults() - removeLastSelectedDateFromUserDefaults() + periodTableViewControllerDeprecated.selectedDate = SiteStatsDashboardPreferences.getLastSelectedDateFromUserDefaults() + SiteStatsDashboardPreferences.removeLastSelectedDateFromUserDefaults() } - func restoreSelectedPeriodFromUserDefaults() { - currentSelectedPeriod = getSelectedPeriodFromUserDefaults() + func restoreSelectedTabFromUserDefaults() { + currentSelectedTab = getSelectedTabFromUserDefaults() } - func updatePeriodView(oldSelectedPeriod: StatsPeriodType) { - let selectedPeriodChanged = currentSelectedPeriod != oldSelectedPeriod - let previousSelectedPeriodWasInsights = oldSelectedPeriod == .insights + func updatePeriodView(oldSelectedTab: StatsTabType) { + let selectedPeriodChanged = currentSelectedTab != oldSelectedTab + let previousSelectedPeriodWasInsights = oldSelectedTab == .insights let pageViewControllerIsEmpty = pageViewController?.viewControllers?.isEmpty ?? true let isGrowAudienceShowingOnInsights = insightsTableViewController.isGrowAudienceShowing - switch currentSelectedPeriod { + switch currentSelectedTab { case .insights: if selectedPeriodChanged || pageViewControllerIsEmpty || isGrowAudienceShowingOnInsights { pageViewController?.setViewControllers([insightsTableViewController], @@ -301,7 +297,7 @@ private extension SiteStatsDashboardViewController { periodTableViewControllerDeprecated.selectedDate = StatsDataHelper.currentDateForSite() } - let selectedPeriod = StatsPeriodUnit(rawValue: currentSelectedPeriod.rawValue - 1) ?? .day + let selectedPeriod = StatsPeriodUnit(rawValue: currentSelectedTab.rawValue - 1) ?? .day periodTableViewControllerDeprecated.selectedPeriod = selectedPeriod } } @@ -321,8 +317,65 @@ private extension SiteStatsDashboardViewController { } func trackAccessEvent() { - if let event = currentSelectedPeriod.analyticsAccessEvent { + if let event = currentSelectedTab.analyticsAccessEvent { captureAnalyticsEvent(event) } } } + +// MARK: - Preferences + +struct SiteStatsDashboardPreferences { + static func setSelected(tabType: StatsTabType, siteID: Int? = nil) { + guard let siteID = siteID ?? SiteStatsInformation.sharedInstance.siteID?.intValue else { return } + + let periodKey = lastSelectedStatsTabTypeKey(forSiteID: siteID) + UserPersistentStoreFactory.instance().set(tabType.rawValue, forKey: periodKey) + + let unitKey = lastSelectedStatsUnitTypeKey(forSiteID: siteID) + if let unit = tabType.unit { + UserPersistentStoreFactory.instance().set(unit.rawValue, forKey: unitKey) + } + } + + static func setSelected(periodUnit: StatsPeriodUnit) { + guard let siteID = SiteStatsInformation.sharedInstance.siteID?.intValue else { return } + + let unitKey = lastSelectedStatsUnitTypeKey(forSiteID: siteID) + UserPersistentStoreFactory.instance().set(periodUnit.rawValue, forKey: unitKey) + } + + static func getSelectedTabType() -> StatsTabType? { + guard let siteID = SiteStatsInformation.sharedInstance.siteID?.intValue else { return nil } + + let key = Self.lastSelectedStatsTabTypeKey(forSiteID: siteID) + return StatsTabType(rawValue: UserPersistentStoreFactory.instance().integer(forKey: key)) + } + + static func getSelectedPeriodUnit() -> StatsPeriodUnit? { + guard let siteID = SiteStatsInformation.sharedInstance.siteID?.intValue else { return nil } + + let key = Self.lastSelectedStatsUnitTypeKey(forSiteID: siteID) + return StatsPeriodUnit(rawValue: UserPersistentStoreFactory.instance().integer(forKey: key)) + } + + static func getLastSelectedDateFromUserDefaults() -> Date? { + UserPersistentStoreFactory.instance().object(forKey: Self.lastSelectedStatsDateKey) as? Date + } + + static func removeLastSelectedDateFromUserDefaults() { + UserPersistentStoreFactory.instance().removeObject(forKey: Self.lastSelectedStatsDateKey) + } + + // MARK: - Keys + + private static func lastSelectedStatsTabTypeKey(forSiteID siteID: Int) -> String { + return "LastSelectedStatsTabType-\(siteID)" + } + + private static func lastSelectedStatsUnitTypeKey(forSiteID siteID: Int) -> String { + return "LastSelectedStatsUnitType-\(siteID)" + } + + private static let lastSelectedStatsDateKey = "LastSelectedStatsDate" +} diff --git a/WordPress/Classes/ViewRelated/System/Coordinators/MySitesCoordinator.swift b/WordPress/Classes/ViewRelated/System/Coordinators/MySitesCoordinator.swift index f006708a170f..0730e40c01a4 100644 --- a/WordPress/Classes/ViewRelated/System/Coordinators/MySitesCoordinator.swift +++ b/WordPress/Classes/ViewRelated/System/Coordinators/MySitesCoordinator.swift @@ -139,7 +139,7 @@ class MySitesCoordinator: NSObject { showBlogDetails(for: blog, then: .stats) } - func showStats(for blog: Blog, source: BlogDetailsNavigationSource, timePeriod: StatsPeriodType? = nil, date: Date? = nil) { + func showStats(for blog: Blog, source: BlogDetailsNavigationSource, tab: StatsTabType? = nil, date: Date? = nil) { guard JetpackFeaturesRemovalCoordinator.shouldShowJetpackFeatures() else { unsupportedFeatureFallback() return @@ -151,9 +151,8 @@ class MySitesCoordinator: NSObject { UserPersistentStoreFactory.instance().set(date, forKey: SiteStatsDashboardViewController.lastSelectedStatsDateKey) } - if let siteID = blog.dotComID?.intValue, let timePeriod = timePeriod { - let key = SiteStatsDashboardViewController.lastSelectedStatsPeriodTypeKey(forSiteID: siteID) - UserPersistentStoreFactory.instance().set(timePeriod.rawValue, forKey: key) + if let siteID = blog.dotComID?.intValue, let tab = tab { + SiteStatsDashboardPreferences.setSelected(tabType: tab, siteID: siteID) } let userInfo: [AnyHashable: Any] = [BlogDetailsViewController.userInfoSourceKey(): NSNumber(value: source.rawValue)]