diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsHubView.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsHubView.swift index 419ff6c657c..6262247b783 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsHubView.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsHubView.swift @@ -5,15 +5,39 @@ import SwiftUI /// Hosting Controller for the `AnalyticsHubView` view. /// final class AnalyticsHubHostingViewController: UIHostingController { - init(siteID: Int64, timeRange: StatsTimeRangeV4) { + + /// Presents an error notice in the tab bar context after this `self` is dismissed. + /// + private let systemNoticePresenter: NoticePresenter + + /// Defines a notice that should be presented after `self` is dismissed. + /// Defaults to `nil`. + /// + var notice: Notice? + + init(siteID: Int64, timeRange: StatsTimeRangeV4, systemNoticePresenter: NoticePresenter = ServiceLocator.noticePresenter) { let viewModel = AnalyticsHubViewModel(siteID: siteID, statsTimeRange: timeRange) + self.systemNoticePresenter = systemNoticePresenter super.init(rootView: AnalyticsHubView(viewModel: viewModel)) + + // Needed to pop the hosting controller from within the SwiftUI view + rootView.dismissWithNotice = { [weak self] notice in + self?.notice = notice + self?.navigationController?.popViewController(animated: true) + } } @available(*, unavailable) required dynamic init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + // Show any notice that should be presented after the underlying disappears. + enqueuePendingNotice(notice, using: systemNoticePresenter) + } } /// Main Analytics Hub View @@ -23,6 +47,11 @@ struct AnalyticsHubView: View { /// Environment safe areas @Environment(\.safeAreaInsets) var safeAreaInsets: EdgeInsets + /// Set this closure with UIKit code to pop the view controller and display the provided notice. + /// Needed because we need access to the UIHostingController `popViewController` method. + /// + var dismissWithNotice: ((Notice) -> Void) = { _ in } + @StateObject var viewModel: AnalyticsHubViewModel var body: some View { @@ -81,6 +110,10 @@ struct AnalyticsHubView: View { .task { await viewModel.updateData() } + .onReceive(viewModel.$dismissNotice) { notice in + guard let notice else { return } + dismissWithNotice(notice) + } } } diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsHubViewModel.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsHubViewModel.swift index 452a56a728c..2a724904e65 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsHubViewModel.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsHubViewModel.swift @@ -47,6 +47,11 @@ final class AnalyticsHubViewModel: ObservableObject { /// @Published var timeRangeCard: AnalyticsTimeRangeCardViewModel + /// Defines a notice that, when set, dismisses the view and is then displayed. + /// Defaults to `nil`. + /// + @Published var dismissNotice: Notice? + // MARK: Private data /// Order stats for the current selected time period @@ -67,9 +72,13 @@ final class AnalyticsHubViewModel: ObservableObject { /// Request stats data from network /// + @MainActor func updateData() async { do { try await retrieveOrderStats() + } catch is AnalyticsHubTimeRangeSelection.TimeRangeGeneratorError { + dismissNotice = Notice(title: Localization.timeRangeGeneratorError, feedbackType: .error) + DDLogWarn("⚠️ Error selecting analytics time range: \(timeRangeSelectionType.description)") } catch { switchToErrorState() DDLogWarn("⚠️ Error fetching analytics data: \(error)") @@ -308,5 +317,8 @@ private extension AnalyticsHubViewModel { value) } } + + static let timeRangeGeneratorError = NSLocalizedString("Sorry, something went wrong. We can't load analytics for the selected date range.", + comment: "Error shown when there is a problem retrieving the dates for the selected date range.") } }