Chromium Code Reviews| Index: ios/chrome/browser/ui/payments/payment_request_edit_view_controller.mm |
| diff --git a/ios/chrome/browser/ui/payments/payment_request_edit_view_controller.mm b/ios/chrome/browser/ui/payments/payment_request_edit_view_controller.mm |
| index 5eb5a5199c2bb65d82e88d62ea174728f925f343..56c1b31e58398b9daaa1407a0fe96d81a07aba5f 100644 |
| --- a/ios/chrome/browser/ui/payments/payment_request_edit_view_controller.mm |
| +++ b/ios/chrome/browser/ui/payments/payment_request_edit_view_controller.mm |
| @@ -12,7 +12,9 @@ |
| #import "ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h" |
| #import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrome.h" |
| #import "ios/chrome/browser/ui/collection_view/cells/collection_view_footer_item.h" |
| +#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item+collection_view_controller.h" |
| #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h" |
| +#import "ios/chrome/browser/ui/payments/cells/payments_selector_edit_item.h" |
| #import "ios/chrome/browser/ui/payments/cells/payments_text_item.h" |
| #import "ios/chrome/browser/ui/payments/payment_request_edit_view_controller+internal.h" |
| #import "ios/chrome/browser/ui/payments/payment_request_editor_field.h" |
| @@ -30,8 +32,8 @@ NSString* const kWarningMessageAccessibilityID = |
| namespace { |
| -NSString* const kPaymentRequestEditCollectionViewID = |
| - @"kPaymentRequestEditCollectionViewID"; |
| +NSString* const kPaymentRequestEditCollectionViewAccessibilityID = |
| + @"kPaymentRequestEditCollectionViewAccessibilityID"; |
| const CGFloat kSeparatorEdgeInset = 14; |
| @@ -51,21 +53,23 @@ AutofillEditCell* AutofillEditCellForTextField(UITextField* textField) { |
| } |
| typedef NS_ENUM(NSInteger, SectionIdentifier) { |
| - SectionIdentifierFooter = kSectionIdentifierEnumZero, |
| - SectionIdentifierFirstTextField, |
| + SectionIdentifierHeader = kSectionIdentifierEnumZero, |
| + SectionIdentifierFooter, |
| + SectionIdentifierFirstField, // Must be the last section identifier. |
| }; |
| typedef NS_ENUM(NSInteger, ItemType) { |
| - ItemTypeFooter = kItemTypeEnumZero, |
| - ItemTypeTextField, // This is a repeated item type. |
| - ItemTypeErrorMessage, // This is a repeated item type. |
| + ItemTypeHeader = kItemTypeEnumZero, |
| + ItemTypeFooter, |
| + ItemTypeTextField, // This is a repeated item type. |
| + ItemTypeSelectorField, // This is a repeated item type. |
| + ItemTypeErrorMessage, // This is a repeated item type. |
| }; |
| } // namespace |
| @interface PaymentRequestEditViewController ()< |
| AutofillEditAccessoryDelegate, |
| - PaymentRequestEditViewControllerValidator, |
| UITextFieldDelegate> { |
| NSArray<EditorField*>* _fields; |
| @@ -89,19 +93,22 @@ typedef NS_ENUM(NSInteger, ItemType) { |
| // field. |
| - (void)updateAccessoryViewButtonsStates; |
| +// Adds an error message item in the section |sectionIdentifier| if |
| +// |errorMessage| is non-empty. Otherwise removes such an item if one exists. |
| +- (void)addOrRemoveErrorMessage:(NSString*)errorMessage |
| + inSectionWithIdentifier:(NSInteger)sectionIdentifier; |
| + |
| @end |
| @implementation PaymentRequestEditViewController |
| @synthesize dataSource = _dataSource; |
| +@synthesize delegate = _delegate; |
| @synthesize validatorDelegate = _validatorDelegate; |
| - (instancetype)initWithStyle:(CollectionViewControllerStyle)style { |
| self = [super initWithStyle:style]; |
| if (self) { |
| - // Set self as the validator delegate. |
| - _validatorDelegate = self; |
| - |
| _accessoryView = [[AutofillEditAccessoryView alloc] initWithDelegate:self]; |
| } |
| return self; |
| @@ -136,22 +143,48 @@ typedef NS_ENUM(NSInteger, ItemType) { |
| [super loadModel]; |
| CollectionViewModel* model = self.collectionViewModel; |
| - [self loadHeaderItems]; |
| + CollectionViewItem* headerItem = [_dataSource headerItem]; |
| + if (headerItem) { |
| + [headerItem setType:ItemTypeHeader]; |
| + [model addSectionWithIdentifier:SectionIdentifierHeader]; |
| + [model addItem:headerItem toSectionWithIdentifier:SectionIdentifierHeader]; |
| + } |
| // Iterate over the fields and add the respective sections and items. |
| - int sectionIdentifier = static_cast<int>(SectionIdentifierFirstTextField); |
| + int sectionIdentifier = static_cast<int>(SectionIdentifierFirstField); |
| for (EditorField* field in _fields) { |
| [model addSectionWithIdentifier:sectionIdentifier]; |
| - AutofillEditItem* item = |
| - [[AutofillEditItem alloc] initWithType:ItemTypeTextField]; |
| - item.textFieldName = field.label; |
| - item.textFieldEnabled = YES; |
| - item.textFieldValue = field.value; |
| - item.required = field.isRequired; |
| - item.autofillUIType = field.autofillUIType; |
| - [model addItem:item |
| - toSectionWithIdentifier:static_cast<NSInteger>(sectionIdentifier)]; |
| - field.item = item; |
| + switch (field.fieldType) { |
| + case EditorFieldTypeTextField: { |
| + AutofillEditItem* item = |
| + [[AutofillEditItem alloc] initWithType:ItemTypeTextField]; |
| + item.textFieldName = field.label; |
| + item.textFieldEnabled = YES; |
| + item.textFieldValue = field.value; |
| + item.required = field.isRequired; |
| + item.autofillUIType = field.autofillUIType; |
| + [model addItem:item |
| + toSectionWithIdentifier:static_cast<NSInteger>(sectionIdentifier)]; |
| + field.item = item; |
| + break; |
| + } |
| + case EditorFieldTypeSelector: { |
| + PaymentsSelectorEditItem* item = [[PaymentsSelectorEditItem alloc] |
| + initWithType:ItemTypeSelectorField]; |
| + item.name = field.label; |
| + item.value = field.displayValue; |
| + item.required = field.isRequired; |
| + item.autofillUIType = field.autofillUIType; |
| + item.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator; |
| + [model addItem:item |
| + toSectionWithIdentifier:static_cast<NSInteger>(sectionIdentifier)]; |
| + field.item = item; |
| + break; |
| + } |
| + default: |
| + NOTREACHED(); |
| + } |
| + |
| field.sectionIdentifier = static_cast<NSInteger>(sectionIdentifier); |
| ++sectionIdentifier; |
| } |
| @@ -163,7 +196,7 @@ typedef NS_ENUM(NSInteger, ItemType) { |
| [super viewDidLoad]; |
| self.collectionView.accessibilityIdentifier = |
| - kPaymentRequestEditCollectionViewID; |
| + kPaymentRequestEditCollectionViewAccessibilityID; |
| // Customize collection view settings. |
| self.styler.cellStyle = MDCCollectionViewCellStyleCard; |
| @@ -189,11 +222,16 @@ typedef NS_ENUM(NSInteger, ItemType) { |
| AutofillEditItem* item = base::mac::ObjCCastStrict<AutofillEditItem>( |
| [model itemAtIndexPath:indexPath]); |
| + // Create a dummy EditorField for validation only. |
| + EditorField* dummyField = |
|
macourteau
2017/05/11 18:59:40
nit: dummyField -> fieldForValidation or something
Moe
2017/05/11 19:39:38
Done.
|
| + [[EditorField alloc] initWithAutofillUIType:item.autofillUIType |
| + fieldType:EditorFieldTypeTextField |
| + label:nil |
| + value:textField.text |
| + required:item.required]; |
| NSString* errorMessage = |
| [_validatorDelegate paymentRequestEditViewController:self |
| - validateValue:textField.text |
| - autofillUIType:item.autofillUIType |
| - required:item.required]; |
| + validateField:dummyField]; |
| NSInteger sectionIdentifier = |
| [model sectionIdentifierForSection:[indexPath section]]; |
| [self addOrRemoveErrorMessage:errorMessage |
| @@ -278,6 +316,24 @@ typedef NS_ENUM(NSInteger, ItemType) { |
| return cell; |
| } |
| +#pragma mark UICollectionViewDelegate |
| + |
| +- (void)collectionView:(UICollectionView*)collectionView |
| + didSelectItemAtIndexPath:(NSIndexPath*)indexPath { |
| + [super collectionView:collectionView didSelectItemAtIndexPath:indexPath]; |
| + |
| + // Every field has its own section. Find out which field is selected using |
| + // the section of |indexPath|. Adjust the index if a header section is |
| + // present before the editor fields. |
| + NSInteger index = indexPath.section; |
| + if ([self.collectionViewModel |
| + hasSectionForSectionIdentifier:SectionIdentifierHeader]) |
| + index--; |
| + DCHECK(index >= 0 && index < static_cast<NSInteger>(_fields.count)); |
| + [_delegate paymentRequestEditViewController:self |
| + didSelectField:[_fields objectAtIndex:index]]; |
| +} |
| + |
| #pragma mark MDCCollectionViewStylingDelegate |
| - (CGFloat)collectionView:(UICollectionView*)collectionView |
| @@ -285,12 +341,15 @@ typedef NS_ENUM(NSInteger, ItemType) { |
| CollectionViewItem* item = |
| [self.collectionViewModel itemAtIndexPath:indexPath]; |
| switch (item.type) { |
| + case ItemTypeHeader: |
| + case ItemTypeFooter: |
| case ItemTypeTextField: |
| case ItemTypeErrorMessage: |
| - case ItemTypeFooter: |
| return [MDCCollectionViewCell |
| cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds) |
| forItem:item]; |
| + case ItemTypeSelectorField: |
| + return MDCCellDefaultOneLineHeight; |
| default: |
| NOTREACHED(); |
| return MDCCellDefaultOneLineHeight; |
| @@ -301,8 +360,9 @@ typedef NS_ENUM(NSInteger, ItemType) { |
| hidesInkViewAtIndexPath:(NSIndexPath*)indexPath { |
| NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath]; |
| switch (type) { |
| - case ItemTypeErrorMessage: |
| + case ItemTypeHeader: |
| case ItemTypeFooter: |
| + case ItemTypeErrorMessage: |
| return YES; |
| default: |
| return NO; |
| @@ -313,6 +373,8 @@ typedef NS_ENUM(NSInteger, ItemType) { |
| shouldHideItemBackgroundAtIndexPath:(NSIndexPath*)indexPath { |
| NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath]; |
| switch (type) { |
| + case ItemTypeHeader: |
| + return [_dataSource shouldHideBackgroundForHeaderItem]; |
| case ItemTypeFooter: |
| return YES; |
| default: |
| @@ -320,20 +382,6 @@ typedef NS_ENUM(NSInteger, ItemType) { |
| } |
| } |
| -#pragma mark - PaymentRequestEditViewControllerValidator |
| - |
| -- (NSString*)paymentRequestEditViewController: |
| - (PaymentRequestEditViewController*)controller |
| - validateValue:(NSString*)value |
| - autofillUIType:(AutofillUIType)autofillUIType |
| - required:(BOOL)required { |
| - if (required && !value.length) { |
| - return l10n_util::GetNSString( |
| - IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE); |
| - } |
| - return nil; |
| -} |
| - |
| #pragma mark - Helper methods |
| - (NSIndexPath*)indexPathWithSectionOffset:(NSInteger)offset |
| @@ -354,12 +402,14 @@ typedef NS_ENUM(NSInteger, ItemType) { |
| DCHECK(currentCellPath); |
| NSIndexPath* nextCellPath = |
| [self indexPathWithSectionOffset:offset fromPath:currentCellPath]; |
| - if (nextCellPath) { |
| + while (nextCellPath) { |
|
macourteau
2017/05/11 18:59:40
optional nit: this could be made into a for loop (
Moe
2017/05/11 19:39:38
Acknowledged. The for loop syntax isn't the most i
macourteau
2017/05/11 19:58:23
Acknowledged.
|
| id nextCell = [collectionView cellForItemAtIndexPath:nextCellPath]; |
| if ([nextCell isKindOfClass:[AutofillEditCell class]]) { |
| return base::mac::ObjCCastStrict<AutofillEditCell>( |
| [collectionView cellForItemAtIndexPath:nextCellPath]); |
| } |
| + nextCellPath = |
| + [self indexPathWithSectionOffset:offset fromPath:nextCellPath]; |
| } |
| return nil; |
| } |
| @@ -372,6 +422,39 @@ typedef NS_ENUM(NSInteger, ItemType) { |
| [[_accessoryView nextButton] setEnabled:nextCell != nil]; |
| } |
| +- (void)addOrRemoveErrorMessage:(NSString*)errorMessage |
| + inSectionWithIdentifier:(NSInteger)sectionIdentifier { |
| + CollectionViewModel* model = self.collectionViewModel; |
| + if ([model hasItemForItemType:ItemTypeErrorMessage |
| + sectionIdentifier:sectionIdentifier]) { |
| + NSIndexPath* indexPath = [model indexPathForItemType:ItemTypeErrorMessage |
| + sectionIdentifier:sectionIdentifier]; |
| + if (!errorMessage.length) { |
| + // Remove the item at the index path. |
| + [model removeItemWithType:ItemTypeErrorMessage |
| + fromSectionWithIdentifier:sectionIdentifier]; |
| + [self.collectionView deleteItemsAtIndexPaths:@[ indexPath ]]; |
| + } else { |
| + // Reload the item at the index path. |
| + PaymentsTextItem* item = base::mac::ObjCCastStrict<PaymentsTextItem>( |
| + [model itemAtIndexPath:indexPath]); |
| + item.text = errorMessage; |
| + [self.collectionView reloadItemsAtIndexPaths:@[ indexPath ]]; |
| + } |
| + } else if (errorMessage.length) { |
| + // Insert an item at the index path. |
| + PaymentsTextItem* errorMessageItem = |
| + [[PaymentsTextItem alloc] initWithType:ItemTypeErrorMessage]; |
| + errorMessageItem.text = errorMessage; |
| + errorMessageItem.image = NativeImage(IDR_IOS_PAYMENTS_WARNING); |
| + errorMessageItem.accessibilityIdentifier = kWarningMessageAccessibilityID; |
| + [model addItem:errorMessageItem toSectionWithIdentifier:sectionIdentifier]; |
| + NSIndexPath* indexPath = [model indexPathForItemType:ItemTypeErrorMessage |
| + sectionIdentifier:sectionIdentifier]; |
| + [self.collectionView insertItemsAtIndexPaths:@[ indexPath ]]; |
| + } |
| +} |
| + |
| #pragma mark - Keyboard handling |
| - (void)keyboardDidShow { |
| @@ -388,26 +471,33 @@ typedef NS_ENUM(NSInteger, ItemType) { |
| - (BOOL)validateForm { |
| for (EditorField* field in _fields) { |
| - AutofillEditItem* item = field.item; |
| + switch (field.fieldType) { |
| + case EditorFieldTypeTextField: { |
| + AutofillEditItem* item = |
| + base::mac::ObjCCastStrict<AutofillEditItem>(field.item); |
| + // Update the EditorField with the value of the text field. |
| + field.value = item.textFieldValue; |
| + break; |
| + } |
| + case EditorFieldTypeSelector: { |
| + // No need to update the EditorField. It should already be up-to-date. |
| + break; |
| + } |
| + default: |
| + NOTREACHED(); |
| + } |
| - NSString* errorMessage = [_validatorDelegate |
| - paymentRequestEditViewController:self |
| - validateValue:item.textFieldValue |
| - autofillUIType:field.autofillUIType |
| - required:field.isRequired]; |
| + NSString* errorMessage = |
| + [_validatorDelegate paymentRequestEditViewController:self |
| + validateField:field]; |
| [self addOrRemoveErrorMessage:errorMessage |
| inSectionWithIdentifier:field.sectionIdentifier]; |
| if (errorMessage.length) |
| return NO; |
| - |
| - field.value = item.textFieldValue; |
| } |
| return YES; |
| } |
| -- (void)loadHeaderItems { |
| -} |
| - |
| - (void)loadFooterItems { |
| CollectionViewModel* model = self.collectionViewModel; |
| @@ -426,37 +516,4 @@ typedef NS_ENUM(NSInteger, ItemType) { |
| return indexPath; |
| } |
| -- (void)addOrRemoveErrorMessage:(NSString*)errorMessage |
| - inSectionWithIdentifier:(NSInteger)sectionIdentifier { |
| - CollectionViewModel* model = self.collectionViewModel; |
| - if ([model hasItemForItemType:ItemTypeErrorMessage |
| - sectionIdentifier:sectionIdentifier]) { |
| - NSIndexPath* indexPath = [model indexPathForItemType:ItemTypeErrorMessage |
| - sectionIdentifier:sectionIdentifier]; |
| - if (!errorMessage.length) { |
| - // Remove the item at the index path. |
| - [model removeItemWithType:ItemTypeErrorMessage |
| - fromSectionWithIdentifier:sectionIdentifier]; |
| - [self.collectionView deleteItemsAtIndexPaths:@[ indexPath ]]; |
| - } else { |
| - // Reload the item at the index path. |
| - PaymentsTextItem* item = base::mac::ObjCCastStrict<PaymentsTextItem>( |
| - [model itemAtIndexPath:indexPath]); |
| - item.text = errorMessage; |
| - [self.collectionView reloadItemsAtIndexPaths:@[ indexPath ]]; |
| - } |
| - } else if (errorMessage.length) { |
| - // Insert an item at the index path. |
| - PaymentsTextItem* errorMessageItem = |
| - [[PaymentsTextItem alloc] initWithType:ItemTypeErrorMessage]; |
| - errorMessageItem.text = errorMessage; |
| - errorMessageItem.image = NativeImage(IDR_IOS_PAYMENTS_WARNING); |
| - errorMessageItem.accessibilityIdentifier = kWarningMessageAccessibilityID; |
| - [model addItem:errorMessageItem toSectionWithIdentifier:sectionIdentifier]; |
| - NSIndexPath* indexPath = [model indexPathForItemType:ItemTypeErrorMessage |
| - sectionIdentifier:sectionIdentifier]; |
| - [self.collectionView insertItemsAtIndexPaths:@[ indexPath ]]; |
| - } |
| -} |
| - |
| @end |