chromium/ios/chrome/browser/ui/settings/autofill/autofill_settings_profile_edit_table_view_controller.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/ui/settings/autofill/autofill_settings_profile_edit_table_view_controller.h"

#import "base/feature_list.h"
#import "base/strings/sys_string_conversions.h"
#import "components/autofill/ios/common/features.h"
#import "ios/chrome/browser/autofill/ui_bundled/autofill_profile_edit_table_view_constants.h"
#import "ios/chrome/browser/shared/public/commands/snackbar_commands.h"
#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_item.h"
#import "ios/chrome/browser/shared/ui/table_view/table_view_utils.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/browser/ui/settings/autofill/autofill_settings_constants.h"
#import "ios/chrome/browser/ui/settings/autofill/autofill_settings_profile_edit_table_view_controller_delegate.h"
#import "ios/chrome/browser/ui/settings/cells/settings_image_detail_text_item.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ui/base/l10n/l10n_util.h"

namespace {

const CGFloat kSymbolSize = 22;

}  // namespace

@interface AutofillSettingsProfileEditTableViewController ()

// Stores the signed in user email, or the empty string if the user is not
// signed-in.
@property(nonatomic, readonly) NSString* userEmail;

@end

@implementation AutofillSettingsProfileEditTableViewController {
  __weak id<AutofillSettingsProfileEditTableViewControllerDelegate> _delegate;

  // If YES, a section is shown in the view to migrate the profile to account.
  BOOL _showMigrateToAccountSection;

  // If YES, denotes that the view shown is to edit the incomplete profiles so
  // that it can migrated to account.
  BOOL _editIncompleteProfileForAccountView;
}

#pragma mark - Initialization

- (instancetype)initWithDelegate:
                    (id<AutofillSettingsProfileEditTableViewControllerDelegate>)
                        delegate
    shouldShowMigrateToAccountButton:(BOOL)showMigrateToAccount
                           userEmail:(NSString*)userEmail {
  self = [super initWithStyle:ChromeTableViewStyle()];

  if (self) {
    _delegate = delegate;
    _showMigrateToAccountSection = showMigrateToAccount;
    _userEmail = userEmail;
    _editIncompleteProfileForAccountView = NO;
  }

  return self;
}

- (void)viewDidLoad {
  [super viewDidLoad];

  [self setTitle:l10n_util::GetNSString(IDS_IOS_AUTOFILL_EDIT_ADDRESS)];
  self.tableView.allowsSelectionDuringEditing = YES;
  self.tableView.accessibilityIdentifier = kAutofillProfileEditTableViewId;

  [self loadModel];
}

- (void)viewDidDisappear:(BOOL)animated {
  [self.handler viewDidDisappear];
  [super viewDidDisappear:animated];
}

- (void)loadModel {
  [super loadModel];
  [self.handler loadModel];

  TableViewModel* model = self.tableViewModel;
  if (_showMigrateToAccountSection) {
    AutofillProfileDetailsSectionIdentifier section =
        AutofillProfileDetailsSectionIdentifierFields;
    if (base::FeatureList::IsEnabled(
            kAutofillDynamicallyLoadsFieldsForAddressInput)) {
      section = AutofillProfileDetailsSectionIdentifierMigrationButton;
      [model addSectionWithIdentifier:
                 AutofillProfileDetailsSectionIdentifierMigrationButton];
    }
    [model addItem:[self migrateToAccountRecommendationItem]
        toSectionWithIdentifier:section];
    [model addItem:[self migrateToAccountButtonItem]
        toSectionWithIdentifier:section];
  }

  [self.handler loadFooterForSettings];
}

#pragma mark - AutofillEditTableViewController

- (BOOL)isItemAtIndexPathTextEditCell:(NSIndexPath*)cellPath {
  NSInteger itemType = [self.tableViewModel itemTypeForIndexPath:cellPath];
  if (itemType ==
          AutofillProfileDetailsItemTypeMigrateToAccountRecommendation ||
      itemType == AutofillProfileDetailsItemTypeMigrateToAccountButton) {
    return NO;
  }
  return [self.handler isItemAtIndexPathTextEditCell:cellPath];
}

#pragma mark - SettingsRootTableViewController

- (void)editButtonPressed {
  [super editButtonPressed];

  if (!self.tableView.editing) {
    [self.handler updateProfileData];
    if (_editIncompleteProfileForAccountView) {
      [_delegate didTapMigrateToAccountButton];
      [self showPostMigrationToast];
      [self.handler setMoveToAccountFromSettings:NO];
      _editIncompleteProfileForAccountView = NO;
    } else {
      [_delegate didEditAutofillProfileFromSettings];
    }
  }

  [self loadModel];
  [self.handler reconfigureCells];
  if (_showMigrateToAccountSection &&
      base::FeatureList::IsEnabled(
          kAutofillDynamicallyLoadsFieldsForAddressInput)) {
    [self reconfigureCellsForItems:
              [self.tableViewModel
                  itemsInSectionWithIdentifier:
                      AutofillProfileDetailsSectionIdentifierMigrationButton]];
  }
}

#pragma mark - UITableViewDataSource

- (UITableViewCell*)tableView:(UITableView*)tableView
        cellForRowAtIndexPath:(NSIndexPath*)indexPath {
  UITableViewCell* cell = [super tableView:tableView
                     cellForRowAtIndexPath:indexPath];
  NSInteger itemType = [self.tableViewModel itemTypeForIndexPath:indexPath];
  if (itemType ==
          AutofillProfileDetailsItemTypeMigrateToAccountRecommendation ||
      itemType == AutofillProfileDetailsItemTypeMigrateToAccountButton) {
    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    return cell;
  }
  return [self.handler cell:cell
          forRowAtIndexPath:indexPath
           withTextDelegate:self];
}

- (void)tableView:(UITableView*)tableView
    didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
  NSInteger itemType = [self.tableViewModel itemTypeForIndexPath:indexPath];
  if (itemType ==
      AutofillProfileDetailsItemTypeMigrateToAccountRecommendation) {
    [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
    return;
  }
  if (itemType == AutofillProfileDetailsItemTypeMigrateToAccountButton) {
    [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
    if ([_delegate isMinimumAddress]) {
      [_delegate didTapMigrateToAccountButton];
      __weak __typeof(self) weakSelf = self;
      void (^completion)(BOOL) = ^(BOOL) {
        [weakSelf showPostMigrationToast];
      };
      [self removeMigrateButton:completion];
    } else {
      // Show the profile in the edit mode.
      _editIncompleteProfileForAccountView = YES;
      [self removeMigrateButton:nil];
      [self editButtonPressed];
      [self.handler setMoveToAccountFromSettings:YES];
    }
    return;
  }
  [self.handler didSelectRowAtIndexPath:indexPath];
}

- (CGFloat)tableView:(UITableView*)tableView
    heightForHeaderInSection:(NSInteger)section {
  if ([self.handler heightForHeaderShouldBeZeroInSection:section]) {
    return 0;
  }
  return [super tableView:tableView heightForHeaderInSection:section];
}

- (CGFloat)tableView:(UITableView*)tableView
    heightForFooterInSection:(NSInteger)section {
  if ([self.handler heightForFooterShouldBeZeroInSection:section]) {
    return 0;
  }
  return [super tableView:tableView heightForFooterInSection:section];
}

#pragma mark - UITableViewDelegate

- (BOOL)tableView:(UITableView*)tableView
    canEditRowAtIndexPath:(NSIndexPath*)indexPath {
  // If we don't allow the edit of the cell, the selection of the cell isn't
  // forwarded.
  return YES;
}

- (UITableViewCellEditingStyle)tableView:(UITableView*)tableView
           editingStyleForRowAtIndexPath:(NSIndexPath*)indexPath {
  return UITableViewCellEditingStyleNone;
}

- (BOOL)tableView:(UITableView*)tableView
    shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath*)indexPath {
  return NO;
}

#pragma mark - Items

- (SettingsImageDetailTextItem*)migrateToAccountRecommendationItem {
  CHECK(_userEmail.length)
      << "User must be signed-in to migrate an address to the "
         "account;";
  SettingsImageDetailTextItem* item = [[SettingsImageDetailTextItem alloc]
      initWithType:
          AutofillProfileDetailsItemTypeMigrateToAccountRecommendation];
  item.detailText = l10n_util::GetNSStringF(
      IDS_IOS_SETTINGS_AUTOFILL_MIGRATE_ADDRESS_TO_ACCOUNT_BUTTON_DESCRIPTION,
      base::SysNSStringToUTF16(self.userEmail));
  item.image = CustomSymbolWithPointSize(kCloudAndArrowUpSymbol, kSymbolSize);
  item.imageViewTintColor = [UIColor colorNamed:kBlueColor];
  return item;
}

- (TableViewTextItem*)migrateToAccountButtonItem {
  TableViewTextItem* item = [[TableViewTextItem alloc]
      initWithType:AutofillProfileDetailsItemTypeMigrateToAccountButton];
  item.text = l10n_util::GetNSString(
      IDS_IOS_SETTINGS_AUTOFILL_MIGRATE_ADDRESS_TO_ACCOUNT_BUTTON_TITLE);
  item.textColor = self.tableView.editing
                       ? [UIColor colorNamed:kTextSecondaryColor]
                       : [UIColor colorNamed:kBlueColor];
  item.enabled = !self.tableView.editing;
  item.accessibilityIdentifier = kAutofillAddressMigrateToAccountButtonId;
  item.accessibilityTraits |= UIAccessibilityTraitButton;
  return item;
}

#pragma mark - Private

// Removes the migrate button section from the view.
- (void)removeMigrateButton:(void (^)(BOOL finished))onCompletion {
  __weak AutofillSettingsProfileEditTableViewController* weakSelf = self;
  [self
      performBatchTableViewUpdates:^{
        TableViewModel* model = weakSelf.tableViewModel;
        if (base::FeatureList::IsEnabled(
                kAutofillDynamicallyLoadsFieldsForAddressInput)) {
          [self removeSectionWithIdentifier:
                    AutofillProfileDetailsSectionIdentifierMigrationButton
                           withRowAnimation:UITableViewRowAnimationFade];
        } else {
          NSIndexPath* indexPathForMigrateRecommendationItem = [model
              indexPathForItemType:
                  AutofillProfileDetailsItemTypeMigrateToAccountRecommendation
                 sectionIdentifier:
                     AutofillProfileDetailsSectionIdentifierFields];
          NSIndexPath* indexPathForMigrateButton =
              [model indexPathForItemType:
                         AutofillProfileDetailsItemTypeMigrateToAccountButton
                        sectionIdentifier:
                            AutofillProfileDetailsSectionIdentifierFields];

          [model removeItemWithType:
                     AutofillProfileDetailsItemTypeMigrateToAccountRecommendation
              fromSectionWithIdentifier:
                  AutofillProfileDetailsSectionIdentifierFields];
          [model removeItemWithType:
                     AutofillProfileDetailsItemTypeMigrateToAccountButton
              fromSectionWithIdentifier:
                  AutofillProfileDetailsSectionIdentifierFields];

          [weakSelf.tableView
              deleteRowsAtIndexPaths:@[
                indexPathForMigrateRecommendationItem, indexPathForMigrateButton
              ]
                    withRowAnimation:UITableViewRowAnimationAutomatic];
        }
      }
                        completion:onCompletion];
  _showMigrateToAccountSection = NO;
}

// Removes the given section if it exists.
- (void)removeSectionWithIdentifier:(NSInteger)sectionIdentifier
                   withRowAnimation:(UITableViewRowAnimation)animation {
  TableViewModel* model = self.tableViewModel;
  if ([model hasSectionForSectionIdentifier:sectionIdentifier]) {
    NSInteger section = [model sectionForSectionIdentifier:sectionIdentifier];
    [model removeSectionWithIdentifier:sectionIdentifier];
    [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:section]
                  withRowAnimation:animation];
  }
}

- (void)showPostMigrationToast {
  CHECK(self.snackbarCommandsHandler);
  CHECK(_userEmail.length)
      << "User must be signed-in to migrate an address to the "
         "account;";
  NSString* message = l10n_util::GetNSStringF(
      IDS_IOS_SETTINGS_AUTOFILL_MIGRATE_ADDRESS_TO_ACCOUNT_CONFIRMATION_TEXT,
      base::SysNSStringToUTF16(self.userEmail));
  TriggerHapticFeedbackForNotification(UINotificationFeedbackTypeSuccess);
  [self.snackbarCommandsHandler showSnackbarWithMessage:message
                                             buttonText:nil
                                          messageAction:nil
                                       completionAction:nil];
}

@end