chromium/ios/chrome/browser/ui/push_notification/notifications_opt_in_coordinator.mm

// Copyright 2024 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/push_notification/notifications_opt_in_coordinator.h"

#import "base/metrics/histogram_functions.h"
#import "base/metrics/user_metrics.h"
#import "base/metrics/user_metrics_action.h"
#import "components/signin/public/base/signin_metrics.h"
#import "ios/chrome/browser/content_notification/model/content_notification_util.h"
#import "ios/chrome/browser/push_notification/model/push_notification_client_id.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/public/commands/application_commands.h"
#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
#import "ios/chrome/browser/shared/public/commands/show_signin_command.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_factory.h"
#import "ios/chrome/browser/ui/authentication/signin/signin_constants.h"
#import "ios/chrome/browser/ui/push_notification/metrics.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_delegate.h"
#import "ios/chrome/browser/ui/push_notification/notifications_opt_in_item_identifier.h"
#import "ios/chrome/browser/ui/push_notification/notifications_opt_in_mediator.h"
#import "ios/chrome/browser/ui/push_notification/notifications_opt_in_presenter.h"
#import "ios/chrome/browser/ui/push_notification/notifications_opt_in_view_controller.h"
#import "ios/chrome/grit/ios_branded_strings.h"
#import "ui/base/l10n/l10n_util_mac.h"

@interface NotificationsOptInCoordinator () <
    UIAdaptivePresentationControllerDelegate,
    NotificationsOptInPresenter,
    NotificationsOptInAlertCoordinatorDelegate>

// Mediator for NotificationsOptInViewController.
@property(nonatomic, strong) NotificationsOptInMediator* mediator;

@end

@implementation NotificationsOptInCoordinator {
  // View controller for the Notifications Opt-In screen.
  NotificationsOptInViewController* _viewController;
  // Coordinator that presents the notifications opt-in alert.
  NotificationsOptInAlertCoordinator* _optInAlertCoordinator;
}

- (void)start {
  _viewController = [[NotificationsOptInViewController alloc] init];
  NotificationsOptInMediator* mediator = [[NotificationsOptInMediator alloc]
      initWithAuthenticationService:AuthenticationServiceFactory::
                                        GetForBrowserState(
                                            self.browser->GetBrowserState())];
  mediator.consumer = _viewController;
  mediator.presenter = self;
  _viewController.delegate = mediator;
  _viewController.notificationsDelegate = mediator;
  _viewController.presentationController.delegate = self;
  _viewController.isContentNotificationEnabled =
      IsContentNotificationEnabled(self.browser->GetBrowserState());
  [mediator configureConsumer];
  self.mediator = mediator;
  [self.baseViewController presentViewController:_viewController
                                        animated:YES
                                      completion:nil];
}

- (void)stop {
  [super stop];
  [_viewController.presentingViewController dismissViewControllerAnimated:NO
                                                               completion:nil];
  _viewController = nil;
  self.mediator = nil;
  [_optInAlertCoordinator stop];
  _optInAlertCoordinator = nil;
}

#pragma mark - NotificationsOptInPresenter

- (void)presentSignIn {
  __weak __typeof(self) weakSelf = self;
  ShowSigninCommandCompletionCallback callback =
      ^(SigninCoordinatorResult result, SigninCompletionInfo* completionInfo) {
        if (result != SigninCoordinatorResultSuccess) {
          [weakSelf.mediator disableUserSelectionForItem:kContent];
        }
      };
  // If there are 0 identities, kInstantSignin requires less taps.
  ChromeBrowserState* browserState = self.browser->GetBrowserState();
  AuthenticationOperation operation =
      ChromeAccountManagerServiceFactory::GetForBrowserState(browserState)
              ->HasIdentities()
          ? AuthenticationOperation::kSigninOnly
          : AuthenticationOperation::kInstantSignin;
  ShowSigninCommand* command = [[ShowSigninCommand alloc]
      initWithOperation:operation
               identity:nil
            accessPoint:
                signin_metrics::AccessPoint::
                    ACCESS_POINT_NOTIFICATIONS_OPT_IN_SCREEN_CONTENT_TOGGLE
            promoAction:signin_metrics::PromoAction::
                            PROMO_ACTION_NO_SIGNIN_PROMO
               callback:callback];
  [HandlerForProtocol(self.browser->GetCommandDispatcher(), ApplicationCommands)
              showSignin:command
      baseViewController:_viewController];
}

- (void)presentNotificationsAlertForClientIds:
    (std::vector<PushNotificationClientId>)clientIds {
  [_optInAlertCoordinator stop];
  _optInAlertCoordinator = [[NotificationsOptInAlertCoordinator alloc]
      initWithBaseViewController:_viewController
                         browser:self.browser];
  _optInAlertCoordinator.delegate = self;
  _optInAlertCoordinator.clientIds = clientIds;
  [_optInAlertCoordinator start];
}

- (void)dismiss {
  [self dismissViewController];
}

#pragma mark - NotificationsOptInAlertCoordinatorDelegate

- (void)notificationsOptInAlertCoordinator:
            (NotificationsOptInAlertCoordinator*)alertCoordinator
                                    result:
                                        (NotificationsOptInAlertResult)result {
  CHECK_EQ(_optInAlertCoordinator, alertCoordinator);
  [_optInAlertCoordinator stop];
  _optInAlertCoordinator = nil;
  switch (result) {
    case NotificationsOptInAlertResult::kPermissionGranted:
      [self dismissViewController];
      [self recordNotificationsEnabledForClients:alertCoordinator.clientIds
                                                     .value()];
      break;
    case NotificationsOptInAlertResult::kPermissionDenied:
    case NotificationsOptInAlertResult::kOpenedSettings:
    case NotificationsOptInAlertResult::kCanceled:
    case NotificationsOptInAlertResult::kError:
      break;
  }
}

#pragma mark - UIAdaptivePresentationControllerDelegate

- (void)presentationControllerDidDismiss:
    (UIPresentationController*)presentationController {
  [self.delegate notificationsOptInScreenDidFinish:self];
  base::UmaHistogramEnumeration(
      kNotificationsOptInPromptActionHistogram,
      NotificationsOptInPromptActionType::kSwipedToDismiss);
}

#pragma mark - Private

// Dismisses the base view controller.
- (void)dismissViewController {
  __weak __typeof(self) weakSelf = self;
  [_viewController.presentingViewController
      dismissViewControllerAnimated:YES
                         completion:^{
                           [weakSelf.delegate
                               notificationsOptInScreenDidFinish:weakSelf];
                         }];
}

// Records notifications that are enabled through the prompt.
- (void)recordNotificationsEnabledForClients:
    (std::vector<PushNotificationClientId>)clientIds {
  for (PushNotificationClientId clientId : clientIds) {
    switch (clientId) {
      case PushNotificationClientId::kCommerce:
        base::RecordAction(base::UserMetricsAction(
            kNotificationsOptInPromptPriceTrackingEnabled));
        break;
      case PushNotificationClientId::kTips:
        base::RecordAction(
            base::UserMetricsAction(kNotificationsOptInPromptTipsEnabled));
        break;
      case PushNotificationClientId::kContent:
        base::RecordAction(
            base::UserMetricsAction(kNotificationsOptInPromptContentEnabled));
        break;
      case PushNotificationClientId::kSports:
        // Content and sports are enabled together.
        break;
      case PushNotificationClientId::kSafetyCheck:
        base::RecordAction(base::UserMetricsAction(
            kNotificationsOptInPromptSafetyCheckEnabled));
        break;
    }
  }
}

@end