chromium/ios/chrome/browser/autofill/ui_bundled/authentication/card_unmask_authentication_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/autofill/ui_bundled/authentication/card_unmask_authentication_coordinator.h"

#import "ios/chrome/browser/autofill/model/autofill_tab_helper.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.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/autofill/ui_bundled/authentication/card_unmask_authentication_selection_coordinator.h"
#import "ios/chrome/browser/autofill/ui_bundled/authentication/otp_input_dialog_coordinator.h"
#import "ios/chrome/browser/autofill/ui_bundled/card_unmask_prompt_view_bridge.h"
#import "ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.h"
#import "ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client.h"

@interface CardUnmaskAuthenticationCoordinator () <
    UIAdaptivePresentationControllerDelegate>
@end

@implementation CardUnmaskAuthenticationCoordinator {
  // This coordinator will present sub-coordinators in a UINavigationController.
  UINavigationController* _navigationController;

  // This sub-coordinator is used to prompt the user to select a particular
  // authentication method.
  CardUnmaskAuthenticationSelectionCoordinator* _selectionCoordinator;

  // This sub-coordinator is used to prompt the user to type in the OTP value
  // received via text message for the card verification purposes.
  OtpInputDialogCoordinator* _otpInputCoordinator;

  // This view bridge is used to prompt the user to type in the CVC value for
  // the card verification purpose.
  std::unique_ptr<autofill::CardUnmaskPromptViewBridge> _cvcInputViewBridge;

  id<BrowserCoordinatorCommands> _browserCoordinatorCommands;
}

- (instancetype)initWithBaseViewController:(UIViewController*)viewController
                                   browser:(Browser*)browser {
  self = [super initWithBaseViewController:viewController browser:browser];
  if (self) {
    _browserCoordinatorCommands = HandlerForProtocol(
        browser->GetCommandDispatcher(), BrowserCoordinatorCommands);
  }
  return self;
}

- (void)continueWithOtpAuth {
  _otpInputCoordinator = [[OtpInputDialogCoordinator alloc]
      initWithBaseNavigationController:_navigationController
                               browser:self.browser];
  [_otpInputCoordinator start];
}

// TODO(crbug.com/333925306): Create a CVC input coordinator/mediator out of the
// legacy CardUnmaskPromptViewBridge and move this function there.
- (void)continueWithCvcAuth {
  autofill::ChromeAutofillClientIOS* client =
      AutofillTabHelper::FromWebState(
          self.browser->GetWebStateList()->GetActiveWebState())
          ->autofill_client();
  CHECK(client);
  autofill::payments::IOSChromePaymentsAutofillClient* paymentsClient =
      client->GetPaymentsAutofillClient();
  CHECK(paymentsClient);

  id<BrowserCoordinatorCommands> browserCoordinatorCommandsHandler =
      HandlerForProtocol(self.browser->GetCommandDispatcher(),
                         BrowserCoordinatorCommands);

  autofill::CardUnmaskPromptControllerImpl* cvcInputModelController =
      paymentsClient->GetCardUnmaskPromptModel();
  _cvcInputViewBridge = std::make_unique<autofill::CardUnmaskPromptViewBridge>(
      cvcInputModelController, _navigationController,
      client->GetPersonalDataManager(), browserCoordinatorCommandsHandler);

  __weak __typeof__(self) weakSelf = self;
  cvcInputModelController->ShowPrompt(
      base::BindOnce(^autofill::CardUnmaskPromptView*() {
        return [weakSelf cardUnmaskPromptView];
      }));
}

#pragma mark - ChromeCoordinator

- (void)start {
  _navigationController = [[UINavigationController alloc] init];
  _navigationController.modalPresentationStyle = UIModalPresentationPageSheet;
  if (self.shouldStartWithCvcAuth) {
    [self continueWithCvcAuth];
  } else {
    _selectionCoordinator =
        [[CardUnmaskAuthenticationSelectionCoordinator alloc]
            initWithBaseNavigationController:_navigationController
                                     browser:self.browser];
    [_selectionCoordinator start];
  }

  [self.baseViewController presentViewController:_navigationController
                                        animated:YES
                                      completion:nil];
  _navigationController.presentationController.delegate = self;
}

- (void)stop {
  [_navigationController.presentingViewController
      dismissViewControllerAnimated:YES
                         completion:nil];
  [_selectionCoordinator stop];
  _selectionCoordinator = nil;
  [_otpInputCoordinator stop];
  _otpInputCoordinator = nil;
  _cvcInputViewBridge.reset();
}

#pragma mark - UIAdaptivePresentationControllerDelegate

- (void)presentationControllerDidDismiss:
    (UIPresentationController*)presentationController {
  [_browserCoordinatorCommands dismissCardUnmaskAuthentication];
}

#pragma mark - Private

// TODO(crbug.com/334652807): Change this to have the model controller take a
// WeakPtr.
- (autofill::CardUnmaskPromptView*)cardUnmaskPromptView {
  return _cvcInputViewBridge.get();
}

@end