chromium/ios/chrome/browser/ui/settings/notifications/notifications_coordinator.mm

// Copyright 2022 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/settings/notifications/notifications_coordinator.h"

#import <vector>

#import "base/apple/foundation_util.h"
#import "base/check.h"
#import "base/check_op.h"
#import "base/strings/sys_string_conversions.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/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/ui/table_view/table_view_utils.h"
#import "ios/chrome/browser/signin/model/authentication_service.h"
#import "ios/chrome/browser/signin/model/authentication_service_factory.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
#import "ios/chrome/browser/ui/push_notification/notifications_opt_in_alert_coordinator.h"
#import "ios/chrome/browser/ui/settings/notifications/content_notifications/content_notifications_coordinator.h"
#import "ios/chrome/browser/ui/settings/notifications/notifications_banner_view_controller.h"
#import "ios/chrome/browser/ui/settings/notifications/notifications_mediator.h"
#import "ios/chrome/browser/ui/settings/notifications/notifications_navigation_commands.h"
#import "ios/chrome/browser/ui/settings/notifications/notifications_settings_observer.h"
#import "ios/chrome/browser/ui/settings/notifications/notifications_view_controller.h"
#import "ios/chrome/browser/ui/settings/notifications/tips_notifications_alert_presenter.h"
#import "ios/chrome/browser/ui/settings/notifications/tracking_price/tracking_price_coordinator.h"
#import "ios/chrome/grit/ios_branded_strings.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ui/base/l10n/l10n_util.h"
#import "ui/base/l10n/l10n_util_mac.h"

@interface NotificationsCoordinator () <
    NotificationsNavigationCommands,
    NotificationsViewControllerPresentationDelegate,
    ContentNotificationsCoordinatorDelegate,
    TrackingPriceCoordinatorDelegate,
    NotificationsOptInAlertCoordinatorDelegate,
    NotificationsBannerViewControllerPresentationDelegate>

// View controller presented by coordinator when feature IOSTipsNotifications is
// disabled.
@property(nonatomic, strong) NotificationsViewController* viewController;
// View controller presented by coordinator when feature IOSTipsNotifications is
// enabled.
@property(nonatomic, strong)
    NotificationsBannerViewController* updatedViewController;
// Notifications settings mediator.
@property(nonatomic, strong) NotificationsMediator* mediator;
// Coordinator for Content settings menu.
@property(nonatomic, strong)
    ContentNotificationsCoordinator* contentNotificationsCoordinator;
// Coordinator for Tracking Price settings menu.
@property(nonatomic, strong) TrackingPriceCoordinator* trackingPriceCoordinator;
// An observer that tracks whether push notification permission settings have
// been modified.
@property(nonatomic, strong)
    NotificationsSettingsObserver* notificationsObserver;
// Alert Coordinator used to display the notifications system prompt.
@property(nonatomic, strong)
    NotificationsOptInAlertCoordinator* optInAlertCoordinator;

@end

@implementation NotificationsCoordinator

@synthesize baseNavigationController = _baseNavigationController;

- (instancetype)initWithBaseNavigationController:
                    (UINavigationController*)navigationController
                                         browser:(Browser*)browser {
  self = [super initWithBaseViewController:navigationController
                                   browser:browser];
  if (self) {
    _baseNavigationController = navigationController;
  }
  return self;
}

- (void)start {
  AuthenticationService* authService =
      AuthenticationServiceFactory::GetForBrowserState(
          self.browser->GetBrowserState());
  id<SystemIdentity> identity =
      authService->GetPrimaryIdentity(signin::ConsentLevel::kSignin);
  const std::string& gaiaID = base::SysNSStringToUTF8(identity.gaiaID);
  PrefService* prefService = self.browser->GetBrowserState()->GetPrefs();
  _notificationsObserver = [[NotificationsSettingsObserver alloc]
      initWithPrefService:prefService
               localState:GetApplicationContext()->GetLocalState()];

  self.mediator = [[NotificationsMediator alloc] initWithPrefService:prefService
                                                              gaiaID:gaiaID];
  self.mediator.handler = self;
  self.mediator.presenter = self;
  _notificationsObserver.delegate = self.mediator;

  if (IsIOSTipsNotificationsEnabled()) {
    self.updatedViewController =
        [[NotificationsBannerViewController alloc] init];
    self.updatedViewController.presentationDelegate = self;
    self.updatedViewController.modelDelegate = self.mediator;
    self.updatedViewController.isContentNotificationEnabled =
        IsContentNotificationEnabled(self.browser->GetBrowserState());
    self.mediator.consumer = self.updatedViewController;
    [self.baseNavigationController pushViewController:self.updatedViewController
                                             animated:YES];
  } else {
    self.viewController = [[NotificationsViewController alloc]
        initWithStyle:ChromeTableViewStyle()];
    self.viewController.presentationDelegate = self;
    self.viewController.modelDelegate = self.mediator;
    self.viewController.isContentNotificationEnabled =
        IsContentNotificationEnabled(self.browser->GetBrowserState());
    self.mediator.consumer = self.viewController;
    [self.baseNavigationController pushViewController:self.viewController
                                             animated:YES];
  }
}

- (void)stop {
  _notificationsObserver.delegate = nil;
  [_notificationsObserver disconnect];
  _notificationsObserver = nil;
  [_optInAlertCoordinator stop];
}

#pragma mark - NotificationsAlertPresenter

- (void)presentTipsNotificationPermissionAlert {
  [_optInAlertCoordinator stop];
  UIViewController* baseViewController = IsIOSTipsNotificationsEnabled()
                                             ? self.updatedViewController
                                             : self.viewController;
  _optInAlertCoordinator = [[NotificationsOptInAlertCoordinator alloc]
      initWithBaseViewController:baseViewController
                         browser:self.browser];
  _optInAlertCoordinator.clientIds =
      std::vector{PushNotificationClientId::kTips};
  _optInAlertCoordinator.alertMessage = l10n_util::GetNSString(
      IDS_IOS_TIPS_NOTIFICATIONS_SETTINGS_ALERT_SUBTITLE);
  _optInAlertCoordinator.delegate = self;
  [_optInAlertCoordinator start];
}

#pragma mark - NotificationsNavigationCommands

- (void)showContent {
  DCHECK(!self.contentNotificationsCoordinator);
  DCHECK(self.baseNavigationController);
  self.contentNotificationsCoordinator =
      [[ContentNotificationsCoordinator alloc]
          initWithBaseNavigationController:self.baseNavigationController
                                   browser:self.browser];
  self.contentNotificationsCoordinator.delegate = self;
  [self.contentNotificationsCoordinator start];
}

- (void)showTrackingPrice {
  DCHECK(!self.trackingPriceCoordinator);
  DCHECK(self.baseNavigationController);
  self.trackingPriceCoordinator = [[TrackingPriceCoordinator alloc]
      initWithBaseNavigationController:self.baseNavigationController
                               browser:self.browser];
  self.trackingPriceCoordinator.delegate = self;
  [self.trackingPriceCoordinator start];
}

#pragma mark - NotificationsViewControllerPresentationDelegate

- (void)notificationsViewControllerDidRemove:
    (NotificationsViewController*)controller {
  DCHECK_EQ(self.viewController, controller);
  [self.delegate notificationsCoordinatorDidRemove:self];
}

#pragma mark - NotificationsBannerViewControllerPresentationDelegate

- (void)notificationsBannerViewControllerDidRemove:
    (NotificationsBannerViewController*)controller {
  DCHECK_EQ(self.updatedViewController, controller);
  [self.delegate notificationsCoordinatorDidRemove:self];
}

#pragma mark - ContentNotificationsCoordinatorDelegate

- (void)contentNotificationsCoordinatorDidRemove:
    (ContentNotificationsCoordinator*)coordinator {
  DCHECK_EQ(self.contentNotificationsCoordinator, coordinator);
  [self.contentNotificationsCoordinator stop];
  self.contentNotificationsCoordinator.delegate = nil;
  self.contentNotificationsCoordinator = nil;
}

#pragma mark - TrackingPriceCoordinatorDelegate

- (void)trackingPriceCoordinatorDidRemove:
    (TrackingPriceCoordinator*)coordinator {
  DCHECK_EQ(self.trackingPriceCoordinator, coordinator);
  [self.trackingPriceCoordinator stop];
  self.trackingPriceCoordinator.delegate = nil;
  self.trackingPriceCoordinator = nil;
}

#pragma mark - NotificationsOptInAlertCoordinatorDelegate

- (void)notificationsOptInAlertCoordinator:
            (NotificationsOptInAlertCoordinator*)alertCoordinator
                                    result:
                                        (NotificationsOptInAlertResult)result {
  CHECK_EQ(_optInAlertCoordinator, alertCoordinator);
  std::vector<PushNotificationClientId> clientIds =
      alertCoordinator.clientIds.value();
  [_optInAlertCoordinator stop];
  _optInAlertCoordinator = nil;
  switch (result) {
    case NotificationsOptInAlertResult::kPermissionDenied:
    case NotificationsOptInAlertResult::kCanceled:
    case NotificationsOptInAlertResult::kError:
    case NotificationsOptInAlertResult::kOpenedSettings:
      [_mediator deniedPermissionsForClientIds:std::move(clientIds)];
      break;
    case NotificationsOptInAlertResult::kPermissionGranted:
      break;
  }
}

@end