chromium/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h"

#import <vector>

#import "base/apple/foundation_util.h"
#import "base/ios/ios_util.h"
#import "base/metrics/user_metrics.h"
#import "base/metrics/user_metrics_action.h"
#import "base/strings/sys_string_conversions.h"
#import "components/commerce/core/commerce_feature_list.h"
#import "components/feed/core/v2/public/ios/pref_names.h"
#import "components/ntp_tiles/most_visited_sites.h"
#import "components/password_manager/core/browser/ui/credential_ui_entry.h"
#import "components/password_manager/core/browser/ui/password_check_referrer.h"
#import "components/prefs/pref_service.h"
#import "components/search_engines/prepopulated_engines.h"
#import "components/search_engines/template_url.h"
#import "components/search_engines/template_url_prepopulate_data.h"
#import "components/search_engines/template_url_service.h"
#import "components/segmentation_platform/public/features.h"
#import "components/segmentation_platform/public/segmentation_platform_service.h"
#import "ios/chrome/app/application_delegate/app_state.h"
#import "ios/chrome/app/tests_hook.h"
#import "ios/chrome/browser/commerce/model/push_notification/push_notification_feature.h"
#import "ios/chrome/browser/commerce/model/shopping_service_factory.h"
#import "ios/chrome/browser/discover_feed/model/discover_feed_service.h"
#import "ios/chrome/browser/discover_feed/model/discover_feed_service_factory.h"
#import "ios/chrome/browser/favicon/model/ios_chrome_large_icon_cache_factory.h"
#import "ios/chrome/browser/favicon/model/ios_chrome_large_icon_service_factory.h"
#import "ios/chrome/browser/favicon/model/large_icon_cache.h"
#import "ios/chrome/browser/feature_engagement/model/tracker_factory.h"
#import "ios/chrome/browser/ntp/model/new_tab_page_tab_helper.h"
#import "ios/chrome/browser/ntp/model/set_up_list_item_type.h"
#import "ios/chrome/browser/ntp/model/set_up_list_prefs.h"
#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_constants.h"
#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_delegate.h"
#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h"
#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_constants.h"
#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_metrics_delegate.h"
#import "ios/chrome/browser/ntp_tiles/model/ios_most_visited_sites_factory.h"
#import "ios/chrome/browser/parcel_tracking/features.h"
#import "ios/chrome/browser/parcel_tracking/parcel_tracking_prefs.h"
#import "ios/chrome/browser/passwords/model/password_checkup_utils.h"
#import "ios/chrome/browser/policy/model/policy_util.h"
#import "ios/chrome/browser/promos_manager/model/promos_manager_factory.h"
#import "ios/chrome/browser/push_notification/model/push_notification_client_id.h"
#import "ios/chrome/browser/push_notification/model/push_notification_service.h"
#import "ios/chrome/browser/reading_list/model/reading_list_model_factory.h"
#import "ios/chrome/browser/safety_check/model/ios_chrome_safety_check_manager.h"
#import "ios/chrome/browser/safety_check/model/ios_chrome_safety_check_manager_factory.h"
#import "ios/chrome/browser/search_engines/model/template_url_service_factory.h"
#import "ios/chrome/browser/segmentation_platform/model/segmentation_platform_service_factory.h"
#import "ios/chrome/browser/shared/coordinator/alert/action_sheet_coordinator.h"
#import "ios/chrome/browser/shared/coordinator/alert/alert_coordinator.h"
#import "ios/chrome/browser/shared/coordinator/layout_guide/layout_guide_util.h"
#import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/prefs/pref_backed_boolean.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
#import "ios/chrome/browser/shared/public/commands/application_commands.h"
#import "ios/chrome/browser/shared/public/commands/browser_coordinator_commands.h"
#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
#import "ios/chrome/browser/shared/public/commands/credential_provider_promo_commands.h"
#import "ios/chrome/browser/shared/public/commands/omnibox_commands.h"
#import "ios/chrome/browser/shared/public/commands/open_new_tab_command.h"
#import "ios/chrome/browser/shared/public/commands/parcel_tracking_opt_in_commands.h"
#import "ios/chrome/browser/shared/public/commands/settings_commands.h"
#import "ios/chrome/browser/shared/public/commands/show_signin_command.h"
#import "ios/chrome/browser/shared/public/commands/snackbar_commands.h"
#import "ios/chrome/browser/shared/public/commands/whats_new_commands.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/browser/signin/model/authentication_service.h"
#import "ios/chrome/browser/signin/model/authentication_service_factory.h"
#import "ios/chrome/browser/signin/model/chrome_account_manager_service.h"
#import "ios/chrome/browser/signin/model/chrome_account_manager_service_factory.h"
#import "ios/chrome/browser/signin/model/identity_manager_factory.h"
#import "ios/chrome/browser/sync/model/sync_service_factory.h"
#import "ios/chrome/browser/ui/authentication/signin/signin_constants.h"
#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.h"
#import "ios/chrome/browser/ui/content_suggestions/cells/most_visited_tiles_mediator.h"
#import "ios/chrome/browser/ui/content_suggestions/cells/shortcuts_mediator.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_delegate.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_image_data_source.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h"
#import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_collection_view.h"
#import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_collection_view_audience.h"
#import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_container_delegate.h"
#import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model.h"
#import "ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_mediator.h"
#import "ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_table_view_controller.h"
#import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
#import "ios/chrome/browser/ui/content_suggestions/parcel_tracking/magic_stack_parcel_list_half_sheet_table_view_controller.h"
#import "ios/chrome/browser/ui/content_suggestions/parcel_tracking/parcel_tracking_mediator.h"
#import "ios/chrome/browser/ui/content_suggestions/price_tracking_promo/price_tracking_promo_mediator.h"
#import "ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_magic_stack_mediator.h"
#import "ios/chrome/browser/ui/content_suggestions/safety_check/types.h"
#import "ios/chrome/browser/ui/content_suggestions/safety_check/utils.h"
#import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_content_notification_promo_coordinator.h"
#import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_content_notification_promo_coordinator_delegate.h"
#import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_default_browser_promo_coordinator.h"
#import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_default_browser_promo_coordinator_delegate.h"
#import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_view.h"
#import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_mediator.h"
#import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_show_more_view_controller.h"
#import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_tap_delegate.h"
#import "ios/chrome/browser/ui/content_suggestions/set_up_list/utils.h"
#import "ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_mediator.h"
#import "ios/chrome/browser/ui/menu/browser_action_factory.h"
#import "ios/chrome/browser/ui/menu/menu_histograms.h"
#import "ios/chrome/browser/ui/push_notification/notifications_confirmation_presenter.h"
#import "ios/chrome/browser/ui/push_notification/notifications_opt_in_alert_coordinator.h"
#import "ios/chrome/browser/ui/push_notification/notifications_opt_in_coordinator.h"
#import "ios/chrome/browser/ui/push_notification/notifications_opt_in_coordinator_delegate.h"
#import "ios/chrome/browser/url_loading/model/url_loading_browser_agent.h"
#import "ios/chrome/grit/ios_branded_strings.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/web/public/web_state.h"
#import "ui/base/l10n/l10n_util.h"
#import "ui/base/l10n/l10n_util_mac.h"
#import "url/gurl.h"

@interface ContentSuggestionsCoordinator () <
    ContentSuggestionsCommands,
    ContentSuggestionsViewControllerAudience,
    MagicStackCollectionViewControllerAudience,
    MagicStackHalfSheetTableViewControllerDelegate,
    MagicStackModuleContainerDelegate,
    MagicStackParcelListHalfSheetTableViewControllerDelegate,
    NotificationsConfirmationPresenter,
    NotificationsOptInAlertCoordinatorDelegate,
    NotificationsOptInCoordinatorDelegate,
    SetUpListContentNotificationPromoCoordinatorDelegate,
    SetUpListDefaultBrowserPromoCoordinatorDelegate,
    SetUpListTapDelegate>

@property(nonatomic, strong)
    ContentSuggestionsViewController* contentSuggestionsViewController;
// Authentication Service for the user's signed-in state.
@property(nonatomic, assign) AuthenticationService* authService;
// Redefined to not be readonly.
@property(nonatomic, strong)
    ContentSuggestionsMediator* contentSuggestionsMediator;
// Metrics recorder for the content suggestions.
@property(nonatomic, strong)
    ContentSuggestionsMetricsRecorder* contentSuggestionsMetricsRecorder;
// Parcel Tracking Mediator.
@property(nonatomic, strong) ParcelTrackingMediator* parcelTrackingMediator;
@property(nonatomic, strong) SetUpListMediator* setUpListMediator;

@end

@implementation ContentSuggestionsCoordinator {

  // The coordinator that displays the Default Browser Promo for the Set Up
  // List.
  SetUpListDefaultBrowserPromoCoordinator* _defaultBrowserPromoCoordinator;

  // The coordinator that displays the Content Notification Promo for the Set Up
  // List.
  SetUpListContentNotificationPromoCoordinator* _contentNotificationCoordinator;

  // The coordinator that displays the opt-in notification settings view for the
  // Set Up List.
  NotificationsOptInCoordinator* _notificationsOptInCoordinator;

  // The Show More Menu presented from the Set Up List in the Magic Stack.
  SetUpListShowMoreViewController* _setUpListShowMoreViewController;

  // The edit half sheet for toggling all Magic Stack modules.
  MagicStackHalfSheetTableViewController*
      _magicStackHalfSheetTableViewController;
  MagicStackHalfSheetMediator* _magicStackHalfSheetMediator;

  // The parcel list half sheet to see all tracked parcels.
  MagicStackParcelListHalfSheetTableViewController*
      _parcelListHalfSheetTableViewController;

  // The coordinator used to present a modal alert for the parcel tracking
  // module.
  AlertCoordinator* _parcelTrackingAlertCoordinator;

  // The coordinator used to present an alert to enable Tips notifications.
  NotificationsOptInAlertCoordinator* _notificationsOptInAlertCoordinator;

  MagicStackRankingModel* _magicStackRankingModel;

  // Module mediators.
  ShortcutsMediator* _shortcutsMediator;
  SafetyCheckMagicStackMediator* _safetyCheckMediator;
  MostVisitedTilesMediator* _mostVisitedTilesMediator;
  TabResumptionMediator* _tabResumptionMediator;
  PriceTrackingPromoMediator* _priceTrackingPromoMediator;

  MagicStackCollectionViewController* _magicStackCollectionView;

  segmentation_platform::SegmentationPlatformService* _segmentationService;
  segmentation_platform::DeviceSwitcherResultDispatcher*
      _deviceSwitcherResultDispatcher;
}

- (void)start {
  DCHECK(self.browser);
  DCHECK(self.NTPMetricsDelegate);
  if (self.started) {
    // Prevent this coordinator from being started twice in a row
    return;
  }
  _started = YES;

  ChromeBrowserState* browserState = self.browser->GetBrowserState();

  _segmentationService = segmentation_platform::
      SegmentationPlatformServiceFactory::GetForBrowserState(browserState);
  _deviceSwitcherResultDispatcher =
      segmentation_platform::SegmentationPlatformServiceFactory::
          GetDispatcherForBrowserState(browserState);

  self.authService =
      AuthenticationServiceFactory::GetForBrowserState(browserState);

  PrefService* prefs =
      ChromeBrowserState::FromBrowserState(browserState)->GetPrefs();

  favicon::LargeIconService* largeIconService =
      IOSChromeLargeIconServiceFactory::GetForBrowserState(browserState);

  LargeIconCache* cache =
      IOSChromeLargeIconCacheFactory::GetForBrowserState(browserState);

  std::unique_ptr<ntp_tiles::MostVisitedSites> mostVisitedFactory =
      IOSMostVisitedSitesFactory::NewForBrowserState(browserState);

  ReadingListModel* readingListModel =
      ReadingListModelFactory::GetForBrowserState(browserState);

  self.contentSuggestionsMetricsRecorder =
      [[ContentSuggestionsMetricsRecorder alloc]
          initWithLocalState:GetApplicationContext()->GetLocalState()];

  syncer::SyncService* syncService =
      SyncServiceFactory::GetForBrowserState(browserState);

  AuthenticationService* authenticationService =
      AuthenticationServiceFactory::GetForBrowserState(browserState);

  signin::IdentityManager* identityManager =
      IdentityManagerFactory::GetForBrowserState(browserState);

  commerce::ShoppingService* shoppingService =
      commerce::ShoppingServiceFactory::GetForBrowserState(browserState);

  self.contentSuggestionsMediator = [[ContentSuggestionsMediator alloc] init];

  NSMutableArray* moduleMediators = [NSMutableArray array];

  _mostVisitedTilesMediator = [[MostVisitedTilesMediator alloc]
      initWithMostVisitedSite:std::move(mostVisitedFactory)
                  prefService:prefs
             largeIconService:largeIconService
               largeIconCache:cache
       URLLoadingBrowserAgent:UrlLoadingBrowserAgent::FromBrowser(
                                  self.browser)];
  _mostVisitedTilesMediator.contentSuggestionsDelegate = self.delegate;
  _mostVisitedTilesMediator.contentSuggestionsMetricsRecorder =
      self.contentSuggestionsMetricsRecorder;
  _mostVisitedTilesMediator.actionFactory = [[BrowserActionFactory alloc]
      initWithBrowser:self.browser
             scenario:kMenuScenarioHistogramMostVisitedEntry];
  _mostVisitedTilesMediator.snackbarHandler =
      static_cast<id<SnackbarCommands>>(self.browser->GetCommandDispatcher());
  _mostVisitedTilesMediator.NTPMetricsDelegate = self.NTPMetricsDelegate;
  [moduleMediators addObject:_mostVisitedTilesMediator];
  self.contentSuggestionsMediator.mostVisitedTilesMediator =
      _mostVisitedTilesMediator;

  _shortcutsMediator = [[ShortcutsMediator alloc]
      initWithReadingListModel:readingListModel
      featureEngagementTracker:feature_engagement::TrackerFactory::
                                   GetForBrowserState(browserState)
                   authService:authenticationService];
  _shortcutsMediator.contentSuggestionsMetricsRecorder =
      self.contentSuggestionsMetricsRecorder;
  _shortcutsMediator.NTPMetricsDelegate = self.NTPMetricsDelegate;
  _shortcutsMediator.dispatcher = static_cast<
      id<ApplicationCommands, BrowserCoordinatorCommands, WhatsNewCommands>>(
      self.browser->GetCommandDispatcher());
  [moduleMediators addObject:_shortcutsMediator];
  self.contentSuggestionsMediator.shortcutsMediator = _shortcutsMediator;

  if (IsTabResumptionEnabled()) {
    _tabResumptionMediator = [[TabResumptionMediator alloc]
        initWithLocalState:GetApplicationContext()->GetLocalState()
               prefService:prefs
           identityManager:identityManager
                   browser:self.browser];
    _tabResumptionMediator.NTPMetricsDelegate = self.NTPMetricsDelegate;
    _tabResumptionMediator.contentSuggestionsMetricsRecorder =
        self.contentSuggestionsMetricsRecorder;
    [moduleMediators addObject:_tabResumptionMediator];
  }
  if (base::FeatureList::IsEnabled(commerce::kPriceTrackingPromo) &&
      IsPriceNotificationsEnabled() &&
      IsPriceTrackingEnabled(self.browser->GetBrowserState())) {
    _priceTrackingPromoMediator = [[PriceTrackingPromoMediator alloc]
        initWithShoppingService:commerce::ShoppingServiceFactory::
                                    GetForBrowserState(
                                        self.browser->GetBrowserState())];
    [moduleMediators addObject:_priceTrackingPromoMediator];
  }

  if (IsIOSParcelTrackingEnabled() &&
      !IsParcelTrackingDisabled(
          IsHomeCustomizationEnabled()
              ? prefs
              : GetApplicationContext()->GetLocalState())) {
    _parcelTrackingMediator = [[ParcelTrackingMediator alloc]
        initWithShoppingService:shoppingService
         URLLoadingBrowserAgent:UrlLoadingBrowserAgent::FromBrowser(
                                    self.browser)
                    prefService:IsHomeCustomizationEnabled()
                                    ? prefs
                                    : GetApplicationContext()->GetLocalState()];
    _parcelTrackingMediator.NTPMetricsDelegate = self.NTPMetricsDelegate;
    [moduleMediators addObject:_parcelTrackingMediator];
  }
  if (IsSafetyCheckMagicStackEnabled()) {
    IOSChromeSafetyCheckManager* safetyCheckManager =
        IOSChromeSafetyCheckManagerFactory::GetForBrowserState(browserState);
    _safetyCheckMediator = [[SafetyCheckMagicStackMediator alloc]
        initWithSafetyCheckManager:safetyCheckManager
                        localState:GetApplicationContext()->GetLocalState()
                         userState:prefs
                          appState:self.browser->GetSceneState().appState];
    _safetyCheckMediator.presentationAudience = self;
    [moduleMediators addObject:_safetyCheckMediator];
  }
  if (!ShouldPutMostVisitedSitesInMagicStack()) {
    ContentSuggestionsViewController* viewController =
        [[ContentSuggestionsViewController alloc] init];
    viewController.audience = self;
    viewController.urlLoadingBrowserAgent =
        UrlLoadingBrowserAgent::FromBrowser(self.browser);
    viewController.contentSuggestionsMetricsRecorder =
        self.contentSuggestionsMetricsRecorder;
    self.contentSuggestionsViewController = viewController;
  }
  BOOL isSetupListEnabled = set_up_list_utils::IsSetUpListActive(
      GetApplicationContext()->GetLocalState(), prefs);
  if (isSetupListEnabled) {
    const TemplateURL* defaultSearchURLTemplate =
        ios::TemplateURLServiceFactory::GetForBrowserState(browserState)
            ->GetDefaultSearchProvider();
    BOOL isDefaultSearchEngine = defaultSearchURLTemplate &&
                                 defaultSearchURLTemplate->prepopulate_id() ==
                                     TemplateURLPrepopulateData::google.id;
    _setUpListMediator = [[SetUpListMediator alloc]
                   initWithPrefService:prefs
                           syncService:syncService
                       identityManager:identityManager
                 authenticationService:authenticationService
                            sceneState:self.browser->GetSceneState()
                 isDefaultSearchEngine:isDefaultSearchEngine
                   segmentationService:_segmentationService
        deviceSwitcherResultDispatcher:_deviceSwitcherResultDispatcher];
    if (IsSegmentedDefaultBrowserPromoEnabled()) {
      [_setUpListMediator retrieveUserSegment];
    }
    _setUpListMediator.commandHandler = self;
    _setUpListMediator.contentSuggestionsMetricsRecorder =
        self.contentSuggestionsMetricsRecorder;
    _setUpListMediator.delegate = self.delegate;
    self.contentSuggestionsMediator.setUpListMediator = _setUpListMediator;
    [moduleMediators addObject:_setUpListMediator];
  }
  _magicStackRankingModel = [[MagicStackRankingModel alloc]
      initWithSegmentationService:_segmentationService
                  shoppingService:commerce::ShoppingServiceFactory::
                                      GetForBrowserState(
                                          self.browser->GetBrowserState())
                      prefService:prefs
                       localState:GetApplicationContext()->GetLocalState()
                  moduleMediators:moduleMediators];
  _magicStackRankingModel.contentSuggestionsMetricsRecorder =
      self.contentSuggestionsMetricsRecorder;
  self.contentSuggestionsMediator.magicStackRankingModel =
      _magicStackRankingModel;
  _magicStackRankingModel.delegate = self.contentSuggestionsMediator;
  _magicStackRankingModel.homeStartDataSource = self.homeStartDataSource;

  _magicStackCollectionView = [[MagicStackCollectionViewController alloc] init];
  _magicStackCollectionView.audience = self;
  _mostVisitedTilesMediator.consumer = self.contentSuggestionsViewController;

  self.contentSuggestionsMediator.magicStackConsumer =
      _magicStackCollectionView;
  self.contentSuggestionsMediator.consumer =
      self.contentSuggestionsViewController;
  [self.browser->GetCommandDispatcher()
      startDispatchingToTarget:self
                   forProtocol:@protocol(ContentSuggestionsCommands)];
}

- (void)stop {
  _segmentationService = nullptr;
  _deviceSwitcherResultDispatcher = nullptr;
  [self.parcelTrackingMediator disconnect];
  self.parcelTrackingMediator = nil;
  [_shortcutsMediator disconnect];
  _shortcutsMediator = nil;
  [_safetyCheckMediator disconnect];
  _safetyCheckMediator = nil;
  [_setUpListMediator disconnect];
  _setUpListMediator = nil;
  [_mostVisitedTilesMediator disconnect];
  _mostVisitedTilesMediator = nil;
  [_tabResumptionMediator disconnect];
  _tabResumptionMediator = nil;
  [_magicStackRankingModel disconnect];
  _magicStackRankingModel = nil;
  [self.contentSuggestionsMediator disconnect];
  self.contentSuggestionsMediator = nil;
  [self.contentSuggestionsMetricsRecorder disconnect];
  self.contentSuggestionsMetricsRecorder = nil;
  self.contentSuggestionsViewController.audience = nil;
  self.contentSuggestionsViewController = nil;
  [_defaultBrowserPromoCoordinator stop];
  _defaultBrowserPromoCoordinator = nil;
  [_magicStackHalfSheetMediator disconnect];
  _magicStackHalfSheetMediator = nil;
  [_magicStackHalfSheetTableViewController.presentingViewController
      dismissViewControllerAnimated:NO
                         completion:nil];
  _magicStackHalfSheetTableViewController = nil;
  [self dismissParcelListHalfSheet];
  [self dismissParcelTrackingAlertCoordinator];
  [_notificationsOptInAlertCoordinator stop];
  _notificationsOptInAlertCoordinator = nil;
  [self.browser->GetCommandDispatcher()
      stopDispatchingForProtocol:@protocol(ContentSuggestionsCommands)];
  _started = NO;
}

- (ContentSuggestionsViewController*)viewController {
  return self.contentSuggestionsViewController;
}

#pragma mark - Public methods

- (void)refresh {
  [_magicStackCollectionView reset];
  // Refresh in case there are new MVT to show.
  [_mostVisitedTilesMediator refreshMostVisitedTiles];
  [_safetyCheckMediator reset];
  [_parcelTrackingMediator reset];
  [_magicStackRankingModel fetchLatestMagicStackRanking];
  // Fetch after resetting ranking since parcels could be returned
  // synchronously.
  [_parcelTrackingMediator fetchTrackedParcels];
}

#pragma mark - ContentSuggestionsCommands

- (void)showSetUpListSeeMoreMenu {
  [_setUpListShowMoreViewController.presentingViewController
      dismissViewControllerAnimated:NO
                         completion:nil];
  NSArray<SetUpListItemViewData*>* items = [self.setUpListMediator allItems];
  _setUpListShowMoreViewController =
      [[SetUpListShowMoreViewController alloc] initWithItems:items
                                                 tapDelegate:self];
  _setUpListShowMoreViewController.modalPresentationStyle =
      UIModalPresentationPageSheet;
  UISheetPresentationController* presentationController =
      _setUpListShowMoreViewController.sheetPresentationController;
  presentationController.prefersEdgeAttachedInCompactHeight = YES;
  presentationController.widthFollowsPreferredContentSizeWhenEdgeAttached = YES;
  presentationController.detents = @[
    UISheetPresentationControllerDetent.mediumDetent,
    UISheetPresentationControllerDetent.largeDetent
  ];
  presentationController.preferredCornerRadius = 16;
  [_magicStackCollectionView
      presentViewController:_setUpListShowMoreViewController
                   animated:YES
                 completion:nil];
}

#pragma mark - ContentSuggestionsViewControllerAudience

- (void)viewWillDisappear {
  DiscoverFeedServiceFactory::GetForBrowserState(
      self.browser->GetBrowserState())
      ->SetIsShownOnStartSurface(false);
}

- (void)didTapMagicStackEditButton {
  base::RecordAction(base::UserMetricsAction("IOSMagicStackSettingsOpened"));
  if (IsHomeCustomizationEnabled()) {
    [self.delegate openMagicStackCustomizationMenu];
  } else {
    _magicStackHalfSheetTableViewController =
        [[MagicStackHalfSheetTableViewController alloc] init];

    _magicStackHalfSheetMediator = [[MagicStackHalfSheetMediator alloc]
        initWithPrefService:GetApplicationContext()->GetLocalState()];
    _magicStackHalfSheetMediator.consumer =
        _magicStackHalfSheetTableViewController;
    _magicStackHalfSheetTableViewController.delegate = self;
    _magicStackHalfSheetTableViewController.modelDelegate =
        _magicStackHalfSheetMediator;

    UINavigationController* navViewController = [[UINavigationController alloc]
        initWithRootViewController:_magicStackHalfSheetTableViewController];

    navViewController.modalPresentationStyle = UIModalPresentationPageSheet;
    UISheetPresentationController* presentationController =
        navViewController.sheetPresentationController;
    presentationController.prefersEdgeAttachedInCompactHeight = YES;
    presentationController.widthFollowsPreferredContentSizeWhenEdgeAttached =
        YES;
    presentationController.detents = @[
      UISheetPresentationControllerDetent.mediumDetent,
      UISheetPresentationControllerDetent.largeDetent
    ];
    [_magicStackCollectionView presentViewController:navViewController
                                            animated:YES
                                          completion:nil];
  }
}

- (void)showMagicStackParcelList {
  _parcelListHalfSheetTableViewController =
      [[MagicStackParcelListHalfSheetTableViewController alloc]
          initWithParcels:[self.parcelTrackingMediator allParcelTrackingItems]];
  _parcelListHalfSheetTableViewController.delegate = self;

  UINavigationController* navViewController = [[UINavigationController alloc]
      initWithRootViewController:_parcelListHalfSheetTableViewController];

  navViewController.modalPresentationStyle = UIModalPresentationPageSheet;
  UISheetPresentationController* presentationController =
      navViewController.sheetPresentationController;
  presentationController.prefersEdgeAttachedInCompactHeight = YES;
  presentationController.widthFollowsPreferredContentSizeWhenEdgeAttached = YES;
  presentationController.detents = @[
    UISheetPresentationControllerDetent.mediumDetent,
    UISheetPresentationControllerDetent.largeDetent
  ];
  [_magicStackCollectionView presentViewController:navViewController
                                          animated:YES
                                        completion:nil];
}

- (void)didTapSetUpListItemView:(SetUpListItemView*)view {
  [self didSelectSetUpListItem:view.type];
}

- (void)showMagicStackRecentTabs {
  CommandDispatcher* dispatcher = self.browser->GetCommandDispatcher();
  id<BrowserCoordinatorCommands> browserCoordinatorCommands =
      HandlerForProtocol(dispatcher, BrowserCoordinatorCommands);
  [browserCoordinatorCommands showRecentTabs];
}

#pragma mark - MagicStackModuleContainerDelegate

- (void)seeMoreWasTappedForModuleType:(ContentSuggestionsModuleType)type {
  switch (type) {
    case ContentSuggestionsModuleType::kSafetyCheck:
      [self didSelectSafetyCheckItem:SafetyCheckItemType::kDefault];
      break;
    case ContentSuggestionsModuleType::kCompactedSetUpList:
      [self showSetUpListSeeMoreMenu];
      break;
    case ContentSuggestionsModuleType::kParcelTracking:
      [self showMagicStackParcelList];
      break;
    case ContentSuggestionsModuleType::kTabResumption:
      [self showMagicStackRecentTabs];
      break;
    default:
      break;
  }
}

- (void)neverShowModuleType:(ContentSuggestionsModuleType)type {
  switch (type) {
    case ContentSuggestionsModuleType::kTabResumption:
      [_tabResumptionMediator disableModule];
      break;
    case ContentSuggestionsModuleType::kSafetyCheck:
      [_safetyCheckMediator disableModule];
      break;
    case ContentSuggestionsModuleType::kSetUpListSync:
    case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
    case ContentSuggestionsModuleType::kSetUpListAutofill:
    case ContentSuggestionsModuleType::kSetUpListNotifications:
    case ContentSuggestionsModuleType::kCompactedSetUpList:
      [_setUpListMediator disableModule];
      break;
    case ContentSuggestionsModuleType::kParcelTracking: {
      [self presentParcelTrackingAlertCoordinator];
      break;
    }
    default:
      break;
  }
}

// Returns the `PushNotificationClientId` associated with the specified `type`.
// Currently, push notifications are exclusively supported by the Set Up List
// and Safety Check modules.
- (PushNotificationClientId)pushNotificationClientId:
    (ContentSuggestionsModuleType)type {
  // This is only supported for Set Up List and Safety Check modules.
  CHECK(IsSetUpListModuleType(type) ||
        type == ContentSuggestionsModuleType::kSafetyCheck);

  if (type == ContentSuggestionsModuleType::kSafetyCheck) {
    return PushNotificationClientId::kSafetyCheck;
  }

  if (IsSetUpListModuleType(type)) {
    return PushNotificationClientId::kTips;
  }

  NOTREACHED();
}

// Retrieves the message ID for the push notification feature title associated
// with the specified `ContentSuggestionsModuleType`. Currently, push
// notifications are exclusively supported by the Set Up List and Safety Check
// modules.
- (int)pushNotificationTitleMessageId:(ContentSuggestionsModuleType)type {
  // This is only supported for Set Up List and Safety Check modules.
  CHECK(IsSetUpListModuleType(type) ||
        type == ContentSuggestionsModuleType::kSafetyCheck);

  if (type == ContentSuggestionsModuleType::kSafetyCheck) {
    return IDS_IOS_SAFETY_CHECK_TITLE;
  }

  if (IsSetUpListModuleType(type)) {
    return content_suggestions::SetUpListTitleStringID();
  }

  NOTREACHED();
}

- (void)enableNotifications:(ContentSuggestionsModuleType)type {
  // This is only supported for Set Up List and Safety Check modules.
  CHECK(IsSetUpListModuleType(type) ||
        type == ContentSuggestionsModuleType::kSafetyCheck);

  // Ask user for permission to opt-in to notifications.
  [_notificationsOptInAlertCoordinator stop];

  _notificationsOptInAlertCoordinator =
      [[NotificationsOptInAlertCoordinator alloc]
          initWithBaseViewController:self.viewController
                             browser:self.browser];

  _notificationsOptInAlertCoordinator.delegate = self;

  const PushNotificationClientId clientId =
      [self pushNotificationClientId:type];

  _notificationsOptInAlertCoordinator.clientIds = std::vector{clientId};

  int featureTitle = [self pushNotificationTitleMessageId:type];

  _notificationsOptInAlertCoordinator.confirmationMessage =
      l10n_util::GetNSStringF(IDS_IOS_NOTIFICATIONS_CONFIRMATION_MESSAGE,
                              l10n_util::GetStringUTF16(featureTitle));

  [_notificationsOptInAlertCoordinator start];
}

- (void)disableNotifications:(ContentSuggestionsModuleType)type {
  // This is only supported for Set Up List and Safety Check modules.
  CHECK(IsSetUpListModuleType(type) ||
        type == ContentSuggestionsModuleType::kSafetyCheck);

  id<SystemIdentity> identity =
      self.authService->GetPrimaryIdentity(signin::ConsentLevel::kSignin);

  const PushNotificationClientId clientId =
      [self pushNotificationClientId:type];

  GetApplicationContext()->GetPushNotificationService()->SetPreference(
      identity.gaiaID, clientId, false);

  // Show confirmation snackbar.
  NSString* buttonText =
      l10n_util::GetNSString(IDS_IOS_NOTIFICATIONS_MANAGE_SETTINGS);

  int featureTitle = [self pushNotificationTitleMessageId:type];

  NSString* message =
      l10n_util::GetNSStringF(IDS_IOS_NOTIFICATIONS_CONFIRMATION_MESSAGE_OFF,
                              l10n_util::GetStringUTF16(featureTitle));

  CommandDispatcher* dispatcher = self.browser->GetCommandDispatcher();

  id<SnackbarCommands> snackbarHandler =
      HandlerForProtocol(dispatcher, SnackbarCommands);

  __weak id<SettingsCommands> weakSettingsHandler =
      HandlerForProtocol(dispatcher, SettingsCommands);

  [snackbarHandler showSnackbarWithMessage:message
                                buttonText:buttonText
                             messageAction:^{
                               [weakSettingsHandler showNotificationsSettings];
                             }
                          completionAction:nil];
}

- (void)customizeCardsWasTapped {
  [self didTapMagicStackEditButton];
}

#pragma mark - MagicStackHalfSheetTableViewControllerDelegate

- (void)dismissMagicStackHalfSheet {
  [_magicStackHalfSheetMediator disconnect];
  _magicStackHalfSheetMediator = nil;
  [_magicStackHalfSheetTableViewController.presentingViewController
      dismissViewControllerAnimated:YES
                         completion:nil];
  _magicStackHalfSheetTableViewController = nil;
}

#pragma mark - MagicStackParcelListHalfSheetTableViewControllerDelegate

- (void)dismissParcelListHalfSheet {
  [_parcelListHalfSheetTableViewController.presentingViewController
      dismissViewControllerAnimated:YES
                         completion:nil];
  _parcelListHalfSheetTableViewController = nil;
}

- (void)untrackParcel:(NSString*)parcelID carrier:(ParcelType)carrier {
  [self.parcelTrackingMediator untrackParcel:parcelID];

  id<SnackbarCommands> snackbarHandler = HandlerForProtocol(
      self.browser->GetCommandDispatcher(), SnackbarCommands);
  __weak __typeof(self) weakSelf = self;
  [snackbarHandler
      showSnackbarWithMessage:
          l10n_util::GetNSString(IDS_IOS_PARCEL_TRACKING_UNTRACK_SNACKBAR_TITLE)
                   buttonText:l10n_util::GetNSString(
                                  IDS_IOS_SNACKBAR_ACTION_UNDO)
                messageAction:^{
                  __strong __typeof(weakSelf) strongSelf = weakSelf;
                  if (!strongSelf) {
                    return;
                  }
                  [weakSelf.parcelTrackingMediator trackParcel:parcelID
                                                       carrier:carrier];
                }
             completionAction:nil];
}

#pragma mark - SafetyCheckViewDelegate

// Called when a Safety Check item is selected by the user. Depending on the
// Safety Check item `type`, this method fires a UI command to present the
// Update Chrome page, Password Checkup, or Safety Check half sheet.
- (void)didSelectSafetyCheckItem:(SafetyCheckItemType)type {
  CHECK(IsSafetyCheckMagicStackEnabled());

  [self.NTPMetricsDelegate safetyCheckOpened];
  Browser* browser = self.browser;
  [_magicStackRankingModel logMagicStackEngagementForType:
                               ContentSuggestionsModuleType::kSafetyCheck];

  IOSChromeSafetyCheckManager* safetyCheckManager =
      IOSChromeSafetyCheckManagerFactory::GetForBrowserState(
          browser->GetBrowserState());

  id<ApplicationCommands> applicationHandler =
      HandlerForProtocol(browser->GetCommandDispatcher(), ApplicationCommands);
  id<SettingsCommands> settingsHandler =
      HandlerForProtocol(browser->GetCommandDispatcher(), SettingsCommands);

  switch (type) {
    case SafetyCheckItemType::kUpdateChrome: {
      const GURL& chrome_upgrade_url =
          safetyCheckManager->GetChromeAppUpgradeUrl();
      HandleSafetyCheckUpdateChromeTap(chrome_upgrade_url, applicationHandler);
      break;
    }
    case SafetyCheckItemType::kPassword: {
      std::vector<password_manager::CredentialUIEntry> credentials =
          safetyCheckManager->GetInsecureCredentials();
      HandleSafetyCheckPasswordTap(credentials, applicationHandler,
                                   settingsHandler);
      break;
    }
    case SafetyCheckItemType::kSafeBrowsing:
      [settingsHandler showSafeBrowsingSettings];
      break;
    case SafetyCheckItemType::kAllSafe:
    case SafetyCheckItemType::kRunning:
    case SafetyCheckItemType::kDefault:
      password_manager::PasswordCheckReferrer referrer =
          password_manager::PasswordCheckReferrer::kSafetyCheckMagicStack;
      [settingsHandler showAndStartSafetyCheckForReferrer:referrer];
      break;
  }
}

#pragma mark - SetUpListTapDelegate

- (void)didSelectSetUpListItem:(SetUpListItemType)type {
    if (set_up_list_utils::ShouldShowCompactedSetUpListModule()) {
      [_magicStackRankingModel
          logMagicStackEngagementForType:ContentSuggestionsModuleType::
                                             kCompactedSetUpList];
    } else {
      [_magicStackRankingModel
          logMagicStackEngagementForType:SetUpListModuleTypeForSetUpListType(
                                             type)];
    }
  [self.contentSuggestionsMetricsRecorder recordSetUpListItemSelected:type];
  [self.NTPMetricsDelegate setUpListItemOpened];
  PrefService* localState = GetApplicationContext()->GetLocalState();
  set_up_list_prefs::RecordInteraction(localState);

  __weak ContentSuggestionsCoordinator* weakSelf = self;
  ProceduralBlock completionBlock = ^{
    switch (type) {
      case SetUpListItemType::kSignInSync:
        [weakSelf showSignIn];
        break;
      case SetUpListItemType::kDefaultBrowser:
        [weakSelf showDefaultBrowserPromo];
        break;
      case SetUpListItemType::kAutofill:
        [weakSelf showCredentialProviderPromo];
        break;
      case SetUpListItemType::kNotifications:
        if (IsIOSTipsNotificationsEnabled()) {
          [weakSelf showNotificationsOptInView];
        } else {
          [weakSelf showContentNotificationBottomSheet];
        }
        break;
      case SetUpListItemType::kFollow:
      case SetUpListItemType::kAllSet:
        // TODO(crbug.com/40262090): Add a Follow item to the Set Up List.
        NOTREACHED_IN_MIGRATION();
    }
  };

  if (_setUpListShowMoreViewController) {
    [_setUpListShowMoreViewController.presentingViewController
        dismissViewControllerAnimated:YES
                           completion:completionBlock];
    _setUpListShowMoreViewController = nil;
  } else {
    completionBlock();
  }
}

- (void)dismissSeeMoreViewController {
  DCHECK(_setUpListShowMoreViewController);
  [_setUpListShowMoreViewController.presentingViewController
      dismissViewControllerAnimated:YES
                         completion:nil];
  _setUpListShowMoreViewController = nil;
}

#pragma mark - SetUpList Helpers

// Shows the Default Browser Promo.
- (void)showDefaultBrowserPromo {
  // Stop the coordinator if it is already running. If the user swipes to
  // dismiss a previous instance and then clicks the item again the
  // previous instance may not have been stopped yet due to the animation.
  [_defaultBrowserPromoCoordinator stop];
  if (IsSegmentedDefaultBrowserPromoEnabled()) {
    _defaultBrowserPromoCoordinator =
        [[SetUpListDefaultBrowserPromoCoordinator alloc]
                initWithBaseViewController:[self viewController]
                                   browser:self.browser
                               application:[UIApplication sharedApplication]
                       segmentationService:_segmentationService
            deviceSwitcherResultDispatcher:_deviceSwitcherResultDispatcher];
  } else {
    _defaultBrowserPromoCoordinator =
        [[SetUpListDefaultBrowserPromoCoordinator alloc]
                initWithBaseViewController:[self viewController]
                                   browser:self.browser
                               application:[UIApplication sharedApplication]
                       segmentationService:nullptr
            deviceSwitcherResultDispatcher:nullptr];
  }
  _defaultBrowserPromoCoordinator.delegate = self;
  [_defaultBrowserPromoCoordinator start];
}

// Shows the SigninSync UI with the SetUpList access point.
- (void)showSignIn {
  ShowSigninCommandCompletionCallback callback =
      ^(SigninCoordinatorResult result, SigninCompletionInfo* completionInfo) {
        if (result == SigninCoordinatorResultSuccess ||
            result == SigninCoordinatorResultCanceledByUser) {
          PrefService* localState = GetApplicationContext()->GetLocalState();
          set_up_list_prefs::MarkItemComplete(localState,
                                              SetUpListItemType::kSignInSync);
        }
      };
  // If there are 0 identities, kInstantSignin requires less taps.
  AuthenticationOperation operation =
      ChromeAccountManagerServiceFactory::GetForBrowserState(
          self.browser->GetBrowserState())
              ->HasIdentities()
          ? AuthenticationOperation::kSigninOnly
          : AuthenticationOperation::kInstantSignin;
  ShowSigninCommand* command = [[ShowSigninCommand alloc]
      initWithOperation:operation
               identity:nil
            accessPoint:signin_metrics::AccessPoint::ACCESS_POINT_SET_UP_LIST
            promoAction:signin_metrics::PromoAction::
                            PROMO_ACTION_NO_SIGNIN_PROMO
               callback:callback];
  [HandlerForProtocol(self.browser->GetCommandDispatcher(), ApplicationCommands)
              showSignin:command
      baseViewController:self.viewController];
}

// Shows the Credential Provider Promo using the SetUpList trigger.
- (void)showCredentialProviderPromo {
  [HandlerForProtocol(self.browser->GetCommandDispatcher(),
                      CredentialProviderPromoCommands)
      showCredentialProviderPromoWithTrigger:CredentialProviderPromoTrigger::
                                                 SetUpList];
}

- (void)showContentNotificationBottomSheet {
  // Stop the coordinator if it is already running. If the user swipes to
  // dismiss a previous instance and then clicks the item again the
  // previous instance may not have been stopped yet due to the animation.
  [_contentNotificationCoordinator stop];
  _contentNotificationCoordinator =
      [[SetUpListContentNotificationPromoCoordinator alloc]
          initWithBaseViewController:[self viewController]
                             browser:self.browser
                         application:[UIApplication sharedApplication]];
  _contentNotificationCoordinator.delegate = self;
  _contentNotificationCoordinator.messagePresenter = self;
  [_contentNotificationCoordinator start];
}

- (void)showNotificationsOptInView {
  [_notificationsOptInCoordinator stop];
  _notificationsOptInCoordinator = [[NotificationsOptInCoordinator alloc]
      initWithBaseViewController:[self viewController]
                         browser:self.browser];
  _notificationsOptInCoordinator.delegate = self;
  [_notificationsOptInCoordinator start];
}

#pragma mark - NotificationsOptInAlertCoordinatorDelegate

- (void)notificationsOptInAlertCoordinator:
            (NotificationsOptInAlertCoordinator*)alertCoordinator
                                    result:
                                        (NotificationsOptInAlertResult)result {
  CHECK_EQ(_notificationsOptInAlertCoordinator, alertCoordinator);
  [_notificationsOptInAlertCoordinator stop];
  _notificationsOptInAlertCoordinator = nil;
}

#pragma mark - NotificationsOptInCoordinatorDelegate

- (void)notificationsOptInScreenDidFinish:
    (NotificationsOptInCoordinator*)coordinator {
  CHECK_EQ(coordinator, _notificationsOptInCoordinator);
  [_notificationsOptInCoordinator stop];
  _notificationsOptInCoordinator = nil;
}

#pragma mark - SetUpListDefaultBrowserPromoCoordinatorDelegate

- (void)setUpListDefaultBrowserPromoDidFinish:(BOOL)success {
  [_defaultBrowserPromoCoordinator stop];
  _defaultBrowserPromoCoordinator = nil;
}

#pragma mark - SetUpListContentNotificationPromoCoordinatorDelegate

- (void)setUpListContentNotificationPromoDidFinish {
  [_contentNotificationCoordinator stop];
  _contentNotificationCoordinator = nil;
}

#pragma mark - NotificationsConfirmationPresenter

- (void)presentNotificationsConfirmationMessage {
  id<SnackbarCommands> snackbarHandler = HandlerForProtocol(
      self.browser->GetCommandDispatcher(), SnackbarCommands);
  __weak __typeof(self) weakSelf = self;
  [snackbarHandler
      showSnackbarWithMessage:l10n_util::GetNSString(
                                  IDS_IOS_CONTENT_NOTIFICATION_SNACKBAR_TITLE)
                   buttonText:
                       l10n_util::GetNSString(
                           IDS_IOS_CONTENT_NOTIFICATION_SNACKBAR_ACTION_MANAGE)
                messageAction:^{
                  [weakSelf showNotificationSettings];
                }
             completionAction:nil];

  [self.contentSuggestionsMetricsRecorder
      recordContentNotificationSnackbarEvent:ContentNotificationSnackbarEvent::
                                                 kShown];
}

#pragma mark - Helpers

// Presents the parcel tracking alert modal.
- (void)presentParcelTrackingAlertCoordinator {
  _parcelTrackingAlertCoordinator = [[AlertCoordinator alloc]
      initWithBaseViewController:self.viewController
                         browser:self.browser
                           title:
                               l10n_util::GetNSString(
                                   IDS_IOS_PARCEL_TRACKING_MODULE_HIDE_ALERT_TITLE)
                         message:
                             l10n_util::GetNSStringF(
                                 IDS_IOS_PARCEL_TRACKING_MODULE_HIDE_ALERT_DESCRIPTION,
                                 base::SysNSStringToUTF16(l10n_util::GetNSString(
                                     IDS_IOS_CONTENT_SUGGESTIONS_PARCEL_TRACKING_MODULE_TITLE)))];

  __weak ContentSuggestionsCoordinator* weakSelf = self;
  [_parcelTrackingAlertCoordinator
      addItemWithTitle:
          l10n_util::GetNSStringF(
              IDS_IOS_PARCEL_TRACKING_CONTEXT_MENU_DESCRIPTION,
              base::SysNSStringToUTF16(l10n_util::GetNSString(
                  IDS_IOS_CONTENT_SUGGESTIONS_PARCEL_TRACKING_MODULE_TITLE)))
                action:^{
                  __strong __typeof(weakSelf) strongSelf = weakSelf;
                  if (!strongSelf) {
                    return;
                  }
                  [weakSelf.parcelTrackingMediator disableModule];
                  [weakSelf dismissParcelTrackingAlertCoordinator];
                }
                 style:UIAlertActionStyleDefault];
  [_parcelTrackingAlertCoordinator
      addItemWithTitle:l10n_util::GetNSString(
                           IDS_IOS_PARCEL_TRACKING_MODULE_HIDE_ALERT_CANCEL)
                action:^{
                  [weakSelf dismissParcelTrackingAlertCoordinator];
                }
                 style:UIAlertActionStyleCancel];

  [_parcelTrackingAlertCoordinator start];
}

// Display the notification settings.
- (void)showNotificationSettings {
  [self.contentSuggestionsMetricsRecorder
      recordContentNotificationSnackbarEvent:ContentNotificationSnackbarEvent::
                                                 kActionButtonTapped];
  id<SettingsCommands> settingsHandler = HandlerForProtocol(
      self.browser->GetCommandDispatcher(), SettingsCommands);
  [settingsHandler showNotificationsSettings];
}

// Dismisses the parcel tracking alert modal.
- (void)dismissParcelTrackingAlertCoordinator {
  [_parcelTrackingAlertCoordinator stop];
  _parcelTrackingAlertCoordinator = nil;
}

@end