[go: nahoru, domu]

blob: 79fcf04667c858391a3c3ffad990abf39c89edbd [file] [log] [blame]
// Copyright 2015 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/autofill/autofill_profile_edit_table_view_controller.h"
#import "base/apple/foundation_util.h"
#import "base/strings/sys_string_conversions.h"
#import "components/autofill/core/browser/data_model/autofill_profile.h"
#import "components/autofill/core/browser/field_types.h"
#import "components/autofill/core/common/autofill_features.h"
#import "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_attributed_string_header_footer_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_link_header_footer_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_multi_detail_text_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_button_item.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_model.h"
#import "ios/chrome/browser/shared/ui/table_view/table_view_utils.h"
#import "ios/chrome/browser/ui/autofill/autofill_profile_edit_table_view_constants.h"
#import "ios/chrome/browser/ui/autofill/autofill_profile_edit_table_view_controller_delegate.h"
#import "ios/chrome/browser/ui/autofill/autofill_ui_type.h"
#import "ios/chrome/browser/ui/autofill/autofill_ui_type_util.h"
#import "ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h"
#import "ios/chrome/browser/ui/autofill/cells/country_item.h"
#import "ios/chrome/browser/ui/settings/autofill/autofill_constants.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 {
using ::AutofillTypeFromAutofillUIType;
using ::AutofillUITypeFromAutofillType;
// A constant to separate the error and the footer text.
const CGFloat kLineSpacingBetweenErrorAndFooter = 12.0f;
} // namespace
@interface AutofillProfileEditTableViewController ()
// The AutofillProfileEditTableViewControllerDelegate for this ViewController.
@property(nonatomic, weak) id<AutofillProfileEditTableViewControllerDelegate>
delegate;
// Stores the value displayed in the fields.
@property(nonatomic, strong) NSString* honorificPrefix;
@property(nonatomic, strong) NSString* companyName;
@property(nonatomic, strong) NSString* fullName;
@property(nonatomic, strong) NSString* homeAddressLine1;
@property(nonatomic, strong) NSString* homeAddressLine2;
@property(nonatomic, strong) NSString* homeAddressCity;
@property(nonatomic, strong) NSString* homeAddressState;
@property(nonatomic, strong) NSString* homeAddressZip;
@property(nonatomic, strong) NSString* homeAddressCountry;
@property(nonatomic, strong) NSString* homePhoneWholeNumber;
@property(nonatomic, strong) NSString* emailAddress;
// Stores the required field names whose values are empty;
@property(nonatomic, strong)
NSMutableSet<NSString*>* requiredFieldsWithEmptyValue;
// Yes, if the error section has been presented.
@property(nonatomic, assign) BOOL errorSectionPresented;
// If YES, denote that the particular field requires a value.
@property(nonatomic, assign) BOOL nameRequired;
@property(nonatomic, assign) BOOL line1Required;
@property(nonatomic, assign) BOOL cityRequired;
@property(nonatomic, assign) BOOL stateRequired;
@property(nonatomic, assign) BOOL zipRequired;
// YES, if the profile's source is autofill::AutofillProfile::Source::kAccount.
@property(nonatomic, assign) BOOL accountProfile;
// The shown view controller.
@property(nonatomic, weak) ChromeTableViewController* controller;
// If YES, denotes that the view is shown in the settings.
@property(nonatomic, assign) BOOL settingsView;
// Points to the save/update button in the modal view.
@property(nonatomic, strong) TableViewTextButtonItem* modalSaveUpdateButton;
// If YES, denotes that the view is laid out for the migration prompt.
@property(nonatomic, assign) BOOL migrationPrompt;
// Denotes that the views are laid out to migrate an incomplete profile to
// account from the settings.
@property(nonatomic, assign) BOOL moveToAccountFromSettings;
@end
@implementation AutofillProfileEditTableViewController {
NSString* _userEmail;
}
@synthesize moveToAccountFromSettings = _moveToAccountFromSettings;
#pragma mark - Initialization
- (instancetype)initWithDelegate:
(id<AutofillProfileEditTableViewControllerDelegate>)delegate
userEmail:(NSString*)userEmail
controller:(ChromeTableViewController*)controller
settingsView:(BOOL)settingsView {
self = [super init];
if (self) {
_delegate = delegate;
_userEmail = userEmail;
_errorSectionPresented = NO;
_accountProfile = NO;
_requiredFieldsWithEmptyValue = [[NSMutableSet<NSString*> alloc] init];
_controller = controller;
_settingsView = settingsView;
_moveToAccountFromSettings = NO;
}
return self;
}
#pragma mark - AutofillProfileEditHandler
- (void)viewDidDisappear {
[self.delegate viewDidDisappear];
}
- (void)updateProfileData {
TableViewModel* model = self.controller.tableViewModel;
NSInteger itemCount =
[model numberOfItemsInSection:
[model sectionForSectionIdentifier:
AutofillProfileDetailsSectionIdentifierFields]];
// Reads the values from the fields and updates the local copy of the
// profile accordingly.
NSInteger section = [model sectionForSectionIdentifier:
AutofillProfileDetailsSectionIdentifierFields];
for (NSInteger itemIndex = 0; itemIndex < itemCount; ++itemIndex) {
NSIndexPath* path = [NSIndexPath indexPathForItem:itemIndex
inSection:section];
NSInteger itemType =
[self.controller.tableViewModel itemTypeForIndexPath:path];
if (itemType == AutofillProfileDetailsItemTypeCountry) {
[self.delegate
updateProfileMetadataWithValue:self.homeAddressCountry
forAutofillUIType:
AutofillUITypeProfileHomeAddressCountry];
continue;
} else if (![self isItemTypeTextEditCell:itemType]) {
continue;
}
AutofillEditItem* item = base::apple::ObjCCastStrict<AutofillEditItem>(
[model itemAtIndexPath:path]);
[self.delegate updateProfileMetadataWithValue:item.textFieldValue
forAutofillUIType:item.autofillUIType];
}
}
- (void)reconfigureCells {
[self.controller reconfigureCellsForItems:
[self.controller.tableViewModel
itemsInSectionWithIdentifier:
AutofillProfileDetailsSectionIdentifierFields]];
}
- (void)loadModel {
TableViewModel* model = self.controller.tableViewModel;
[model
addSectionWithIdentifier:AutofillProfileDetailsSectionIdentifierFields];
for (size_t i = 0; i < std::size(kProfileFieldsToDisplay); ++i) {
const AutofillProfileFieldDisplayInfo& field = kProfileFieldsToDisplay[i];
if (field.autofillType == autofill::NAME_HONORIFIC_PREFIX &&
!base::FeatureList::IsEnabled(
autofill::features::kAutofillEnableSupportForHonorificPrefixes)) {
continue;
}
if (AutofillUITypeFromAutofillType(field.autofillType) ==
AutofillUITypeProfileHomeAddressCountry) {
[model addItem:[self countryItem]
toSectionWithIdentifier:
AutofillProfileDetailsSectionIdentifierFields];
} else {
[model addItem:[self autofillEditItemFromField:field]
toSectionWithIdentifier:
AutofillProfileDetailsSectionIdentifierFields];
}
}
}
- (UITableViewCell*)cell:(UITableViewCell*)cell
forRowAtIndexPath:(NSIndexPath*)indexPath
withTextDelegate:(id<UITextFieldDelegate>)delegate {
cell.selectionStyle = UITableViewCellSelectionStyleNone;
NSInteger itemType =
[self.controller.tableViewModel itemTypeForIndexPath:indexPath];
if (itemType == AutofillProfileDetailsItemTypeFooter ||
itemType == AutofillProfileDetailsItemTypeError) {
if (!self.settingsView) {
cell.separatorInset = UIEdgeInsetsMake(
0, self.controller.tableView.bounds.size.width, 0, 0);
}
return cell;
}
if (itemType == AutofillProfileDetailsItemTypeSaveButton) {
TableViewTextButtonCell* tableViewTextButtonCell =
base::apple::ObjCCastStrict<TableViewTextButtonCell>(cell);
[tableViewTextButtonCell.button addTarget:self
action:@selector(didTapSaveButton)
forControlEvents:UIControlEventTouchUpInside];
return tableViewTextButtonCell;
}
if (itemType == AutofillProfileDetailsItemTypeCountry) {
TableViewMultiDetailTextCell* multiDetailTextCell =
base::apple::ObjCCastStrict<TableViewMultiDetailTextCell>(cell);
multiDetailTextCell.accessibilityIdentifier =
multiDetailTextCell.textLabel.text;
return multiDetailTextCell;
}
TableViewTextEditCell* textFieldCell =
base::apple::ObjCCastStrict<TableViewTextEditCell>(cell);
textFieldCell.accessibilityIdentifier = textFieldCell.textLabel.text;
textFieldCell.textField.delegate = delegate;
return textFieldCell;
}
- (void)didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
NSInteger itemType =
[self.controller.tableViewModel itemTypeForIndexPath:indexPath];
if ([self showEditView]) {
if (itemType == AutofillProfileDetailsItemTypeCountry) {
[self.delegate willSelectCountryWithCurrentlySelectedCountry:
self.homeAddressCountry];
} else if (itemType != AutofillProfileDetailsItemTypeFooter &&
itemType != AutofillProfileDetailsItemTypeError) {
UITableViewCell* cell =
[self.controller.tableView cellForRowAtIndexPath:indexPath];
TableViewTextEditCell* textFieldCell =
base::apple::ObjCCastStrict<TableViewTextEditCell>(cell);
[textFieldCell.textField becomeFirstResponder];
}
}
}
- (BOOL)heightForHeaderShouldBeZeroInSection:(NSInteger)section {
NSInteger sectionIdentifier =
[self.controller.tableViewModel sectionIdentifierForSectionIndex:section];
return sectionIdentifier == AutofillProfileDetailsSectionIdentifierFooter ||
sectionIdentifier ==
AutofillProfileDetailsSectionIdentifierErrorFooter;
}
- (BOOL)heightForFooterShouldBeZeroInSection:(NSInteger)section {
NSInteger sectionIdentifier =
[self.controller.tableViewModel sectionIdentifierForSectionIndex:section];
return (sectionIdentifier == AutofillProfileDetailsSectionIdentifierFields) ||
(!self.settingsView &&
sectionIdentifier == AutofillProfileDetailsSectionIdentifierFooter);
}
- (void)loadFooterForSettings {
CHECK(self.settingsView);
TableViewModel* model = self.controller.tableViewModel;
if (self.accountProfile && _userEmail != nil) {
[model
addSectionWithIdentifier:AutofillProfileDetailsSectionIdentifierFooter];
[model setFooter:[self footerItem]
forSectionWithIdentifier:AutofillProfileDetailsSectionIdentifierFooter];
}
}
- (void)loadMessageAndButtonForModalIfSaveOrUpdate:(BOOL)update {
CHECK(!self.settingsView);
TableViewModel* model = self.controller.tableViewModel;
if (self.accountProfile || self.migrationPrompt) {
DCHECK([_userEmail length] > 0);
[model addItem:[self footerItemForModalViewIfSaveOrUpdate:update]
toSectionWithIdentifier:AutofillProfileDetailsSectionIdentifierFields];
}
[model addItem:[self saveButtonIfSaveOrUpdate:update]
toSectionWithIdentifier:AutofillProfileDetailsSectionIdentifierFields];
}
- (BOOL)isItemAtIndexPathTextEditCell:(NSIndexPath*)cellPath {
NSInteger itemType =
[self.controller.tableViewModel itemTypeForIndexPath:cellPath];
return [self isItemTypeTextEditCell:itemType];
}
- (void)setMoveToAccountFromSettings:(BOOL)moveToAccountFromSettings {
if (_moveToAccountFromSettings == moveToAccountFromSettings) {
return;
}
_moveToAccountFromSettings = moveToAccountFromSettings;
if (moveToAccountFromSettings) {
[self findRequiredFieldsWithEmptyValues];
}
}
#pragma mark - TableViewTextEditItemDelegate
- (void)tableViewItemDidBeginEditing:(TableViewTextEditItem*)tableViewItem {
[self.controller reconfigureCellsForItems:@[ tableViewItem ]];
}
- (void)tableViewItemDidChange:(TableViewTextEditItem*)tableViewItem {
if ((self.accountProfile || self.migrationPrompt ||
self.moveToAccountFromSettings)) {
[self computeErrorIfRequiredTextField:tableViewItem];
if (self.settingsView) {
[self updateDoneButtonStatus];
} else {
[self updateSaveButtonStatus];
}
}
[self.controller reconfigureCellsForItems:@[ tableViewItem ]];
}
- (void)tableViewItemDidEndEditing:(TableViewTextEditItem*)tableViewItem {
[self.controller reconfigureCellsForItems:@[ tableViewItem ]];
}
#pragma mark - AutofillProfileEditConsumer
- (void)didSelectCountry:(NSString*)country {
self.homeAddressCountry = country;
[self findRequiredFieldsWithEmptyValues];
}
#pragma mark - Actions
- (void)didTapSaveButton {
CHECK(!self.settingsView);
[self updateProfileData];
[self.delegate didSaveProfileFromModal];
}
#pragma mark - Conversion Helper Methods
// Returns `autofill::ServerFieldType` corresponding to the `itemType`.
- (autofill::ServerFieldType)serverFieldTypeCorrespondingToRequiredItemType:
(AutofillProfileDetailsItemType)itemType {
switch (itemType) {
case AutofillProfileDetailsItemTypeFullName:
return autofill::NAME_FULL;
case AutofillProfileDetailsItemTypeLine1:
return autofill::ADDRESS_HOME_LINE1;
case AutofillProfileDetailsItemTypeCity:
return autofill::ADDRESS_HOME_CITY;
case AutofillProfileDetailsItemTypeState:
return autofill::ADDRESS_HOME_STATE;
case AutofillProfileDetailsItemTypeZip:
return autofill::ADDRESS_HOME_ZIP;
case AutofillProfileDetailsItemTypeHonorificPrefix:
case AutofillProfileDetailsItemTypeCompanyName:
case AutofillProfileDetailsItemTypeLine2:
case AutofillProfileDetailsItemTypePhoneNumber:
case AutofillProfileDetailsItemTypeEmailAddress:
case AutofillProfileDetailsItemTypeCountry:
case AutofillProfileDetailsItemTypeError:
case AutofillProfileDetailsItemTypeFooter:
case AutofillProfileDetailsItemTypeSaveButton:
case AutofillProfileDetailsItemTypeMigrateToAccountButton:
case AutofillProfileDetailsItemTypeMigrateToAccountRecommendation:
break;
}
NOTREACHED();
return autofill::UNKNOWN_TYPE;
}
// Returns the label corresponding to the item type for a required field.
- (NSString*)labelCorrespondingToRequiredItemType:
(AutofillProfileDetailsItemType)itemType {
switch (itemType) {
case AutofillProfileDetailsItemTypeFullName:
return l10n_util::GetNSString(IDS_IOS_AUTOFILL_FULLNAME);
case AutofillProfileDetailsItemTypeLine1:
return l10n_util::GetNSString(IDS_IOS_AUTOFILL_ADDRESS1);
case AutofillProfileDetailsItemTypeCity:
return l10n_util::GetNSString(IDS_IOS_AUTOFILL_CITY);
case AutofillProfileDetailsItemTypeState:
return l10n_util::GetNSString(IDS_IOS_AUTOFILL_STATE);
case AutofillProfileDetailsItemTypeZip:
return l10n_util::GetNSString(IDS_IOS_AUTOFILL_ZIP);
case AutofillProfileDetailsItemTypeHonorificPrefix:
case AutofillProfileDetailsItemTypeCompanyName:
case AutofillProfileDetailsItemTypeLine2:
case AutofillProfileDetailsItemTypePhoneNumber:
case AutofillProfileDetailsItemTypeEmailAddress:
case AutofillProfileDetailsItemTypeCountry:
case AutofillProfileDetailsItemTypeError:
case AutofillProfileDetailsItemTypeFooter:
case AutofillProfileDetailsItemTypeSaveButton:
case AutofillProfileDetailsItemTypeMigrateToAccountButton:
case AutofillProfileDetailsItemTypeMigrateToAccountRecommendation:
break;
}
NOTREACHED();
return @"";
}
// Returns the value corresponding to `autofillType`.
- (NSString*)valueForAutofillUIType:(AutofillUIType)autofillUIType {
switch (autofillUIType) {
case AutofillUITypeProfileHonorificPrefix:
return self.honorificPrefix;
case AutofillUITypeProfileCompanyName:
return self.companyName;
case AutofillUITypeProfileFullName:
return self.fullName;
case AutofillUITypeProfileHomeAddressLine1:
return self.homeAddressLine1;
case AutofillUITypeProfileHomeAddressLine2:
return self.homeAddressLine2;
case AutofillUITypeProfileHomeAddressCity:
return self.homeAddressCity;
case AutofillUITypeProfileHomeAddressState:
return self.homeAddressState;
case AutofillUITypeProfileHomeAddressZip:
return self.homeAddressZip;
case AutofillUITypeProfileHomeAddressCountry:
return self.homeAddressCountry;
case AutofillUITypeProfileHomePhoneWholeNumber:
return self.homePhoneWholeNumber;
case AutofillUITypeProfileEmailAddress:
return self.emailAddress;
default:
break;
}
NOTREACHED();
return @"";
}
// Returns the item type corresponding to the `autofillUIType`.
- (AutofillProfileDetailsItemType)itemTypeForAutofillUIType:
(AutofillUIType)autofillUIType {
switch (autofillUIType) {
case AutofillUITypeProfileHonorificPrefix:
return AutofillProfileDetailsItemTypeHonorificPrefix;
case AutofillUITypeProfileCompanyName:
return AutofillProfileDetailsItemTypeCompanyName;
case AutofillUITypeProfileFullName:
return AutofillProfileDetailsItemTypeFullName;
case AutofillUITypeProfileHomeAddressLine1:
return AutofillProfileDetailsItemTypeLine1;
case AutofillUITypeProfileHomeAddressLine2:
return AutofillProfileDetailsItemTypeLine2;
case AutofillUITypeProfileHomeAddressCity:
return AutofillProfileDetailsItemTypeCity;
case AutofillUITypeProfileHomeAddressState:
return AutofillProfileDetailsItemTypeState;
case AutofillUITypeProfileHomeAddressZip:
return AutofillProfileDetailsItemTypeZip;
case AutofillUITypeProfileHomeAddressCountry:
return AutofillProfileDetailsItemTypeCountry;
case AutofillUITypeProfileHomePhoneWholeNumber:
return AutofillProfileDetailsItemTypePhoneNumber;
case AutofillUITypeProfileEmailAddress:
return AutofillProfileDetailsItemTypeEmailAddress;
default:
break;
}
NOTREACHED();
return AutofillProfileDetailsItemTypeError;
}
#pragma mark - Items
// Creates and returns the `TableViewLinkHeaderFooterItem` footer item.
- (TableViewLinkHeaderFooterItem*)footerItem {
CHECK(self.settingsView);
TableViewLinkHeaderFooterItem* item = [[TableViewLinkHeaderFooterItem alloc]
initWithType:AutofillProfileDetailsItemTypeFooter];
item.text = [self footerMessage];
return item;
}
// Returns the error message item as a footer.
- (TableViewAttributedStringHeaderFooterItem*)errorMessageItem {
CHECK(self.settingsView);
TableViewAttributedStringHeaderFooterItem* item =
[[TableViewAttributedStringHeaderFooterItem alloc]
initWithType:AutofillProfileDetailsItemTypeError];
[item setAttributedString:[self errorAndFooterMessage]];
return item;
}
// Returns text fields displaying autofill field info used in save/update
// prompts as well as the settings view.
- (AutofillEditItem*)autofillEditItemFromField:
(const AutofillProfileFieldDisplayInfo&)field {
AutofillUIType autofillUIType =
AutofillUITypeFromAutofillType(field.autofillType);
AutofillEditItem* item = [[AutofillEditItem alloc]
initWithType:[self itemTypeForAutofillUIType:autofillUIType]];
item.fieldNameLabelText = l10n_util::GetNSString(field.displayStringID);
item.textFieldValue = [self valueForAutofillUIType:autofillUIType];
item.autofillUIType = autofillUIType;
item.textFieldEnabled = [self showEditView];
item.hideIcon = ![self showEditView];
item.autoCapitalizationType = field.autoCapitalizationType;
item.returnKeyType =
self.settingsView ? field.returnKeyType : UIReturnKeyDone;
item.keyboardType = field.keyboardType;
item.delegate = self;
item.accessibilityIdentifier = l10n_util::GetNSString(field.displayStringID);
item.useCustomSeparator = !self.settingsView;
return item;
}
// Returns the country field used in the save/update prompts as well as the
// settings view.
- (TableViewMultiDetailTextItem*)countryItem {
TableViewMultiDetailTextItem* item = [[TableViewMultiDetailTextItem alloc]
initWithType:AutofillProfileDetailsItemTypeCountry];
item.text = l10n_util::GetNSString(IDS_IOS_AUTOFILL_COUNTRY);
item.trailingDetailText = self.homeAddressCountry;
item.trailingDetailTextColor = [UIColor colorNamed:kTextPrimaryColor];
item.useCustomSeparator = !self.settingsView;
if (!self.settingsView) {
item.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
} else if (self.controller.tableView.editing) {
item.editingAccessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
return item;
}
// Returns the footer element for the save/update prompts.
- (TableViewTextItem*)footerItemForModalViewIfSaveOrUpdate:(BOOL)update {
CHECK(!self.settingsView);
TableViewTextItem* item = [[TableViewTextItem alloc]
initWithType:AutofillProfileDetailsItemTypeFooter];
item.text = l10n_util::GetNSStringF(
update ? IDS_IOS_SETTINGS_AUTOFILL_ACCOUNT_ADDRESS_FOOTER_TEXT
: IDS_IOS_AUTOFILL_SAVE_ADDRESS_IN_ACCOUNT_FOOTER,
base::SysNSStringToUTF16(_userEmail));
item.textFont = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
item.textColor = [UIColor colorNamed:kTextSecondaryColor];
return item;
}
// Returns the button element for the save/update prompts.
- (TableViewTextButtonItem*)saveButtonIfSaveOrUpdate:(BOOL)update {
CHECK(!self.settingsView);
self.modalSaveUpdateButton = [[TableViewTextButtonItem alloc]
initWithType:AutofillProfileDetailsItemTypeSaveButton];
self.modalSaveUpdateButton.textAlignment = NSTextAlignmentNatural;
if (self.migrationPrompt) {
self.modalSaveUpdateButton.buttonText = l10n_util::GetNSString(
IDS_AUTOFILL_ADDRESS_MIGRATION_TO_ACCOUNT_PROMPT_OK_BUTTON_LABEL);
} else {
self.modalSaveUpdateButton.buttonText = l10n_util::GetNSString(
update ? IDS_AUTOFILL_UPDATE_ADDRESS_PROMPT_OK_BUTTON_LABEL
: IDS_AUTOFILL_SAVE_ADDRESS_PROMPT_OK_BUTTON_LABEL);
}
self.modalSaveUpdateButton.disableButtonIntrinsicWidth = YES;
return self.modalSaveUpdateButton;
}
#pragma mark - Private
// Returns true if the itemType belongs to a required field.
- (BOOL)isItemTypeRequiredField:(AutofillProfileDetailsItemType)itemType {
switch (itemType) {
case AutofillProfileDetailsItemTypeFullName:
return self.nameRequired;
case AutofillProfileDetailsItemTypeLine1:
return self.line1Required;
case AutofillProfileDetailsItemTypeCity:
return self.cityRequired;
case AutofillProfileDetailsItemTypeState:
return self.stateRequired;
case AutofillProfileDetailsItemTypeZip:
return self.zipRequired;
case AutofillProfileDetailsItemTypeHonorificPrefix:
case AutofillProfileDetailsItemTypeCompanyName:
case AutofillProfileDetailsItemTypeLine2:
case AutofillProfileDetailsItemTypePhoneNumber:
case AutofillProfileDetailsItemTypeEmailAddress:
case AutofillProfileDetailsItemTypeCountry:
case AutofillProfileDetailsItemTypeError:
case AutofillProfileDetailsItemTypeFooter:
case AutofillProfileDetailsItemTypeSaveButton:
case AutofillProfileDetailsItemTypeMigrateToAccountButton:
case AutofillProfileDetailsItemTypeMigrateToAccountRecommendation:
break;
}
return NO;
}
// Computes whether the `tableViewItem` is a required field and empty.
- (void)computeErrorIfRequiredTextField:(TableViewTextEditItem*)tableViewItem {
AutofillProfileDetailsItemType itemType =
static_cast<AutofillProfileDetailsItemType>(tableViewItem.type);
if (![self isItemTypeRequiredField:itemType] ||
[self requiredFieldWasEmptyOnProfileLoadForItemType:itemType]) {
// Early return if the text field is not a required field or contained an
// empty value when the profile was loaded.
tableViewItem.hasValidText = YES;
return;
}
NSString* requiredTextFieldLabel =
[self labelCorrespondingToRequiredItemType:itemType];
BOOL isValueEmpty = (tableViewItem.textFieldValue.length == 0);
// If the required text field contains a value now, remove it from
// `self.requiredFieldsWithEmptyValue`.
if ([self.requiredFieldsWithEmptyValue
containsObject:requiredTextFieldLabel] &&
!isValueEmpty) {
[self.requiredFieldsWithEmptyValue removeObject:requiredTextFieldLabel];
tableViewItem.hasValidText = YES;
}
// If the required field is empty, add it to
// `self.requiredFieldsWithEmptyValue`.
if (isValueEmpty) {
[self.requiredFieldsWithEmptyValue addObject:requiredTextFieldLabel];
tableViewItem.hasValidText = NO;
}
}
// Returns YES if the profile contained an empty value for the required
// `itemType`.
- (BOOL)requiredFieldWasEmptyOnProfileLoadForItemType:
(AutofillProfileDetailsItemType)itemType {
DCHECK([self isItemTypeRequiredField:itemType]);
if (self.moveToAccountFromSettings) {
return NO;
}
return [self.delegate
fieldValueEmptyOnProfileLoadForType:
[self serverFieldTypeCorrespondingToRequiredItemType:itemType]];
}
// Removes the given section if it exists.
- (void)removeSectionWithIdentifier:(NSInteger)sectionIdentifier
withRowAnimation:(UITableViewRowAnimation)animation {
CHECK(self.settingsView);
TableViewModel* model = self.controller.tableViewModel;
if ([model hasSectionForSectionIdentifier:sectionIdentifier]) {
NSInteger section = [model sectionForSectionIdentifier:sectionIdentifier];
[model removeSectionWithIdentifier:sectionIdentifier];
[[self.controller tableView]
deleteSections:[NSIndexSet indexSetWithIndex:section]
withRowAnimation:animation];
}
}
// If the error status has changed, displays the footer accordingly.
- (void)changeFooterStatusToRemoveSection:
(AutofillProfileDetailsSectionIdentifier)removeSection
addSection:
(AutofillProfileDetailsSectionIdentifier)
addSection {
CHECK(self.settingsView);
TableViewModel* model = self.controller.tableViewModel;
[self.controller
performBatchTableViewUpdates:^{
[self removeSectionWithIdentifier:removeSection
withRowAnimation:UITableViewRowAnimationTop];
NSUInteger fieldsSectionIndex =
[model sectionForSectionIdentifier:
AutofillProfileDetailsSectionIdentifierFields];
[model insertSectionWithIdentifier:addSection
atIndex:fieldsSectionIndex + 1];
[self.controller.tableView
insertSections:[NSIndexSet
indexSetWithIndex:fieldsSectionIndex + 1]
withRowAnimation:UITableViewRowAnimationTop];
[self.controller.tableViewModel
setFooter:(([self.requiredFieldsWithEmptyValue
count] > 0)
? [self errorMessageItem]
: [self footerItem])
forSectionWithIdentifier:addSection];
}
completion:nil];
}
// Updates the Done button status based on `self.requiredFieldsWithEmptyValue`
// and shows/removes the error footer if required.
- (void)updateDoneButtonStatus {
CHECK(self.settingsView);
BOOL shouldShowError = ([self.requiredFieldsWithEmptyValue count] > 0);
self.controller.navigationItem.rightBarButtonItem.enabled = !shouldShowError;
if (shouldShowError != self.errorSectionPresented) {
AutofillProfileDetailsSectionIdentifier addSection =
shouldShowError ? AutofillProfileDetailsSectionIdentifierErrorFooter
: AutofillProfileDetailsSectionIdentifierFooter;
AutofillProfileDetailsSectionIdentifier removeSection =
shouldShowError ? AutofillProfileDetailsSectionIdentifierFooter
: AutofillProfileDetailsSectionIdentifierErrorFooter;
[self changeFooterStatusToRemoveSection:removeSection
addSection:addSection];
self.errorSectionPresented = shouldShowError;
} else if (shouldShowError && [self shouldChangeErrorMessage]) {
[self
changeFooterStatusToRemoveSection:
AutofillProfileDetailsSectionIdentifierErrorFooter
addSection:
AutofillProfileDetailsSectionIdentifierErrorFooter];
}
}
// Updates the Save/Update button status based on
// `self.requiredFieldsWithEmptyValue`.
- (void)updateSaveButtonStatus {
CHECK(!self.settingsView);
BOOL shouldShowError = ([self.requiredFieldsWithEmptyValue count] > 0);
self.modalSaveUpdateButton.enabled = !shouldShowError;
[self.controller reconfigureCellsForItems:@[ self.modalSaveUpdateButton ]];
}
// Returns YES, if the error message needs to be changed. This happens when
// there are multiple required fields that become empty.
- (BOOL)shouldChangeErrorMessage {
CHECK(self.settingsView);
TableViewHeaderFooterItem* currentFooter = [self.controller.tableViewModel
footerForSectionWithIdentifier:
AutofillProfileDetailsSectionIdentifierErrorFooter];
TableViewAttributedStringHeaderFooterItem* attributedFooterItem =
base::apple::ObjCCastStrict<TableViewAttributedStringHeaderFooterItem>(
currentFooter);
NSAttributedString* newFooter = [self errorAndFooterMessage];
return ![attributedFooterItem.attributedString
isEqualToAttributedString:newFooter];
}
// Returns YES, if the view controller is shown in the edit mode.
- (BOOL)showEditView {
return !self.settingsView || self.controller.tableView.editing;
}
// Returns the footer message.
- (NSString*)footerMessage {
CHECK([_userEmail length] > 0);
return self.moveToAccountFromSettings
? @""
: l10n_util::GetNSStringF(
IDS_IOS_SETTINGS_AUTOFILL_ACCOUNT_ADDRESS_FOOTER_TEXT,
base::SysNSStringToUTF16(_userEmail));
}
// Returns the error message combined with footer.
- (NSAttributedString*)errorAndFooterMessage {
CHECK([self.requiredFieldsWithEmptyValue count] > 0);
CHECK(self.settingsView);
NSString* error = l10n_util::GetPluralNSStringF(
IDS_IOS_SETTINGS_EDIT_AUTOFILL_ADDRESS_REQUIREMENT_ERROR,
(int)[self.requiredFieldsWithEmptyValue count]);
NSString* finalErrorString =
[NSString stringWithFormat:@"%@\n%@", error, [self footerMessage]];
NSMutableParagraphStyle* paragraphStyle =
[[NSMutableParagraphStyle alloc] init];
paragraphStyle.paragraphSpacing = kLineSpacingBetweenErrorAndFooter;
NSMutableAttributedString* attributedText = [[NSMutableAttributedString alloc]
initWithString:finalErrorString
attributes:@{
NSFontAttributeName :
[UIFont preferredFontForTextStyle:UIFontTextStyleFootnote],
NSForegroundColorAttributeName :
[UIColor colorNamed:kTextSecondaryColor],
NSParagraphStyleAttributeName : paragraphStyle
}];
[attributedText addAttribute:NSForegroundColorAttributeName
value:[UIColor colorNamed:kRedColor]
range:NSMakeRange(0, error.length)];
return attributedText;
}
// Returns YES if the `itemType` belongs to a text edit field.
- (BOOL)isItemTypeTextEditCell:(NSInteger)itemType {
switch (static_cast<AutofillProfileDetailsItemType>(itemType)) {
case AutofillProfileDetailsItemTypeHonorificPrefix:
case AutofillProfileDetailsItemTypeCompanyName:
case AutofillProfileDetailsItemTypeFullName:
case AutofillProfileDetailsItemTypeLine1:
case AutofillProfileDetailsItemTypeLine2:
case AutofillProfileDetailsItemTypeCity:
case AutofillProfileDetailsItemTypeState:
case AutofillProfileDetailsItemTypeZip:
case AutofillProfileDetailsItemTypePhoneNumber:
case AutofillProfileDetailsItemTypeEmailAddress:
return YES;
case AutofillProfileDetailsItemTypeCountry:
case AutofillProfileDetailsItemTypeError:
case AutofillProfileDetailsItemTypeFooter:
case AutofillProfileDetailsItemTypeSaveButton:
case AutofillProfileDetailsItemTypeMigrateToAccountButton:
case AutofillProfileDetailsItemTypeMigrateToAccountRecommendation:
break;
}
return NO;
}
// Recomputes the required fields that are empty.
- (void)findRequiredFieldsWithEmptyValues {
[self.requiredFieldsWithEmptyValue removeAllObjects];
for (TableViewItem* item in [self.controller.tableViewModel
itemsInSectionWithIdentifier:
AutofillProfileDetailsSectionIdentifierFields]) {
if (item.type == AutofillProfileDetailsItemTypeCountry) {
TableViewMultiDetailTextItem* multiDetailTextItem =
base::apple::ObjCCastStrict<TableViewMultiDetailTextItem>(item);
multiDetailTextItem.trailingDetailText = self.homeAddressCountry;
} else if ([self isItemTypeTextEditCell:item.type]) {
// No requirement checks for local profiles.
if (self.accountProfile || self.migrationPrompt ||
self.moveToAccountFromSettings) {
TableViewTextEditItem* tableViewTextEditItem =
base::apple::ObjCCastStrict<TableViewTextEditItem>(item);
[self computeErrorIfRequiredTextField:tableViewTextEditItem];
}
}
}
[self reconfigureCells];
if (self.settingsView) {
[self updateDoneButtonStatus];
} else {
[self updateSaveButtonStatus];
}
}
@end