chromium/ios/chrome/browser/docking_promo/coordinator/docking_promo_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/docking_promo/coordinator/docking_promo_coordinator.h"

#import <UIKit/UIKit.h>

#import <optional>

#import "base/feature_list.h"
#import "components/feature_engagement/public/event_constants.h"
#import "components/feature_engagement/public/tracker.h"
#import "ios/chrome/app/application_delegate/app_state.h"
#import "ios/chrome/browser/docking_promo/coordinator/docking_promo_mediator.h"
#import "ios/chrome/browser/docking_promo/model/utils.h"
#import "ios/chrome/browser/docking_promo/ui/docking_promo_metrics.h"
#import "ios/chrome/browser/docking_promo/ui/docking_promo_view_controller.h"
#import "ios/chrome/browser/feature_engagement/model/tracker_factory.h"
#import "ios/chrome/browser/first_run/ui_bundled/first_run_screen_delegate.h"
#import "ios/chrome/browser/promos_manager/model/promos_manager.h"
#import "ios/chrome/browser/promos_manager/model/promos_manager_factory.h"
#import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/start_surface/ui_bundled/start_surface_util.h"
#import "ios/chrome/browser/ui/promos_manager/promos_manager_ui_handler.h"
#import "ios/chrome/common/ui/confirmation_alert/confirmation_alert_action_handler.h"

@interface DockingPromoCoordinator () <ConfirmationAlertActionHandler,
                                       UIAdaptivePresentationControllerDelegate>

// Main mediator for this coordinator.
@property(nonatomic, strong) DockingPromoMediator* mediator;

// Main view controller for this coordinator.
@property(nonatomic, strong) DockingPromoViewController* viewController;

@end

@implementation DockingPromoCoordinator {
  /// Whether the screen is being shown in the FRE.
  BOOL _firstRun;
  /// First run screen delegate.
  __weak id<FirstRunScreenDelegate> _firstRunDelegate;
}

@synthesize baseNavigationController = _baseNavigationController;

- (instancetype)initWithBaseViewController:(UIViewController*)viewController
                                   browser:(Browser*)browser {
  if ((self = [super initWithBaseViewController:viewController
                                        browser:browser])) {
    _firstRun = NO;
  }
  return self;
}

- (instancetype)initWithBaseNavigationController:
                    (UINavigationController*)navigationController
                                         browser:(Browser*)browser
                                        delegate:(id<FirstRunScreenDelegate>)
                                                     delegate {
  if ([self initWithBaseViewController:navigationController browser:browser]) {
    _baseNavigationController = navigationController;
    _firstRun = YES;
    _firstRunDelegate = delegate;
  }
  return self;
}

- (void)start {
  if (!_firstRun) {
    [self.browser->GetCommandDispatcher()
        startDispatchingToTarget:self
                     forProtocol:@protocol(DockingPromoCommands)];
  }

  PromosManager* promosManager =
      PromosManagerFactory::GetForBrowserState(self.browser->GetBrowserState());

  AppState* appState = self.browser->GetSceneState().appState;

  std::optional<base::TimeDelta> timeSinceLastForeground =
      MinTimeSinceLastForeground(appState.foregroundScenes);

  self.mediator = [[DockingPromoMediator alloc]
        initWithPromosManager:promosManager
      timeSinceLastForeground:timeSinceLastForeground.value_or(
                                  base::TimeDelta::Min())];

  if (_firstRun) {
    self.viewController = [[DockingPromoViewController alloc] init];
    self.mediator.consumer = self.viewController;
    self.mediator.tracker =
        feature_engagement::TrackerFactory::GetForBrowserState(
            self.browser->GetBrowserState());
    self.viewController.actionHandler = self;
    self.viewController.presentationController.delegate = self;
    self.viewController.modalInPresentation = YES;

    [self.mediator configureConsumer];

    BOOL animated = self.baseNavigationController.topViewController != nil;
    [self.baseNavigationController setViewControllers:@[ self.viewController ]
                                             animated:animated];
  }
}

- (void)stop {
  [super stop];

  if (!_firstRun) {
    [self.browser->GetCommandDispatcher()
        stopDispatchingForProtocol:@protocol(DockingPromoCommands)];

    [self.viewController.presentingViewController
        dismissViewControllerAnimated:YES
                           completion:nil];
  }

  self.mediator = nil;
  self.viewController = nil;
  _baseNavigationController = nil;
  _firstRunDelegate = nil;
}

#pragma mark - DockingPromoCommands

- (void)showDockingPromo:(BOOL)forced {
  if ((!forced && ![self.mediator canShowDockingPromo]) ||
      [self.viewController isBeingPresented]) {
    return;
  }

  self.viewController = [[DockingPromoViewController alloc] init];
  self.mediator.consumer = self.viewController;
  self.mediator.tracker =
      feature_engagement::TrackerFactory::GetForBrowserState(
          self.browser->GetBrowserState());
  self.viewController.actionHandler = self;
  self.viewController.presentationController.delegate = self;

  [self.mediator configureConsumer];

  [self.baseViewController presentViewController:self.viewController
                                        animated:YES
                                      completion:nil];
}

#pragma mark - ConfirmationAlertActionHandler

- (void)confirmationAlertPrimaryAction {
  if (_firstRun) {
    [_firstRunDelegate screenWillFinishPresenting];
  } else {
    [self hidePromo];
  }

  [self promoWasDismissed];
  RecordDockingPromoAction(IOSDockingPromoAction::kGotIt);
}

- (void)confirmationAlertSecondaryAction {
  feature_engagement::Tracker* tracker =
      feature_engagement::TrackerFactory::GetForBrowserState(
          self.browser->GetBrowserState());
  tracker->NotifyEvent(feature_engagement::events::kDockingPromoRemindMeLater);

  if (_firstRun) {
    [_firstRunDelegate screenWillFinishPresenting];
  } else {
    [self hidePromo];
  }

  [self.mediator registerPromoWithPromosManager];
  [self promoWasDismissed];
  RecordDockingPromoAction(IOSDockingPromoAction::kRemindMeLater);
}

#pragma mark - UIAdaptivePresentationControllerDelegate

- (void)presentationControllerDidDismiss:
    (UIPresentationController*)presentationController {
  [self promoWasDismissed];
  RecordDockingPromoAction(IOSDockingPromoAction::kDismissViaSwipe);
}

#pragma mark - Private

// Dismisses the feature.
- (void)hidePromo {
  [self.viewController.presentingViewController
      dismissViewControllerAnimated:YES
                         completion:nil];
}

// Does any clean up for when the promo is fully dismissed.
- (void)promoWasDismissed {
  [self.promosUIHandler promoWasDismissed];
}

@end