| OLD | NEW |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "ios/chrome/browser/ui/payments/payment_request_edit_view_controller.h" | 5 #import "ios/chrome/browser/ui/payments/payment_request_edit_view_controller.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #import "base/mac/foundation_util.h" | 8 #import "base/mac/foundation_util.h" |
| 9 #include "base/strings/sys_string_conversions.h" | 9 #include "base/strings/sys_string_conversions.h" |
| 10 #include "components/strings/grit/components_strings.h" | 10 #include "components/strings/grit/components_strings.h" |
| 11 #import "ios/chrome/browser/ui/autofill/autofill_edit_accessory_view.h" | 11 #import "ios/chrome/browser/ui/autofill/autofill_edit_accessory_view.h" |
| 12 #import "ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h" | 12 #import "ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h" |
| 13 #import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrom
e.h" | 13 #import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrom
e.h" |
| 14 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_footer_item
.h" | 14 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_footer_item
.h" |
| 15 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_item+collec
tion_view_controller.h" |
| 15 #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h" | 16 #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h" |
| 17 #import "ios/chrome/browser/ui/payments/cells/payments_selector_edit_item.h" |
| 16 #import "ios/chrome/browser/ui/payments/cells/payments_text_item.h" | 18 #import "ios/chrome/browser/ui/payments/cells/payments_text_item.h" |
| 17 #import "ios/chrome/browser/ui/payments/payment_request_edit_view_controller+int
ernal.h" | 19 #import "ios/chrome/browser/ui/payments/payment_request_edit_view_controller+int
ernal.h" |
| 18 #import "ios/chrome/browser/ui/payments/payment_request_editor_field.h" | 20 #import "ios/chrome/browser/ui/payments/payment_request_editor_field.h" |
| 19 #import "ios/chrome/browser/ui/uikit_ui_util.h" | 21 #import "ios/chrome/browser/ui/uikit_ui_util.h" |
| 20 #include "ios/chrome/grit/ios_theme_resources.h" | 22 #include "ios/chrome/grit/ios_theme_resources.h" |
| 21 #import "ios/third_party/material_components_ios/src/components/Typography/src/M
aterialTypography.h" | 23 #import "ios/third_party/material_components_ios/src/components/Typography/src/M
aterialTypography.h" |
| 22 #include "ui/base/l10n/l10n_util.h" | 24 #include "ui/base/l10n/l10n_util.h" |
| 23 | 25 |
| 24 #if !defined(__has_feature) || !__has_feature(objc_arc) | 26 #if !defined(__has_feature) || !__has_feature(objc_arc) |
| 25 #error "This file requires ARC support." | 27 #error "This file requires ARC support." |
| 26 #endif | 28 #endif |
| 27 | 29 |
| 28 NSString* const kWarningMessageAccessibilityID = | 30 NSString* const kWarningMessageAccessibilityID = |
| 29 @"kWarningMessageAccessibilityID"; | 31 @"kWarningMessageAccessibilityID"; |
| 30 | 32 |
| 31 namespace { | 33 namespace { |
| 32 | 34 |
| 33 NSString* const kPaymentRequestEditCollectionViewID = | 35 NSString* const kPaymentRequestEditCollectionViewAccessibilityID = |
| 34 @"kPaymentRequestEditCollectionViewID"; | 36 @"kPaymentRequestEditCollectionViewAccessibilityID"; |
| 35 | 37 |
| 36 const CGFloat kSeparatorEdgeInset = 14; | 38 const CGFloat kSeparatorEdgeInset = 14; |
| 37 | 39 |
| 38 const CGFloat kFooterCellHorizontalPadding = 16; | 40 const CGFloat kFooterCellHorizontalPadding = 16; |
| 39 | 41 |
| 40 // Returns the AutofillEditCell that is the parent view of the |textField|. | 42 // Returns the AutofillEditCell that is the parent view of the |textField|. |
| 41 AutofillEditCell* AutofillEditCellForTextField(UITextField* textField) { | 43 AutofillEditCell* AutofillEditCellForTextField(UITextField* textField) { |
| 42 for (UIView* view = textField; view; view = [view superview]) { | 44 for (UIView* view = textField; view; view = [view superview]) { |
| 43 AutofillEditCell* cell = base::mac::ObjCCast<AutofillEditCell>(view); | 45 AutofillEditCell* cell = base::mac::ObjCCast<AutofillEditCell>(view); |
| 44 if (cell) | 46 if (cell) |
| 45 return cell; | 47 return cell; |
| 46 } | 48 } |
| 47 | 49 |
| 48 // There has to be a cell associated with this text field. | 50 // There has to be a cell associated with this text field. |
| 49 NOTREACHED(); | 51 NOTREACHED(); |
| 50 return nil; | 52 return nil; |
| 51 } | 53 } |
| 52 | 54 |
| 53 typedef NS_ENUM(NSInteger, SectionIdentifier) { | 55 typedef NS_ENUM(NSInteger, SectionIdentifier) { |
| 54 SectionIdentifierFooter = kSectionIdentifierEnumZero, | 56 SectionIdentifierHeader = kSectionIdentifierEnumZero, |
| 55 SectionIdentifierFirstTextField, | 57 SectionIdentifierFooter, |
| 58 SectionIdentifierFirstField, // Must be the last section identifier. |
| 56 }; | 59 }; |
| 57 | 60 |
| 58 typedef NS_ENUM(NSInteger, ItemType) { | 61 typedef NS_ENUM(NSInteger, ItemType) { |
| 59 ItemTypeFooter = kItemTypeEnumZero, | 62 ItemTypeHeader = kItemTypeEnumZero, |
| 60 ItemTypeTextField, // This is a repeated item type. | 63 ItemTypeFooter, |
| 61 ItemTypeErrorMessage, // This is a repeated item type. | 64 ItemTypeTextField, // This is a repeated item type. |
| 65 ItemTypeSelectorField, // This is a repeated item type. |
| 66 ItemTypeErrorMessage, // This is a repeated item type. |
| 62 }; | 67 }; |
| 63 | 68 |
| 64 } // namespace | 69 } // namespace |
| 65 | 70 |
| 66 @interface PaymentRequestEditViewController ()< | 71 @interface PaymentRequestEditViewController ()< |
| 67 AutofillEditAccessoryDelegate, | 72 AutofillEditAccessoryDelegate, |
| 68 PaymentRequestEditViewControllerValidator, | |
| 69 UITextFieldDelegate> { | 73 UITextFieldDelegate> { |
| 70 NSArray<EditorField*>* _fields; | 74 NSArray<EditorField*>* _fields; |
| 71 | 75 |
| 72 // The currently focused cell. May be nil. | 76 // The currently focused cell. May be nil. |
| 73 __weak AutofillEditCell* _currentEditingCell; | 77 __weak AutofillEditCell* _currentEditingCell; |
| 74 | 78 |
| 75 AutofillEditAccessoryView* _accessoryView; | 79 AutofillEditAccessoryView* _accessoryView; |
| 76 } | 80 } |
| 77 | 81 |
| 78 // Returns the indexPath for the same row as that of |indexPath| in a section | 82 // Returns the indexPath for the same row as that of |indexPath| in a section |
| 79 // with the given offset relative to that of |indexPath|. May return nil. | 83 // with the given offset relative to that of |indexPath|. May return nil. |
| 80 - (NSIndexPath*)indexPathWithSectionOffset:(NSInteger)offset | 84 - (NSIndexPath*)indexPathWithSectionOffset:(NSInteger)offset |
| 81 fromPath:(NSIndexPath*)indexPath; | 85 fromPath:(NSIndexPath*)indexPath; |
| 82 | 86 |
| 83 // Returns the text field with the given offset relative to the currently | 87 // Returns the text field with the given offset relative to the currently |
| 84 // focused text field. May return nil. | 88 // focused text field. May return nil. |
| 85 - (AutofillEditCell*)nextTextFieldWithOffset:(NSInteger)offset; | 89 - (AutofillEditCell*)nextTextFieldWithOffset:(NSInteger)offset; |
| 86 | 90 |
| 87 // Enables or disables the accessory view's previous and next buttons depending | 91 // Enables or disables the accessory view's previous and next buttons depending |
| 88 // on whether there is a text field before and after the currently focused text | 92 // on whether there is a text field before and after the currently focused text |
| 89 // field. | 93 // field. |
| 90 - (void)updateAccessoryViewButtonsStates; | 94 - (void)updateAccessoryViewButtonsStates; |
| 91 | 95 |
| 96 // Adds an error message item in the section |sectionIdentifier| if |
| 97 // |errorMessage| is non-empty. Otherwise removes such an item if one exists. |
| 98 - (void)addOrRemoveErrorMessage:(NSString*)errorMessage |
| 99 inSectionWithIdentifier:(NSInteger)sectionIdentifier; |
| 100 |
| 92 @end | 101 @end |
| 93 | 102 |
| 94 @implementation PaymentRequestEditViewController | 103 @implementation PaymentRequestEditViewController |
| 95 | 104 |
| 96 @synthesize dataSource = _dataSource; | 105 @synthesize dataSource = _dataSource; |
| 106 @synthesize delegate = _delegate; |
| 97 @synthesize validatorDelegate = _validatorDelegate; | 107 @synthesize validatorDelegate = _validatorDelegate; |
| 98 | 108 |
| 99 - (instancetype)initWithStyle:(CollectionViewControllerStyle)style { | 109 - (instancetype)initWithStyle:(CollectionViewControllerStyle)style { |
| 100 self = [super initWithStyle:style]; | 110 self = [super initWithStyle:style]; |
| 101 if (self) { | 111 if (self) { |
| 102 // Set self as the validator delegate. | |
| 103 _validatorDelegate = self; | |
| 104 | |
| 105 _accessoryView = [[AutofillEditAccessoryView alloc] initWithDelegate:self]; | 112 _accessoryView = [[AutofillEditAccessoryView alloc] initWithDelegate:self]; |
| 106 } | 113 } |
| 107 return self; | 114 return self; |
| 108 } | 115 } |
| 109 | 116 |
| 110 - (void)setDataSource: | 117 - (void)setDataSource: |
| 111 (id<PaymentRequestEditViewControllerDataSource>)dataSource { | 118 (id<PaymentRequestEditViewControllerDataSource>)dataSource { |
| 112 _dataSource = dataSource; | 119 _dataSource = dataSource; |
| 113 _fields = [dataSource editorFields]; | 120 _fields = [dataSource editorFields]; |
| 114 } | 121 } |
| (...skipping 14 matching lines...) Expand all Loading... |
| 129 name:UIKeyboardDidShowNotification | 136 name:UIKeyboardDidShowNotification |
| 130 object:nil]; | 137 object:nil]; |
| 131 } | 138 } |
| 132 | 139 |
| 133 #pragma mark - CollectionViewController methods | 140 #pragma mark - CollectionViewController methods |
| 134 | 141 |
| 135 - (void)loadModel { | 142 - (void)loadModel { |
| 136 [super loadModel]; | 143 [super loadModel]; |
| 137 CollectionViewModel* model = self.collectionViewModel; | 144 CollectionViewModel* model = self.collectionViewModel; |
| 138 | 145 |
| 139 [self loadHeaderItems]; | 146 CollectionViewItem* headerItem = [_dataSource headerItem]; |
| 147 if (headerItem) { |
| 148 [headerItem setType:ItemTypeHeader]; |
| 149 [model addSectionWithIdentifier:SectionIdentifierHeader]; |
| 150 [model addItem:headerItem toSectionWithIdentifier:SectionIdentifierHeader]; |
| 151 } |
| 140 | 152 |
| 141 // Iterate over the fields and add the respective sections and items. | 153 // Iterate over the fields and add the respective sections and items. |
| 142 int sectionIdentifier = static_cast<int>(SectionIdentifierFirstTextField); | 154 int sectionIdentifier = static_cast<int>(SectionIdentifierFirstField); |
| 143 for (EditorField* field in _fields) { | 155 for (EditorField* field in _fields) { |
| 144 [model addSectionWithIdentifier:sectionIdentifier]; | 156 [model addSectionWithIdentifier:sectionIdentifier]; |
| 145 AutofillEditItem* item = | 157 switch (field.fieldType) { |
| 146 [[AutofillEditItem alloc] initWithType:ItemTypeTextField]; | 158 case EditorFieldTypeTextField: { |
| 147 item.textFieldName = field.label; | 159 AutofillEditItem* item = |
| 148 item.textFieldEnabled = YES; | 160 [[AutofillEditItem alloc] initWithType:ItemTypeTextField]; |
| 149 item.textFieldValue = field.value; | 161 item.textFieldName = field.label; |
| 150 item.required = field.isRequired; | 162 item.textFieldEnabled = YES; |
| 151 item.autofillUIType = field.autofillUIType; | 163 item.textFieldValue = field.value; |
| 152 [model addItem:item | 164 item.required = field.isRequired; |
| 153 toSectionWithIdentifier:static_cast<NSInteger>(sectionIdentifier)]; | 165 item.autofillUIType = field.autofillUIType; |
| 154 field.item = item; | 166 [model addItem:item |
| 167 toSectionWithIdentifier:static_cast<NSInteger>(sectionIdentifier)]; |
| 168 field.item = item; |
| 169 break; |
| 170 } |
| 171 case EditorFieldTypeSelector: { |
| 172 PaymentsSelectorEditItem* item = [[PaymentsSelectorEditItem alloc] |
| 173 initWithType:ItemTypeSelectorField]; |
| 174 item.name = field.label; |
| 175 item.value = field.displayValue; |
| 176 item.required = field.isRequired; |
| 177 item.autofillUIType = field.autofillUIType; |
| 178 item.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator; |
| 179 [model addItem:item |
| 180 toSectionWithIdentifier:static_cast<NSInteger>(sectionIdentifier)]; |
| 181 field.item = item; |
| 182 break; |
| 183 } |
| 184 default: |
| 185 NOTREACHED(); |
| 186 } |
| 187 |
| 155 field.sectionIdentifier = static_cast<NSInteger>(sectionIdentifier); | 188 field.sectionIdentifier = static_cast<NSInteger>(sectionIdentifier); |
| 156 ++sectionIdentifier; | 189 ++sectionIdentifier; |
| 157 } | 190 } |
| 158 | 191 |
| 159 [self loadFooterItems]; | 192 [self loadFooterItems]; |
| 160 } | 193 } |
| 161 | 194 |
| 162 - (void)viewDidLoad { | 195 - (void)viewDidLoad { |
| 163 [super viewDidLoad]; | 196 [super viewDidLoad]; |
| 164 | 197 |
| 165 self.collectionView.accessibilityIdentifier = | 198 self.collectionView.accessibilityIdentifier = |
| 166 kPaymentRequestEditCollectionViewID; | 199 kPaymentRequestEditCollectionViewAccessibilityID; |
| 167 | 200 |
| 168 // Customize collection view settings. | 201 // Customize collection view settings. |
| 169 self.styler.cellStyle = MDCCollectionViewCellStyleCard; | 202 self.styler.cellStyle = MDCCollectionViewCellStyleCard; |
| 170 self.styler.separatorInset = | 203 self.styler.separatorInset = |
| 171 UIEdgeInsetsMake(0, kSeparatorEdgeInset, 0, kSeparatorEdgeInset); | 204 UIEdgeInsetsMake(0, kSeparatorEdgeInset, 0, kSeparatorEdgeInset); |
| 172 } | 205 } |
| 173 | 206 |
| 174 #pragma mark - UITextFieldDelegate | 207 #pragma mark - UITextFieldDelegate |
| 175 | 208 |
| 176 - (void)textFieldDidBeginEditing:(UITextField*)textField { | 209 - (void)textFieldDidBeginEditing:(UITextField*)textField { |
| 177 _currentEditingCell = AutofillEditCellForTextField(textField); | 210 _currentEditingCell = AutofillEditCellForTextField(textField); |
| 178 [textField setInputAccessoryView:_accessoryView]; | 211 [textField setInputAccessoryView:_accessoryView]; |
| 179 [self updateAccessoryViewButtonsStates]; | 212 [self updateAccessoryViewButtonsStates]; |
| 180 } | 213 } |
| 181 | 214 |
| 182 - (void)textFieldDidEndEditing:(UITextField*)textField { | 215 - (void)textFieldDidEndEditing:(UITextField*)textField { |
| 183 DCHECK(_currentEditingCell == AutofillEditCellForTextField(textField)); | 216 DCHECK(_currentEditingCell == AutofillEditCellForTextField(textField)); |
| 184 | 217 |
| 185 // Validate the text field. | 218 // Validate the text field. |
| 186 CollectionViewModel* model = self.collectionViewModel; | 219 CollectionViewModel* model = self.collectionViewModel; |
| 187 | 220 |
| 188 NSIndexPath* indexPath = [self indexPathForCurrentTextField]; | 221 NSIndexPath* indexPath = [self indexPathForCurrentTextField]; |
| 189 AutofillEditItem* item = base::mac::ObjCCastStrict<AutofillEditItem>( | 222 AutofillEditItem* item = base::mac::ObjCCastStrict<AutofillEditItem>( |
| 190 [model itemAtIndexPath:indexPath]); | 223 [model itemAtIndexPath:indexPath]); |
| 191 | 224 |
| 225 // Create a dummy EditorField for validation only. |
| 226 EditorField* fieldForValidation = |
| 227 [[EditorField alloc] initWithAutofillUIType:item.autofillUIType |
| 228 fieldType:EditorFieldTypeTextField |
| 229 label:nil |
| 230 value:textField.text |
| 231 required:item.required]; |
| 192 NSString* errorMessage = | 232 NSString* errorMessage = |
| 193 [_validatorDelegate paymentRequestEditViewController:self | 233 [_validatorDelegate paymentRequestEditViewController:self |
| 194 validateValue:textField.text | 234 validateField:fieldForValidation]; |
| 195 autofillUIType:item.autofillUIType | |
| 196 required:item.required]; | |
| 197 NSInteger sectionIdentifier = | 235 NSInteger sectionIdentifier = |
| 198 [model sectionIdentifierForSection:[indexPath section]]; | 236 [model sectionIdentifierForSection:[indexPath section]]; |
| 199 [self addOrRemoveErrorMessage:errorMessage | 237 [self addOrRemoveErrorMessage:errorMessage |
| 200 inSectionWithIdentifier:sectionIdentifier]; | 238 inSectionWithIdentifier:sectionIdentifier]; |
| 201 | 239 |
| 202 [textField setInputAccessoryView:nil]; | 240 [textField setInputAccessoryView:nil]; |
| 203 _currentEditingCell = nil; | 241 _currentEditingCell = nil; |
| 204 } | 242 } |
| 205 | 243 |
| 206 - (BOOL)textFieldShouldReturn:(UITextField*)textField { | 244 - (BOOL)textFieldShouldReturn:(UITextField*)textField { |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 271 footerCell.horizontalPadding = kFooterCellHorizontalPadding; | 309 footerCell.horizontalPadding = kFooterCellHorizontalPadding; |
| 272 break; | 310 break; |
| 273 } | 311 } |
| 274 default: | 312 default: |
| 275 break; | 313 break; |
| 276 } | 314 } |
| 277 | 315 |
| 278 return cell; | 316 return cell; |
| 279 } | 317 } |
| 280 | 318 |
| 319 #pragma mark UICollectionViewDelegate |
| 320 |
| 321 - (void)collectionView:(UICollectionView*)collectionView |
| 322 didSelectItemAtIndexPath:(NSIndexPath*)indexPath { |
| 323 [super collectionView:collectionView didSelectItemAtIndexPath:indexPath]; |
| 324 |
| 325 // Every field has its own section. Find out which field is selected using |
| 326 // the section of |indexPath|. Adjust the index if a header section is |
| 327 // present before the editor fields. |
| 328 NSInteger index = indexPath.section; |
| 329 if ([self.collectionViewModel |
| 330 hasSectionForSectionIdentifier:SectionIdentifierHeader]) |
| 331 index--; |
| 332 DCHECK(index >= 0 && index < static_cast<NSInteger>(_fields.count)); |
| 333 [_delegate paymentRequestEditViewController:self |
| 334 didSelectField:[_fields objectAtIndex:index]]; |
| 335 } |
| 336 |
| 281 #pragma mark MDCCollectionViewStylingDelegate | 337 #pragma mark MDCCollectionViewStylingDelegate |
| 282 | 338 |
| 283 - (CGFloat)collectionView:(UICollectionView*)collectionView | 339 - (CGFloat)collectionView:(UICollectionView*)collectionView |
| 284 cellHeightAtIndexPath:(NSIndexPath*)indexPath { | 340 cellHeightAtIndexPath:(NSIndexPath*)indexPath { |
| 285 CollectionViewItem* item = | 341 CollectionViewItem* item = |
| 286 [self.collectionViewModel itemAtIndexPath:indexPath]; | 342 [self.collectionViewModel itemAtIndexPath:indexPath]; |
| 287 switch (item.type) { | 343 switch (item.type) { |
| 344 case ItemTypeHeader: |
| 345 case ItemTypeFooter: |
| 288 case ItemTypeTextField: | 346 case ItemTypeTextField: |
| 289 case ItemTypeErrorMessage: | 347 case ItemTypeErrorMessage: |
| 290 case ItemTypeFooter: | |
| 291 return [MDCCollectionViewCell | 348 return [MDCCollectionViewCell |
| 292 cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds) | 349 cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds) |
| 293 forItem:item]; | 350 forItem:item]; |
| 351 case ItemTypeSelectorField: |
| 352 return MDCCellDefaultOneLineHeight; |
| 294 default: | 353 default: |
| 295 NOTREACHED(); | 354 NOTREACHED(); |
| 296 return MDCCellDefaultOneLineHeight; | 355 return MDCCellDefaultOneLineHeight; |
| 297 } | 356 } |
| 298 } | 357 } |
| 299 | 358 |
| 300 - (BOOL)collectionView:(UICollectionView*)collectionView | 359 - (BOOL)collectionView:(UICollectionView*)collectionView |
| 301 hidesInkViewAtIndexPath:(NSIndexPath*)indexPath { | 360 hidesInkViewAtIndexPath:(NSIndexPath*)indexPath { |
| 302 NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath]; | 361 NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath]; |
| 303 switch (type) { | 362 switch (type) { |
| 363 case ItemTypeHeader: |
| 364 case ItemTypeFooter: |
| 304 case ItemTypeErrorMessage: | 365 case ItemTypeErrorMessage: |
| 305 case ItemTypeFooter: | |
| 306 return YES; | 366 return YES; |
| 307 default: | 367 default: |
| 308 return NO; | 368 return NO; |
| 309 } | 369 } |
| 310 } | 370 } |
| 311 | 371 |
| 312 - (BOOL)collectionView:(UICollectionView*)collectionView | 372 - (BOOL)collectionView:(UICollectionView*)collectionView |
| 313 shouldHideItemBackgroundAtIndexPath:(NSIndexPath*)indexPath { | 373 shouldHideItemBackgroundAtIndexPath:(NSIndexPath*)indexPath { |
| 314 NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath]; | 374 NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath]; |
| 315 switch (type) { | 375 switch (type) { |
| 376 case ItemTypeHeader: |
| 377 return [_dataSource shouldHideBackgroundForHeaderItem]; |
| 316 case ItemTypeFooter: | 378 case ItemTypeFooter: |
| 317 return YES; | 379 return YES; |
| 318 default: | 380 default: |
| 319 return NO; | 381 return NO; |
| 320 } | 382 } |
| 321 } | 383 } |
| 322 | 384 |
| 323 #pragma mark - PaymentRequestEditViewControllerValidator | |
| 324 | |
| 325 - (NSString*)paymentRequestEditViewController: | |
| 326 (PaymentRequestEditViewController*)controller | |
| 327 validateValue:(NSString*)value | |
| 328 autofillUIType:(AutofillUIType)autofillUIType | |
| 329 required:(BOOL)required { | |
| 330 if (required && !value.length) { | |
| 331 return l10n_util::GetNSString( | |
| 332 IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE); | |
| 333 } | |
| 334 return nil; | |
| 335 } | |
| 336 | |
| 337 #pragma mark - Helper methods | 385 #pragma mark - Helper methods |
| 338 | 386 |
| 339 - (NSIndexPath*)indexPathWithSectionOffset:(NSInteger)offset | 387 - (NSIndexPath*)indexPathWithSectionOffset:(NSInteger)offset |
| 340 fromPath:(NSIndexPath*)indexPath { | 388 fromPath:(NSIndexPath*)indexPath { |
| 341 DCHECK(indexPath); | 389 DCHECK(indexPath); |
| 342 DCHECK(offset); | 390 DCHECK(offset); |
| 343 NSInteger nextSection = [indexPath section] + offset; | 391 NSInteger nextSection = [indexPath section] + offset; |
| 344 if (nextSection >= 0 && | 392 if (nextSection >= 0 && |
| 345 nextSection < [[self collectionView] numberOfSections]) { | 393 nextSection < [[self collectionView] numberOfSections]) { |
| 346 return [NSIndexPath indexPathForRow:[indexPath row] inSection:nextSection]; | 394 return [NSIndexPath indexPathForRow:[indexPath row] inSection:nextSection]; |
| 347 } | 395 } |
| 348 return nil; | 396 return nil; |
| 349 } | 397 } |
| 350 | 398 |
| 351 - (AutofillEditCell*)nextTextFieldWithOffset:(NSInteger)offset { | 399 - (AutofillEditCell*)nextTextFieldWithOffset:(NSInteger)offset { |
| 352 UICollectionView* collectionView = [self collectionView]; | 400 UICollectionView* collectionView = [self collectionView]; |
| 353 NSIndexPath* currentCellPath = [self indexPathForCurrentTextField]; | 401 NSIndexPath* currentCellPath = [self indexPathForCurrentTextField]; |
| 354 DCHECK(currentCellPath); | 402 DCHECK(currentCellPath); |
| 355 NSIndexPath* nextCellPath = | 403 NSIndexPath* nextCellPath = |
| 356 [self indexPathWithSectionOffset:offset fromPath:currentCellPath]; | 404 [self indexPathWithSectionOffset:offset fromPath:currentCellPath]; |
| 357 if (nextCellPath) { | 405 while (nextCellPath) { |
| 358 id nextCell = [collectionView cellForItemAtIndexPath:nextCellPath]; | 406 id nextCell = [collectionView cellForItemAtIndexPath:nextCellPath]; |
| 359 if ([nextCell isKindOfClass:[AutofillEditCell class]]) { | 407 if ([nextCell isKindOfClass:[AutofillEditCell class]]) { |
| 360 return base::mac::ObjCCastStrict<AutofillEditCell>( | 408 return base::mac::ObjCCastStrict<AutofillEditCell>( |
| 361 [collectionView cellForItemAtIndexPath:nextCellPath]); | 409 [collectionView cellForItemAtIndexPath:nextCellPath]); |
| 362 } | 410 } |
| 411 nextCellPath = |
| 412 [self indexPathWithSectionOffset:offset fromPath:nextCellPath]; |
| 363 } | 413 } |
| 364 return nil; | 414 return nil; |
| 365 } | 415 } |
| 366 | 416 |
| 367 - (void)updateAccessoryViewButtonsStates { | 417 - (void)updateAccessoryViewButtonsStates { |
| 368 AutofillEditCell* previousCell = [self nextTextFieldWithOffset:-1]; | 418 AutofillEditCell* previousCell = [self nextTextFieldWithOffset:-1]; |
| 369 [[_accessoryView previousButton] setEnabled:previousCell != nil]; | 419 [[_accessoryView previousButton] setEnabled:previousCell != nil]; |
| 370 | 420 |
| 371 AutofillEditCell* nextCell = [self nextTextFieldWithOffset:1]; | 421 AutofillEditCell* nextCell = [self nextTextFieldWithOffset:1]; |
| 372 [[_accessoryView nextButton] setEnabled:nextCell != nil]; | 422 [[_accessoryView nextButton] setEnabled:nextCell != nil]; |
| 373 } | 423 } |
| 374 | 424 |
| 425 - (void)addOrRemoveErrorMessage:(NSString*)errorMessage |
| 426 inSectionWithIdentifier:(NSInteger)sectionIdentifier { |
| 427 CollectionViewModel* model = self.collectionViewModel; |
| 428 if ([model hasItemForItemType:ItemTypeErrorMessage |
| 429 sectionIdentifier:sectionIdentifier]) { |
| 430 NSIndexPath* indexPath = [model indexPathForItemType:ItemTypeErrorMessage |
| 431 sectionIdentifier:sectionIdentifier]; |
| 432 if (!errorMessage.length) { |
| 433 // Remove the item at the index path. |
| 434 [model removeItemWithType:ItemTypeErrorMessage |
| 435 fromSectionWithIdentifier:sectionIdentifier]; |
| 436 [self.collectionView deleteItemsAtIndexPaths:@[ indexPath ]]; |
| 437 } else { |
| 438 // Reload the item at the index path. |
| 439 PaymentsTextItem* item = base::mac::ObjCCastStrict<PaymentsTextItem>( |
| 440 [model itemAtIndexPath:indexPath]); |
| 441 item.text = errorMessage; |
| 442 [self.collectionView reloadItemsAtIndexPaths:@[ indexPath ]]; |
| 443 } |
| 444 } else if (errorMessage.length) { |
| 445 // Insert an item at the index path. |
| 446 PaymentsTextItem* errorMessageItem = |
| 447 [[PaymentsTextItem alloc] initWithType:ItemTypeErrorMessage]; |
| 448 errorMessageItem.text = errorMessage; |
| 449 errorMessageItem.image = NativeImage(IDR_IOS_PAYMENTS_WARNING); |
| 450 errorMessageItem.accessibilityIdentifier = kWarningMessageAccessibilityID; |
| 451 [model addItem:errorMessageItem toSectionWithIdentifier:sectionIdentifier]; |
| 452 NSIndexPath* indexPath = [model indexPathForItemType:ItemTypeErrorMessage |
| 453 sectionIdentifier:sectionIdentifier]; |
| 454 [self.collectionView insertItemsAtIndexPaths:@[ indexPath ]]; |
| 455 } |
| 456 } |
| 457 |
| 375 #pragma mark - Keyboard handling | 458 #pragma mark - Keyboard handling |
| 376 | 459 |
| 377 - (void)keyboardDidShow { | 460 - (void)keyboardDidShow { |
| 378 [self.collectionView | 461 [self.collectionView |
| 379 scrollToItemAtIndexPath:[self.collectionView | 462 scrollToItemAtIndexPath:[self.collectionView |
| 380 indexPathForCell:_currentEditingCell] | 463 indexPathForCell:_currentEditingCell] |
| 381 atScrollPosition:UICollectionViewScrollPositionCenteredVertically | 464 atScrollPosition:UICollectionViewScrollPositionCenteredVertically |
| 382 animated:YES]; | 465 animated:YES]; |
| 383 } | 466 } |
| 384 | 467 |
| 385 @end | 468 @end |
| 386 | 469 |
| 387 @implementation PaymentRequestEditViewController (Internal) | 470 @implementation PaymentRequestEditViewController (Internal) |
| 388 | 471 |
| 389 - (BOOL)validateForm { | 472 - (BOOL)validateForm { |
| 390 for (EditorField* field in _fields) { | 473 for (EditorField* field in _fields) { |
| 391 AutofillEditItem* item = field.item; | 474 switch (field.fieldType) { |
| 475 case EditorFieldTypeTextField: { |
| 476 AutofillEditItem* item = |
| 477 base::mac::ObjCCastStrict<AutofillEditItem>(field.item); |
| 478 // Update the EditorField with the value of the text field. |
| 479 field.value = item.textFieldValue; |
| 480 break; |
| 481 } |
| 482 case EditorFieldTypeSelector: { |
| 483 // No need to update the EditorField. It should already be up-to-date. |
| 484 break; |
| 485 } |
| 486 default: |
| 487 NOTREACHED(); |
| 488 } |
| 392 | 489 |
| 393 NSString* errorMessage = [_validatorDelegate | 490 NSString* errorMessage = |
| 394 paymentRequestEditViewController:self | 491 [_validatorDelegate paymentRequestEditViewController:self |
| 395 validateValue:item.textFieldValue | 492 validateField:field]; |
| 396 autofillUIType:field.autofillUIType | |
| 397 required:field.isRequired]; | |
| 398 [self addOrRemoveErrorMessage:errorMessage | 493 [self addOrRemoveErrorMessage:errorMessage |
| 399 inSectionWithIdentifier:field.sectionIdentifier]; | 494 inSectionWithIdentifier:field.sectionIdentifier]; |
| 400 if (errorMessage.length) | 495 if (errorMessage.length) |
| 401 return NO; | 496 return NO; |
| 402 | |
| 403 field.value = item.textFieldValue; | |
| 404 } | 497 } |
| 405 return YES; | 498 return YES; |
| 406 } | 499 } |
| 407 | 500 |
| 408 - (void)loadHeaderItems { | |
| 409 } | |
| 410 | |
| 411 - (void)loadFooterItems { | 501 - (void)loadFooterItems { |
| 412 CollectionViewModel* model = self.collectionViewModel; | 502 CollectionViewModel* model = self.collectionViewModel; |
| 413 | 503 |
| 414 [model addSectionWithIdentifier:SectionIdentifierFooter]; | 504 [model addSectionWithIdentifier:SectionIdentifierFooter]; |
| 415 CollectionViewFooterItem* footerItem = | 505 CollectionViewFooterItem* footerItem = |
| 416 [[CollectionViewFooterItem alloc] initWithType:ItemTypeFooter]; | 506 [[CollectionViewFooterItem alloc] initWithType:ItemTypeFooter]; |
| 417 footerItem.text = l10n_util::GetNSString(IDS_PAYMENTS_REQUIRED_FIELD_MESSAGE); | 507 footerItem.text = l10n_util::GetNSString(IDS_PAYMENTS_REQUIRED_FIELD_MESSAGE); |
| 418 [model addItem:footerItem toSectionWithIdentifier:SectionIdentifierFooter]; | 508 [model addItem:footerItem toSectionWithIdentifier:SectionIdentifierFooter]; |
| 419 } | 509 } |
| 420 | 510 |
| 421 - (NSIndexPath*)indexPathForCurrentTextField { | 511 - (NSIndexPath*)indexPathForCurrentTextField { |
| 422 DCHECK(_currentEditingCell); | 512 DCHECK(_currentEditingCell); |
| 423 NSIndexPath* indexPath = | 513 NSIndexPath* indexPath = |
| 424 [[self collectionView] indexPathForCell:_currentEditingCell]; | 514 [[self collectionView] indexPathForCell:_currentEditingCell]; |
| 425 DCHECK(indexPath); | 515 DCHECK(indexPath); |
| 426 return indexPath; | 516 return indexPath; |
| 427 } | 517 } |
| 428 | 518 |
| 429 - (void)addOrRemoveErrorMessage:(NSString*)errorMessage | |
| 430 inSectionWithIdentifier:(NSInteger)sectionIdentifier { | |
| 431 CollectionViewModel* model = self.collectionViewModel; | |
| 432 if ([model hasItemForItemType:ItemTypeErrorMessage | |
| 433 sectionIdentifier:sectionIdentifier]) { | |
| 434 NSIndexPath* indexPath = [model indexPathForItemType:ItemTypeErrorMessage | |
| 435 sectionIdentifier:sectionIdentifier]; | |
| 436 if (!errorMessage.length) { | |
| 437 // Remove the item at the index path. | |
| 438 [model removeItemWithType:ItemTypeErrorMessage | |
| 439 fromSectionWithIdentifier:sectionIdentifier]; | |
| 440 [self.collectionView deleteItemsAtIndexPaths:@[ indexPath ]]; | |
| 441 } else { | |
| 442 // Reload the item at the index path. | |
| 443 PaymentsTextItem* item = base::mac::ObjCCastStrict<PaymentsTextItem>( | |
| 444 [model itemAtIndexPath:indexPath]); | |
| 445 item.text = errorMessage; | |
| 446 [self.collectionView reloadItemsAtIndexPaths:@[ indexPath ]]; | |
| 447 } | |
| 448 } else if (errorMessage.length) { | |
| 449 // Insert an item at the index path. | |
| 450 PaymentsTextItem* errorMessageItem = | |
| 451 [[PaymentsTextItem alloc] initWithType:ItemTypeErrorMessage]; | |
| 452 errorMessageItem.text = errorMessage; | |
| 453 errorMessageItem.image = NativeImage(IDR_IOS_PAYMENTS_WARNING); | |
| 454 errorMessageItem.accessibilityIdentifier = kWarningMessageAccessibilityID; | |
| 455 [model addItem:errorMessageItem toSectionWithIdentifier:sectionIdentifier]; | |
| 456 NSIndexPath* indexPath = [model indexPathForItemType:ItemTypeErrorMessage | |
| 457 sectionIdentifier:sectionIdentifier]; | |
| 458 [self.collectionView insertItemsAtIndexPaths:@[ indexPath ]]; | |
| 459 } | |
| 460 } | |
| 461 | |
| 462 @end | 519 @end |
| OLD | NEW |