| OLD | NEW |
| (Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import "ios/chrome/browser/payments/payment_request_edit_view_controller.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #import "base/mac/foundation_util.h" |
| 9 #include "base/strings/sys_string_conversions.h" |
| 10 #include "components/autofill/core/browser/validation.h" |
| 11 #include "components/strings/grit/components_strings.h" |
| 12 #import "ios/chrome/browser/payments/cells/payments_text_item.h" |
| 13 #import "ios/chrome/browser/ui/settings/autofill_edit_accessory_view.h" |
| 14 #import "ios/chrome/browser/ui/settings/cells/autofill_edit_item.h" |
| 15 #import "ios/chrome/browser/ui/uikit_ui_util.h" |
| 16 #include "ios/chrome/grit/ios_theme_resources.h" |
| 17 #include "ui/base/l10n/l10n_util.h" |
| 18 |
| 19 #if !defined(__has_feature) || !__has_feature(objc_arc) |
| 20 #error "This file requires ARC support." |
| 21 #endif |
| 22 |
| 23 namespace { |
| 24 |
| 25 AutofillEditCell* AutofillEditCellForTextField(UITextField* textField) { |
| 26 for (UIView* view = textField; view; view = [view superview]) { |
| 27 AutofillEditCell* cell = base::mac::ObjCCast<AutofillEditCell>(view); |
| 28 if (cell) |
| 29 return cell; |
| 30 } |
| 31 |
| 32 // There has to be a cell associated with this text field. |
| 33 NOTREACHED(); |
| 34 return nil; |
| 35 } |
| 36 |
| 37 } // namespace |
| 38 |
| 39 @interface PaymentRequestEditViewController ()<AutofillEditAccessoryDelegate> { |
| 40 // The currently focused cell. May be nil. |
| 41 __weak AutofillEditCell* _currentEditingCell; |
| 42 |
| 43 AutofillEditAccessoryView* _accessoryView; |
| 44 |
| 45 // The PaymentRequest object owning an instance of web::PaymentRequest as |
| 46 // provided by the page invoking the Payment Request API. This is a weak |
| 47 // pointer and should outlive this class. |
| 48 PaymentRequest* _paymentRequest; |
| 49 } |
| 50 @end |
| 51 |
| 52 @implementation PaymentRequestEditViewController |
| 53 |
| 54 - (instancetype)initWithPaymentRequest:(PaymentRequest*)paymentRequest { |
| 55 self = [super initWithStyle:CollectionViewControllerStyleAppBar]; |
| 56 if (self) { |
| 57 DCHECK(paymentRequest); |
| 58 _paymentRequest = paymentRequest; |
| 59 _accessoryView = [[AutofillEditAccessoryView alloc] initWithDelegate:self]; |
| 60 } |
| 61 return self; |
| 62 } |
| 63 |
| 64 - (void)viewDidAppear:(BOOL)animated { |
| 65 [super viewDidAppear:animated]; |
| 66 [[NSNotificationCenter defaultCenter] |
| 67 addObserver:self |
| 68 selector:@selector(keyboardDidShow) |
| 69 name:UIKeyboardDidShowNotification |
| 70 object:nil]; |
| 71 } |
| 72 |
| 73 - (void)viewWillDisappear:(BOOL)animated { |
| 74 [super viewWillDisappear:animated]; |
| 75 [[NSNotificationCenter defaultCenter] |
| 76 removeObserver:self |
| 77 name:UIKeyboardDidShowNotification |
| 78 object:nil]; |
| 79 } |
| 80 |
| 81 #pragma mark - CollectionViewController methods |
| 82 |
| 83 - (void)loadModel { |
| 84 [super loadModel]; |
| 85 CollectionViewModel* model = self.collectionViewModel; |
| 86 |
| 87 // Iterate over the field definitions and add the respective sections and |
| 88 // items. |
| 89 for (const auto& field : [self editorFields]) { |
| 90 [model addSectionWithIdentifier:field.section_id]; |
| 91 AutofillEditItem* item = |
| 92 [[AutofillEditItem alloc] initWithType:field.item_type]; |
| 93 NSString* labelFormat = field.required ? @"%@*" : @"%@"; |
| 94 item.textFieldName = [NSString |
| 95 stringWithFormat:labelFormat, base::SysUTF16ToNSString(field.label)]; |
| 96 item.textFieldEnabled = YES; |
| 97 item.required = field.required; |
| 98 item.autofillType = field.data_type; |
| 99 [model addItem:item toSectionWithIdentifier:field.section_id]; |
| 100 } |
| 101 } |
| 102 |
| 103 #pragma mark - UITextFieldDelegate |
| 104 |
| 105 - (void)textFieldDidBeginEditing:(UITextField*)textField { |
| 106 _currentEditingCell = AutofillEditCellForTextField(textField); |
| 107 [textField setInputAccessoryView:_accessoryView]; |
| 108 [self updateAccessoryViewButtonState]; |
| 109 } |
| 110 |
| 111 - (void)textFieldDidEndEditing:(UITextField*)textField { |
| 112 DCHECK(_currentEditingCell == AutofillEditCellForTextField(textField)); |
| 113 [textField setInputAccessoryView:nil]; |
| 114 _currentEditingCell = nil; |
| 115 } |
| 116 |
| 117 - (BOOL)textFieldShouldReturn:(UITextField*)textField { |
| 118 DCHECK([_currentEditingCell textField] == textField); |
| 119 [self nextPressed]; |
| 120 return NO; |
| 121 } |
| 122 |
| 123 #pragma mark - AutofillEditAccessoryDelegate |
| 124 |
| 125 - (void)nextPressed { |
| 126 AutofillEditCell* nextCell = [self nextTextFieldWithOffset:1]; |
| 127 if (nextCell) |
| 128 [nextCell.textField becomeFirstResponder]; |
| 129 } |
| 130 |
| 131 - (void)previousPressed { |
| 132 AutofillEditCell* previousCell = [self nextTextFieldWithOffset:-1]; |
| 133 if (previousCell) |
| 134 [previousCell.textField becomeFirstResponder]; |
| 135 } |
| 136 |
| 137 - (void)closePressed { |
| 138 [[_currentEditingCell textField] resignFirstResponder]; |
| 139 } |
| 140 |
| 141 #pragma mark - Protected methods |
| 142 |
| 143 - (std::vector<EditorField>)editorFields { |
| 144 return std::vector<EditorField>(); |
| 145 } |
| 146 |
| 147 - (NSIndexPath*)indexPathForCurrentTextField { |
| 148 DCHECK(_currentEditingCell); |
| 149 NSIndexPath* indexPath = |
| 150 [[self collectionView] indexPathForCell:_currentEditingCell]; |
| 151 DCHECK(indexPath); |
| 152 return indexPath; |
| 153 } |
| 154 |
| 155 - (NSString*)validateValue:(NSString*)value |
| 156 autofillType:(autofill::ServerFieldType)autofillType |
| 157 required:(BOOL)required { |
| 158 base::string16 errorMessage; |
| 159 if (value.length != 0) { |
| 160 base::string16 valueString = base::SysNSStringToUTF16(value); |
| 161 autofillType == autofill::CREDIT_CARD_NUMBER |
| 162 ? autofill::IsValidCreditCardNumberForBasicCardNetworks( |
| 163 valueString, _paymentRequest->supported_card_networks(), |
| 164 &errorMessage) |
| 165 : autofill::IsValidForType(valueString, autofillType, &errorMessage); |
| 166 return base::SysUTF16ToNSString(errorMessage); |
| 167 } |
| 168 |
| 169 if (required) { |
| 170 errorMessage = l10n_util::GetStringUTF16( |
| 171 IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE); |
| 172 } |
| 173 |
| 174 return base::SysUTF16ToNSString(errorMessage); |
| 175 } |
| 176 |
| 177 - (void)addOrRemoveErrorItemWithType:(NSInteger)itemType |
| 178 errorMessage:(NSString*)errorMessage |
| 179 inSectionWithIdentifier:(NSInteger)sectionIdentifier { |
| 180 CollectionViewModel* model = self.collectionViewModel; |
| 181 if ([model hasItemForItemType:itemType sectionIdentifier:sectionIdentifier]) { |
| 182 [model removeItemWithType:itemType |
| 183 fromSectionWithIdentifier:sectionIdentifier]; |
| 184 } |
| 185 |
| 186 if (errorMessage.length != 0) { |
| 187 PaymentsTextItem* errorMessageItem = |
| 188 [[PaymentsTextItem alloc] initWithType:itemType]; |
| 189 errorMessageItem.text = errorMessage; |
| 190 errorMessageItem.image = NativeImage(IDR_IOS_PAYMENTS_WARNING); |
| 191 [model addItem:errorMessageItem toSectionWithIdentifier:sectionIdentifier]; |
| 192 } |
| 193 |
| 194 // Update the entire section. |
| 195 NSInteger section = [model sectionForSectionIdentifier:sectionIdentifier]; |
| 196 [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:section]]; |
| 197 } |
| 198 |
| 199 #pragma mark - Helper methods |
| 200 |
| 201 - (NSIndexPath*)indexPathWithSectionOffset:(NSInteger)offset |
| 202 fromPath:(NSIndexPath*)indexPath { |
| 203 DCHECK(indexPath); |
| 204 DCHECK(offset); |
| 205 |
| 206 NSInteger nextSection = [indexPath section] + offset; |
| 207 if (nextSection >= 0 && |
| 208 nextSection < [[self collectionView] numberOfSections]) |
| 209 return [NSIndexPath indexPathForRow:[indexPath row] inSection:nextSection]; |
| 210 |
| 211 return nil; |
| 212 } |
| 213 |
| 214 - (AutofillEditCell*)nextTextFieldWithOffset:(NSInteger)offset { |
| 215 UICollectionView* collectionView = [self collectionView]; |
| 216 NSIndexPath* currentCellPath = [self indexPathForCurrentTextField]; |
| 217 DCHECK(currentCellPath); |
| 218 for (NSIndexPath* nextCellPath = |
| 219 [self indexPathWithSectionOffset:offset fromPath:currentCellPath]; |
| 220 nextCellPath; |
| 221 nextCellPath = |
| 222 [self indexPathWithSectionOffset:offset fromPath:nextCellPath]) { |
| 223 id nextCell = [collectionView cellForItemAtIndexPath:nextCellPath]; |
| 224 if ([nextCell isMemberOfClass:[AutofillEditCell class]]) { |
| 225 return base::mac::ObjCCastStrict<AutofillEditCell>( |
| 226 [collectionView cellForItemAtIndexPath:nextCellPath]); |
| 227 } |
| 228 } |
| 229 return nil; |
| 230 } |
| 231 |
| 232 - (void)updateAccessoryViewButtonState { |
| 233 AutofillEditCell* previousCell = [self nextTextFieldWithOffset:-1]; |
| 234 [[_accessoryView previousButton] setEnabled:previousCell != nil]; |
| 235 |
| 236 AutofillEditCell* nextCell = [self nextTextFieldWithOffset:1]; |
| 237 [[_accessoryView nextButton] setEnabled:nextCell != nil]; |
| 238 } |
| 239 |
| 240 #pragma mark - Keyboard handling |
| 241 |
| 242 - (void)keyboardDidShow { |
| 243 [self.collectionView scrollRectToVisible:[_currentEditingCell frame] |
| 244 animated:YES]; |
| 245 } |
| 246 |
| 247 @end |
| OLD | NEW |