chromium/ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_mediator.mm

// Copyright 2023 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/plus_addresses/coordinator/plus_address_bottom_sheet_mediator.h"

#import "base/functional/bind.h"
#import "base/memory/raw_ptr.h"
#import "base/strings/sys_string_conversions.h"
#import "components/plus_addresses/features.h"
#import "components/plus_addresses/metrics/plus_address_metrics.h"
#import "components/plus_addresses/plus_address_service.h"
#import "components/plus_addresses/plus_address_types.h"
#import "components/plus_addresses/settings/plus_address_setting_service.h"
#import "ios/chrome/browser/plus_addresses/ui/plus_address_bottom_sheet_constants.h"
#import "ios/chrome/browser/plus_addresses/ui/plus_address_bottom_sheet_consumer.h"
#import "ios/chrome/browser/url_loading/model/url_loading_browser_agent.h"
#import "ios/chrome/browser/url_loading/model/url_loading_params.h"
#import "url/gurl.h"
#import "url/origin.h"

@implementation PlusAddressBottomSheetMediator {
  // The service implementation that owns the data.
  raw_ptr<plus_addresses::PlusAddressService> _plusAddressService;
  // Manages settings for `PlusAddressService`.
  raw_ptr<plus_addresses::PlusAddressSettingService> _plusAddressSettingService;
  // The origin to which all operations should be scoped.
  url::Origin _mainFrameOrigin;
  // The autofill callback to be run if the process completes via confirmation
  // on the bottom sheet.
  plus_addresses::PlusAddressCallback _autofillCallback;
  // The reserved plus address, which is then eligible for confirmation.
  NSString* _reservedPlusAddress;
  raw_ptr<UrlLoadingBrowserAgent> _urlLoader;
  BOOL _incognito;
}

- (instancetype)
    initWithPlusAddressService:(plus_addresses::PlusAddressService*)service
     plusAddressSettingService:
         (plus_addresses::PlusAddressSettingService*)plusAddressSettingService
                     activeUrl:(GURL)activeUrl
              autofillCallback:(plus_addresses::PlusAddressCallback)callback
                     urlLoader:(UrlLoadingBrowserAgent*)urlLoader
                     incognito:(BOOL)incognito {
  // In order to have reached this point, the service should've been created. If
  // not, fail now, since something bad happened.
  CHECK(service);
  self = [super init];
  if (self) {
    _plusAddressService = service;
    _plusAddressSettingService = plusAddressSettingService;
    _mainFrameOrigin = url::Origin::Create(activeUrl);
    _autofillCallback = std::move(callback);
    _urlLoader = urlLoader;
    _incognito = incognito;
  }
  return self;
}

#pragma mark - PlusAddressBottomSheetDelegate

- (void)reservePlusAddress {
  __weak __typeof(self) weakSelf = self;
  // Create the callback needed by the C++ `_plusAddressService` object,
  // notifying the consumer once the call returns.
  auto callback = base::BindOnce(^(
      const plus_addresses::PlusProfileOrError& maybePlusProfile) {
    if (maybePlusProfile.has_value()) {
      [weakSelf didReservePlusAddress:base::SysUTF8ToNSString(
                                          *maybePlusProfile->plus_address)];
    } else {
      [weakSelf.consumer notifyError:plus_addresses::metrics::
                                         PlusAddressModalCompletionStatus::
                                             kReservePlusAddressError];
    }
  });
  _plusAddressService->ReservePlusAddress(_mainFrameOrigin,
                                          std::move(callback));
}

- (void)confirmPlusAddress {
  __weak __typeof(self) weakSelf = self;
  // Create the callback needed by the C++ `_plusAddressService` object,
  // notifying the consumer once the call returns.
  auto callback = base::BindOnce(
      ^(const plus_addresses::PlusProfileOrError& maybePlusProfile) {
        if (maybePlusProfile.has_value()) {
          [weakSelf runAutofillCallback:base::SysUTF8ToNSString(
                                            *maybePlusProfile->plus_address)];
        } else {
          [weakSelf.consumer notifyError:plus_addresses::metrics::
                                             PlusAddressModalCompletionStatus::
                                                 kConfirmPlusAddressError];
        }
      });
  _plusAddressService->ConfirmPlusAddress(
      _mainFrameOrigin,
      plus_addresses::PlusAddress(
          base::SysNSStringToUTF8(_reservedPlusAddress)),
      std::move(callback));
}

- (NSString*)primaryEmailAddress {
  std::optional<std::string> primaryAddress =
      _plusAddressService->GetPrimaryEmail();
  // TODO(crbug.com/40276862): determine the appropriate behavior in cases
  // without a primary email (or just switch the signature away from optional).
  if (!primaryAddress.has_value()) {
    return @"";
  }
  return base::SysUTF8ToNSString(primaryAddress.value());
}

- (void)openNewTab:(PlusAddressURLType)type {
  UrlLoadParams params = UrlLoadParams::InNewTab([self plusAddressURL:type]);
  params.append_to = OpenPosition::kCurrentTab;
  params.user_initiated = NO;
  params.in_incognito = _incognito;
  _urlLoader->Load(params);
}

- (BOOL)isRefreshEnabled {
  return _plusAddressService->IsRefreshingSupported(_mainFrameOrigin);
}

- (void)didTapRefreshButton {
  __weak __typeof(self) weakSelf = self;
  // Create the callback needed by the C++ `_plusAddressService` object,
  // notifying the consumer once the call returns.
  auto callback = base::BindOnce(
      ^(const plus_addresses::PlusProfileOrError& maybePlusProfile) {
        if (maybePlusProfile.has_value()) {
          [weakSelf didReservePlusAddress:base::SysUTF8ToNSString(
                                              *maybePlusProfile->plus_address)];
        } else {
          [weakSelf.consumer notifyError:plus_addresses::metrics::
                                             PlusAddressModalCompletionStatus::
                                                 kReservePlusAddressError];
        }
      });
  _plusAddressService->RefreshPlusAddress(_mainFrameOrigin,
                                          std::move(callback));
}

- (BOOL)shouldShowNotice {
  return !_plusAddressSettingService->GetHasAcceptedNotice() &&
         base::FeatureList::IsEnabled(
             plus_addresses::features::kPlusAddressUserOnboardingEnabled);
}

#pragma mark - Private

// Runs the autofill callback and notifies the consumer of the successful
// confirmation.
- (void)runAutofillCallback:(NSString*)confirmedPlusAddress {
  std::move(_autofillCallback)
      .Run(base::SysNSStringToUTF8(confirmedPlusAddress));
  if ([self shouldShowNotice]) {
    _plusAddressSettingService->SetHasAcceptedNotice();
  }
  [_consumer didConfirmPlusAddress];
}

// Once a plus address is successfully reserved, store it and notify the
// consumer.
- (void)didReservePlusAddress:(NSString*)reservedPlusAddress {
  _reservedPlusAddress = reservedPlusAddress;
  [_consumer didReservePlusAddress:reservedPlusAddress];
}

- (GURL)plusAddressURL:(PlusAddressURLType)type {
  switch (type) {
    case PlusAddressURLType::kErrorReport:
      return GURL(plus_addresses::features::kPlusAddressErrorReportUrl.Get());
    case PlusAddressURLType::kManagement:
      return GURL(plus_addresses::features::kPlusAddressManagementUrl.Get());
    case PlusAddressURLType::kLearnMore:
      return GURL(plus_addresses::features::kPlusAddressLearnMoreUrl.Get());
  }
}
@end