| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // 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_add_credit_card_view_controller.h" |
| |
| #include "base/feature_list.h" |
| #include "base/mac/foundation_util.h" |
| #include "base/metrics/user_metrics.h" |
| #import "ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h" |
| #import "ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_view_controller_delegate.h" |
| #import "ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.h" |
| #import "ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item_delegate.h" |
| #import "ios/chrome/browser/ui/table_view/cells/table_view_text_item.h" |
| #import "ios/chrome/browser/ui/table_view/chrome_table_view_controller.h" |
| #import "ios/chrome/browser/ui/table_view/table_view_utils.h" |
| #include "ios/chrome/browser/ui/ui_feature_flags.h" |
| #import "ios/chrome/common/ui/colors/semantic_color_names.h" |
| #include "ios/chrome/grit/ios_strings.h" |
| #include "ui/base/l10n/l10n_util_mac.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| NSString* const kAddCreditCardViewID = @"kAddCreditCardViewID"; |
| NSString* const kSettingsAddCreditCardButtonID = |
| @"kSettingsAddCreditCardButtonID"; |
| NSString* const kSettingsAddCreditCardCancelButtonID = |
| @"kSettingsAddCreditCardCancelButtonID"; |
| |
| namespace { |
| |
| typedef NS_ENUM(NSInteger, SectionIdentifier) { |
| SectionIdentifierName = kSectionIdentifierEnumZero, |
| SectionIdentifierCreditCardDetails, |
| SectionIdentifierCameraButton, |
| }; |
| |
| typedef NS_ENUM(NSInteger, ItemType) { |
| ItemTypeName = kItemTypeEnumZero, |
| ItemTypeCardNumber, |
| ItemTypeExpirationMonth, |
| ItemTypeExpirationYear, |
| ItemTypeCardNickname, |
| }; |
| |
| } // namespace |
| |
| @interface AutofillAddCreditCardViewController () < |
| TableViewTextEditItemDelegate> |
| |
| // The AddCreditCardViewControllerDelegate for this ViewController. |
| @property(nonatomic, weak) id<AddCreditCardViewControllerDelegate> delegate; |
| |
| // The card holder name updated with the text in tableview cell. |
| @property(nonatomic, strong) NSString* cardHolderName; |
| |
| // The card number in the UI. |
| @property(nonatomic, strong) NSString* cardNumber; |
| |
| // The expiration month in the UI. |
| @property(nonatomic, strong) NSString* expirationMonth; |
| |
| // The expiration year in the UI. |
| @property(nonatomic, strong) NSString* expirationYear; |
| |
| // The user provided nickname for the credit card. |
| @property(nonatomic, strong) NSString* cardNickname; |
| |
| @end |
| |
| @implementation AutofillAddCreditCardViewController |
| |
| - (instancetype)initWithDelegate: |
| (id<AddCreditCardViewControllerDelegate>)delegate { |
| self = [super initWithStyle:ChromeTableViewStyle()]; |
| |
| if (self) { |
| _delegate = delegate; |
| } |
| |
| return self; |
| } |
| |
| - (void)viewDidLoad { |
| [super viewDidLoad]; |
| |
| self.view.backgroundColor = |
| [UIColor colorNamed:kGroupedPrimaryBackgroundColor]; |
| self.tableView.accessibilityIdentifier = kAddCreditCardViewID; |
| |
| self.navigationItem.title = l10n_util::GetNSString( |
| IDS_IOS_CREDIT_CARD_SETTINGS_ADD_PAYMENT_METHOD_TITLE); |
| |
| // Adds 'Cancel' and 'Add' buttons to Navigation bar. |
| self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] |
| initWithTitle:l10n_util::GetNSString(IDS_IOS_NAVIGATION_BAR_CANCEL_BUTTON) |
| style:UIBarButtonItemStylePlain |
| target:self |
| action:@selector(handleCancelButton:)]; |
| self.navigationItem.leftBarButtonItem.accessibilityIdentifier = |
| kSettingsAddCreditCardCancelButtonID; |
| |
| self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] |
| initWithTitle:l10n_util::GetNSString(IDS_IOS_NAVIGATION_BAR_ADD_BUTTON) |
| style:UIBarButtonItemStyleDone |
| target:self |
| action:@selector(didTapAddButton:)]; |
| self.navigationItem.rightBarButtonItem.enabled = NO; |
| self.navigationItem.rightBarButtonItem.accessibilityIdentifier = |
| kSettingsAddCreditCardButtonID; |
| |
| [self loadModel]; |
| } |
| |
| - (BOOL)tableViewHasUserInput { |
| [self updateCreditCardData]; |
| |
| BOOL hasUserInput = self.cardHolderName.length || self.cardNumber.length || |
| self.expirationMonth.length || |
| self.expirationYear.length || self.cardNickname.length; |
| |
| return hasUserInput; |
| } |
| |
| #pragma mark - ChromeTableViewController |
| |
| - (void)loadModel { |
| [super loadModel]; |
| |
| TableViewModel* model = self.tableViewModel; |
| AutofillEditItem* cardHolderNameItem = [self cardHolderNameItem]; |
| AutofillEditItem* cardNumberItem = [self cardNumberItem]; |
| AutofillEditItem* expirationMonthItem = [self expirationMonthItem]; |
| AutofillEditItem* expirationYearItem = [self expirationYearItem]; |
| |
| [model addSectionWithIdentifier:SectionIdentifierCreditCardDetails]; |
| [model addItem:cardNumberItem |
| toSectionWithIdentifier:SectionIdentifierCreditCardDetails]; |
| [model addItem:expirationMonthItem |
| toSectionWithIdentifier:SectionIdentifierCreditCardDetails]; |
| [model addItem:expirationYearItem |
| toSectionWithIdentifier:SectionIdentifierCreditCardDetails]; |
| [model addItem:cardHolderNameItem |
| toSectionWithIdentifier:SectionIdentifierCreditCardDetails]; |
| [model addItem:[self cardNicknameItem] |
| toSectionWithIdentifier:SectionIdentifierCreditCardDetails]; |
| } |
| |
| #pragma mark - TableViewTextEditItemDelegate |
| |
| - (void)tableViewItemDidBeginEditing: |
| (TableViewTextEditItem*)tableViewTextEditItem { |
| // Sets a field to have valid text when a user begins editing so that the |
| // error icon is not visible while a user edits a field. |
| tableViewTextEditItem.hasValidText = YES; |
| [self reconfigureCellsForItems:@[ tableViewTextEditItem ]]; |
| } |
| |
| - (void)tableViewItemDidChange:(TableViewTextEditItem*)tableViewTextEditItem { |
| // Checks the validity of the form and enables/disables the add button when |
| // the user types a character that makes the form valid/invalid. |
| [self updateCreditCardData]; |
| |
| self.navigationItem.rightBarButtonItem.enabled = |
| [self.delegate addCreditCardViewController:self |
| isValidCreditCardNumber:self.cardNumber |
| expirationMonth:self.expirationMonth |
| expirationYear:self.expirationYear |
| cardNickname:self.cardNickname]; |
| |
| [self reconfigureCellsForItems:@[ tableViewTextEditItem ]]; |
| } |
| |
| - (void)tableViewItemDidEndEditing: |
| (TableViewTextEditItem*)tableViewTextEditItem { |
| // Checks the validity of the field when a user ends editing and updates the |
| // cells to display the error icon if the text is invalid. |
| [self updateCreditCardData]; |
| |
| // Considers a textfield to be valid if it has no data. |
| if (tableViewTextEditItem.textFieldValue.length == 0) { |
| tableViewTextEditItem.hasValidText = YES; |
| [self reconfigureCellsForItems:@[ tableViewTextEditItem ]]; |
| return; |
| } |
| |
| switch (tableViewTextEditItem.type) { |
| case ItemTypeCardNumber: |
| tableViewTextEditItem.hasValidText = |
| [self.delegate addCreditCardViewController:self |
| isValidCreditCardNumber:self.cardNumber]; |
| break; |
| case ItemTypeExpirationMonth: |
| tableViewTextEditItem.hasValidText = |
| [self.delegate addCreditCardViewController:self |
| isValidCreditCardExpirationMonth:self.expirationMonth]; |
| break; |
| case ItemTypeExpirationYear: |
| tableViewTextEditItem.hasValidText = |
| [self.delegate addCreditCardViewController:self |
| isValidCreditCardExpirationYear:self.expirationYear]; |
| break; |
| case ItemTypeCardNickname: |
| tableViewTextEditItem.hasValidText = |
| [self.delegate addCreditCardViewController:self |
| isValidCardNickname:self.cardNickname]; |
| break; |
| default: |
| // For the 'Name on card' textfield. |
| tableViewTextEditItem.hasValidText = YES; |
| } |
| [self reconfigureCellsForItems:@[ tableViewTextEditItem ]]; |
| } |
| |
| #pragma mark - UITableViewDataSource |
| |
| - (UITableViewCell*)tableView:(UITableView*)tableView |
| cellForRowAtIndexPath:(NSIndexPath*)indexPath { |
| UITableViewCell* cell = [super tableView:tableView |
| cellForRowAtIndexPath:indexPath]; |
| |
| // Use |ObjCCast| because |cell| might not be |TableViewTextEditCell|. |
| // Set the delegate and style for only |TableViewTextEditCell| type of cell |
| // not other types. |
| TableViewTextEditCell* editCell = |
| base::mac::ObjCCast<TableViewTextEditCell>(cell); |
| editCell.textField.delegate = self; |
| editCell.selectionStyle = UITableViewCellSelectionStyleNone; |
| |
| return cell; |
| } |
| |
| #pragma mark - Private |
| |
| // Handles Add button to add a new credit card. |
| - (void)didTapAddButton:(id)sender { |
| [self updateCreditCardData]; |
| [self.delegate addCreditCardViewController:self |
| addCreditCardWithHolderName:self.cardHolderName |
| cardNumber:self.cardNumber |
| expirationMonth:self.expirationMonth |
| expirationYear:self.expirationYear |
| cardNickname:self.cardNickname]; |
| } |
| |
| // Updates credit card data properties with the text in TableView cells. |
| - (void)updateCreditCardData { |
| self.cardHolderName = |
| [self readTextFromItemtype:ItemTypeName |
| sectionIdentifier:SectionIdentifierCreditCardDetails]; |
| |
| self.cardNumber = |
| [self readTextFromItemtype:ItemTypeCardNumber |
| sectionIdentifier:SectionIdentifierCreditCardDetails]; |
| |
| self.expirationMonth = |
| [self readTextFromItemtype:ItemTypeExpirationMonth |
| sectionIdentifier:SectionIdentifierCreditCardDetails]; |
| |
| self.expirationYear = |
| [self readTextFromItemtype:ItemTypeExpirationYear |
| sectionIdentifier:SectionIdentifierCreditCardDetails]; |
| |
| self.cardNickname = |
| [self readTextFromItemtype:ItemTypeCardNickname |
| sectionIdentifier:SectionIdentifierCreditCardDetails]; |
| } |
| |
| // Reads and returns the data from the item with passed |itemType| and |
| // |sectionIdentifier|. |
| - (NSString*)readTextFromItemtype:(NSInteger)itemType |
| sectionIdentifier:(NSInteger)sectionIdentifier { |
| NSIndexPath* path = |
| [self.tableViewModel indexPathForItemType:itemType |
| sectionIdentifier:sectionIdentifier]; |
| AutofillEditItem* item = base::mac::ObjCCastStrict<AutofillEditItem>( |
| [self.tableViewModel itemAtIndexPath:path]); |
| NSString* text = item.textFieldValue; |
| return text; |
| } |
| |
| // Updates TableView cell of |itemType| in |sectionIdentifier| textfieldValue |
| // with |text|. |
| - (void)updateCellForItemType:(NSInteger)itemType |
| inSectionIdentifier:(NSInteger)sectionIdentifier |
| withText:(NSString*)text { |
| NSIndexPath* path = |
| [self.tableViewModel indexPathForItemType:itemType |
| sectionIdentifier:sectionIdentifier]; |
| AutofillEditItem* item = base::mac::ObjCCastStrict<AutofillEditItem>( |
| [self.tableViewModel itemAtIndexPath:path]); |
| item.textFieldValue = text; |
| [self reconfigureCellsForItems:@[ item ]]; |
| } |
| |
| // Dimisses this view controller when Cancel button is tapped. |
| - (void)handleCancelButton:(id)sender { |
| [self.delegate addCreditCardViewControllerDidCancel:self]; |
| } |
| |
| // Returns initialized tableViewItem with passed arguments. |
| - (AutofillEditItem*)createTableViewItemWithType:(NSInteger)itemType |
| textFieldName:(NSString*)textFieldName |
| textFieldValue:(NSString*)textFieldValue |
| textFieldPlaceholder:(NSString*)textFieldPlaceholder |
| keyboardType:(UIKeyboardType)keyboardType |
| autofillUIType: |
| (AutofillUIType)autofillUIType { |
| AutofillEditItem* item = [[AutofillEditItem alloc] initWithType:itemType]; |
| item.delegate = self; |
| item.textFieldName = textFieldName; |
| item.textFieldValue = textFieldValue; |
| item.textFieldPlaceholder = textFieldPlaceholder; |
| item.keyboardType = keyboardType; |
| item.hideIcon = NO; |
| item.textFieldEnabled = YES; |
| item.autofillUIType = autofillUIType; |
| return item; |
| } |
| |
| - (AutofillEditItem*)expirationYearItem { |
| AutofillEditItem* expirationYearItem = |
| [self createTableViewItemWithType:ItemTypeExpirationYear |
| textFieldName:l10n_util::GetNSString( |
| IDS_IOS_AUTOFILL_EXP_YEAR) |
| textFieldValue:self.expirationYear |
| textFieldPlaceholder: |
| l10n_util::GetNSString( |
| IDS_IOS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRATION_YEAR) |
| keyboardType:UIKeyboardTypeNumberPad |
| autofillUIType:AutofillUITypeCreditCardExpYear]; |
| return expirationYearItem; |
| } |
| |
| - (AutofillEditItem*)expirationMonthItem { |
| AutofillEditItem* expirationMonthItem = |
| [self createTableViewItemWithType:ItemTypeExpirationMonth |
| textFieldName:l10n_util::GetNSString( |
| IDS_IOS_AUTOFILL_EXP_MONTH) |
| textFieldValue:self.expirationMonth |
| textFieldPlaceholder: |
| l10n_util::GetNSString( |
| IDS_IOS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_MONTH) |
| keyboardType:UIKeyboardTypeNumberPad |
| autofillUIType:AutofillUITypeCreditCardExpMonth]; |
| return expirationMonthItem; |
| } |
| |
| - (AutofillEditItem*)cardNumberItem { |
| AutofillEditItem* cardNumberItem = |
| [self createTableViewItemWithType:ItemTypeCardNumber |
| textFieldName:l10n_util::GetNSString( |
| IDS_IOS_AUTOFILL_CARD_NUMBER) |
| textFieldValue:self.cardNumber |
| textFieldPlaceholder: |
| l10n_util::GetNSString( |
| IDS_IOS_AUTOFILL_DIALOG_PLACEHOLDER_CARD_NUMBER) |
| keyboardType:UIKeyboardTypeNumberPad |
| autofillUIType:AutofillUITypeCreditCardNumber]; |
| return cardNumberItem; |
| } |
| |
| - (AutofillEditItem*)cardHolderNameItem { |
| AutofillEditItem* cardHolderNameItem = |
| [self createTableViewItemWithType:ItemTypeName |
| textFieldName:l10n_util::GetNSString( |
| IDS_IOS_AUTOFILL_CARDHOLDER) |
| textFieldValue:self.cardHolderName |
| textFieldPlaceholder: |
| l10n_util::GetNSString( |
| IDS_IOS_AUTOFILL_DIALOG_PLACEHOLDER_CARD_HOLDER_NAME) |
| keyboardType:UIKeyboardTypeDefault |
| autofillUIType:AutofillUITypeCreditCardHolderFullName]; |
| return cardHolderNameItem; |
| } |
| |
| - (AutofillEditItem*)cardNicknameItem { |
| AutofillEditItem* cardNicknameItem = |
| [self createTableViewItemWithType:ItemTypeCardNickname |
| textFieldName:l10n_util::GetNSString( |
| IDS_IOS_AUTOFILL_NICKNAME) |
| textFieldValue:self.cardNickname |
| textFieldPlaceholder: |
| l10n_util::GetNSString( |
| IDS_IOS_AUTOFILL_DIALOG_PLACEHOLDER_NICKNAME) |
| keyboardType:UIKeyboardTypeDefault |
| autofillUIType:AutofillUITypeUnknown]; |
| return cardNicknameItem; |
| } |
| |
| @end |