chromium/ios/chrome/browser/overlays/ui_bundled/infobar_modal/infobar_modal_overlay_coordinator.mm

// Copyright 2020 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/overlays/ui_bundled/infobar_modal/infobar_modal_overlay_coordinator.h"
#import "ios/chrome/browser/overlays/ui_bundled/infobar_modal/infobar_modal_overlay_coordinator+modal_configuration.h"

#import "base/apple/foundation_util.h"
#import "base/notreached.h"
#import "ios/chrome/browser/ui/infobars/presentation/infobar_modal_positioner.h"
#import "ios/chrome/browser/ui/infobars/presentation/infobar_modal_transition_driver.h"
#import "ios/chrome/browser/overlays/ui_bundled/infobar_modal/infobar_modal_overlay_mediator.h"
#import "ios/chrome/browser/overlays/ui_bundled/overlay_presentation_util.h"
#import "ios/chrome/browser/overlays/ui_bundled/overlay_request_coordinator+subclassing.h"
#import "ios/chrome/browser/overlays/ui_bundled/overlay_request_coordinator_delegate.h"

@interface InfobarModalOverlayCoordinator () <InfobarModalPositioner>
// The navigation controller used to display the modal view.
@property(nonatomic) UINavigationController* modalNavController;
// The transition delegate used by the coordinator to present the modal UI.
@property(nonatomic, strong)
    InfobarModalTransitionDriver* modalTransitionDriver;
@end

@implementation InfobarModalOverlayCoordinator

#pragma mark - OverlayRequestCoordinator

- (void)startAnimated:(BOOL)animated {
  if (self.started || !self.request)
    return;
  [self configureModal];
  [self configureViewController];
  __weak InfobarModalOverlayCoordinator* weakSelf = self;
  [self.baseViewController presentViewController:self.viewController
                                        animated:animated
                                      completion:^{
                                        [weakSelf finishPresentation];
                                      }];
  self.started = YES;
}

- (void)stopAnimated:(BOOL)animated {
  if (!self.started)
    return;
  // Mark started as NO before calling dismissal callback to prevent dup
  // stopAnimated: executions.
  self.started = NO;
  __weak InfobarModalOverlayCoordinator* weakSelf = self;
  [self.baseViewController dismissViewControllerAnimated:animated
                                              completion:^{
                                                [weakSelf finishDismissal];
                                              }];
}

- (UIViewController*)viewController {
  return self.modalNavController;
}

#pragma mark - InfobarModalPositioner

- (CGFloat)modalHeightForWidth:(CGFloat)width {
  UIView* modalView = self.modalViewController.view;
  CGSize modalContentSize = CGSizeZero;
  if (UIScrollView* scrollView =
          base::apple::ObjCCast<UIScrollView>(modalView)) {
    CGRect layoutFrame = self.baseViewController.view.bounds;
    layoutFrame.size.width = width;
    scrollView.frame = layoutFrame;
    [scrollView setNeedsLayout];
    [scrollView layoutIfNeeded];
    modalContentSize = scrollView.contentSize;
  } else {
    modalContentSize = [modalView sizeThatFits:CGSizeMake(width, CGFLOAT_MAX)];
  }
  return modalContentSize.height +
         CGRectGetHeight(self.modalNavController.navigationBar.bounds);
}

#pragma mark - InfobarModalPresentationHandler

- (void)resizeInfobarModal {
  UIView* containerView = self.modalNavController.view;
  containerView.frame = ContainedModalFrameThatFit(self, containerView);
}

#pragma mark - Private

// Called when the presentation of the modal UI is completed.
- (void)finishPresentation {
  // Notify the presentation context that the presentation has finished.  This
  // is necessary to synchronize OverlayPresenter scheduling logic with the UI
  // layer.
  if (self.delegate) {
    self.delegate->OverlayUIDidFinishPresentation(self.request);
  }
}

// Called when the dismissal of the modal UI is finished.
- (void)finishDismissal {
  [self resetModal];
  self.modalNavController = nil;
  // Notify the presentation context that the dismissal has finished.  This
  // is necessary to synchronize OverlayPresenter scheduling logic with the UI
  // layer.
  if (self.delegate) {
    self.delegate->OverlayUIDidFinishDismissal(self.request);
  }
}

@end

@implementation InfobarModalOverlayCoordinator (ModalConfiguration)

- (OverlayRequestMediator*)modalMediator {
  NOTREACHED_IN_MIGRATION() << "Subclasses implement.";
  return nullptr;
}

- (UIViewController*)modalViewController {
  NOTREACHED_IN_MIGRATION() << "Subclasses implement.";
  return nil;
}

- (void)configureModal {
  NOTREACHED_IN_MIGRATION() << "Subclasses implement.";
}

- (void)configureViewController {
  self.mediator = self.modalMediator;
  self.modalTransitionDriver = [[InfobarModalTransitionDriver alloc]
      initWithTransitionMode:InfobarModalTransitionBase];
  self.modalTransitionDriver.modalPositioner = self;
  self.modalNavController = [[UINavigationController alloc]
      initWithRootViewController:self.modalViewController];
  self.modalNavController.modalPresentationStyle = UIModalPresentationCustom;
  self.modalNavController.transitioningDelegate = self.modalTransitionDriver;
  UINavigationBarAppearance* opaqueAppearance =
      [[UINavigationBarAppearance alloc] init];
  [opaqueAppearance configureWithOpaqueBackground];
  self.modalNavController.navigationBar.standardAppearance = opaqueAppearance;
  self.modalNavController.navigationBar.compactAppearance = opaqueAppearance;
  self.modalNavController.navigationBar.scrollEdgeAppearance = opaqueAppearance;
}

- (void)resetModal {
  NOTREACHED_IN_MIGRATION() << "Subclasses implement.";
}

@end