Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(50)

Side by Side Diff: ios/chrome/browser/payments/payment_request_edit_view_controller.mm

Issue 2744823003: [Payment Request] Generic edit form (Closed)
Patch Set: Addressed comments Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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/strings/grit/components_strings.h"
11 #import "ios/chrome/browser/payments/cells/payments_text_item.h"
12 #import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrom e.h"
13 #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
14 #import "ios/chrome/browser/ui/settings/autofill_edit_accessory_view.h"
15 #import "ios/chrome/browser/ui/settings/cells/autofill_edit_item.h"
16 #import "ios/chrome/browser/ui/uikit_ui_util.h"
17 #include "ios/chrome/grit/ios_theme_resources.h"
18 #import "ios/third_party/material_components_ios/src/components/Typography/src/M aterialTypography.h"
19 #include "ui/base/l10n/l10n_util.h"
20
21 #if !defined(__has_feature) || !__has_feature(objc_arc)
22 #error "This file requires ARC support."
23 #endif
24
25 namespace {
26
27 const CGFloat kSeparatorEdgeInset = 14;
28
29 AutofillEditCell* AutofillEditCellForTextField(UITextField* textField) {
30 for (UIView* view = textField; view; view = [view superview]) {
31 AutofillEditCell* cell = base::mac::ObjCCast<AutofillEditCell>(view);
32 if (cell)
33 return cell;
34 }
35
36 // There has to be a cell associated with this text field.
37 NOTREACHED();
38 return nil;
39 }
40
41 // Item type for the error message item. This value is chosen to avoid
42 // overlapping with the values starting from kItemTypeEnumZero. This is a
43 // repeated item type.
44 const NSInteger ItemTypeErrorMessage = kItemTypeEnumZero + 100;
45
46 } // namespace
47
48 @interface PaymentRequestEditViewController ()<AutofillEditAccessoryDelegate> {
49 // The currently focused cell. May be nil.
50 __weak AutofillEditCell* _currentEditingCell;
51
52 AutofillEditAccessoryView* _accessoryView;
53 }
54
55 // Returns the index path for the cell associated with the currently focused
56 // text field.
57 - (NSIndexPath*)indexPathForCurrentTextField;
58
59 // Returns the validation error string for |value| for the autofill type
60 // |autofillType|. |required| indicates whether this is a required field or not.
61 // If there are no validation errors, an empty string is returned.
62 - (NSString*)validateValue:(NSString*)value
63 autofillType:(autofill::ServerFieldType)autofillType
64 required:(BOOL)required;
65
66 // Adds an error message item in the section |sectionIdentifier| if
67 // |errorMessage| is non-empty. Otherwise removes such an item if one exists.
68 - (void)addOrRemoveErrorMessage:(NSString*)errorMessage
69 inSectionWithIdentifier:(NSInteger)sectionIdentifier;
70
71 // Returns the indexPath for the same row as that of |indexPath| in a section
72 // with the given offset relative to that of |indexPath|. It may return nil.
73 - (NSIndexPath*)indexPathWithSectionOffset:(NSInteger)offset
74 fromPath:(NSIndexPath*)indexPath;
75
76 // Returns the text field with the given offset relative to the currently
77 // focused text field. May return nil.
78 - (AutofillEditCell*)nextTextFieldWithOffset:(NSInteger)offset;
79
80 // Enables or disables the accessory view's previous and next buttons depending
81 // on whether there is a text field before and after the currently focused text
82 // field.
83 - (void)updateAccessoryViewButtonsStates;
84
85 @end
86
87 @implementation PaymentRequestEditViewController
88
89 - (instancetype)init {
90 self = [super initWithStyle:CollectionViewControllerStyleAppBar];
91 if (self) {
92 _accessoryView = [[AutofillEditAccessoryView alloc] initWithDelegate:self];
93 }
94 return self;
95 }
96
97 - (void)viewDidAppear:(BOOL)animated {
98 [super viewDidAppear:animated];
99 [[NSNotificationCenter defaultCenter]
100 addObserver:self
101 selector:@selector(keyboardDidShow)
102 name:UIKeyboardDidShowNotification
103 object:nil];
104 }
105
106 - (void)viewWillDisappear:(BOOL)animated {
107 [super viewWillDisappear:animated];
108 [[NSNotificationCenter defaultCenter]
109 removeObserver:self
110 name:UIKeyboardDidShowNotification
111 object:nil];
112 }
113
114 #pragma mark - CollectionViewController methods
115
116 - (void)loadModel {
117 [super loadModel];
118 CollectionViewModel* model = self.collectionViewModel;
119
120 // Iterate over the field definitions and add the respective sections and
121 // items.
122 for (const auto& field : [self editorFields]) {
123 [model addSectionWithIdentifier:field.section_id];
124 AutofillEditItem* item =
125 [[AutofillEditItem alloc] initWithType:field.item_type];
126 item.textFieldName = base::SysUTF16ToNSString(field.label);
127 item.textFieldEnabled = YES;
128 item.required = field.required;
129 item.autofillType = field.data_type;
130 [model addItem:item toSectionWithIdentifier:field.section_id];
131 }
132 }
133
134 - (void)viewDidLoad {
135 [super viewDidLoad];
136
137 // Customize collection view settings.
138 self.styler.cellStyle = MDCCollectionViewCellStyleCard;
139 self.styler.separatorInset =
140 UIEdgeInsetsMake(0, kSeparatorEdgeInset, 0, kSeparatorEdgeInset);
141 }
142
143 #pragma mark - UITextFieldDelegate
144
145 - (void)textFieldDidBeginEditing:(UITextField*)textField {
146 _currentEditingCell = AutofillEditCellForTextField(textField);
147 [textField setInputAccessoryView:_accessoryView];
148 [self updateAccessoryViewButtonsStates];
149 }
150
151 - (void)textFieldDidEndEditing:(UITextField*)textField {
152 DCHECK(_currentEditingCell == AutofillEditCellForTextField(textField));
153 CollectionViewModel* model = self.collectionViewModel;
154
155 // Validate the text field. If there is a validation error, display an error
156 // message item in the same section as the field. Otherwise remove the error
157 // message item in that section.
158 NSIndexPath* indexPath = [self indexPathForCurrentTextField];
159 AutofillEditItem* item = base::mac::ObjCCastStrict<AutofillEditItem>(
160 [model itemAtIndexPath:indexPath]);
161
162 NSString* errorMessage = [self validateValue:textField.text
163 autofillType:item.autofillType
164 required:item.required];
165 NSInteger sectionIdentifier =
166 [model sectionIdentifierForSection:[indexPath section]];
167 [self addOrRemoveErrorMessage:errorMessage
168 inSectionWithIdentifier:sectionIdentifier];
169
170 [textField setInputAccessoryView:nil];
171 _currentEditingCell = nil;
172 }
173
174 - (BOOL)textFieldShouldReturn:(UITextField*)textField {
175 DCHECK([_currentEditingCell textField] == textField);
176 [self nextPressed];
177 return NO;
178 }
179
180 #pragma mark - AutofillEditAccessoryDelegate
181
182 - (void)nextPressed {
183 AutofillEditCell* nextCell = [self nextTextFieldWithOffset:1];
184 if (nextCell)
185 [nextCell.textField becomeFirstResponder];
186 }
187
188 - (void)previousPressed {
189 AutofillEditCell* previousCell = [self nextTextFieldWithOffset:-1];
190 if (previousCell)
191 [previousCell.textField becomeFirstResponder];
192 }
193
194 - (void)closePressed {
195 [[_currentEditingCell textField] resignFirstResponder];
196 }
197
198 #pragma mark - UICollectionViewDataSource
199
200 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
201 cellForItemAtIndexPath:(NSIndexPath*)indexPath {
202 UICollectionViewCell* cell =
203 [super collectionView:collectionView cellForItemAtIndexPath:indexPath];
204
205 if ([cell isKindOfClass:[AutofillEditCell class]]) {
206 AutofillEditCell* autofillEditCell =
207 base::mac::ObjCCast<AutofillEditCell>(cell);
208 autofillEditCell.textField.delegate = self;
209 }
210
211 CollectionViewItem* item =
212 [self.collectionViewModel itemAtIndexPath:indexPath];
213 switch (item.type) {
214 case ItemTypeErrorMessage: {
215 PaymentsTextCell* errorMessageCell =
216 base::mac::ObjCCastStrict<PaymentsTextCell>(cell);
217 errorMessageCell.textLabel.font = [MDCTypography body1Font];
218 errorMessageCell.textLabel.textColor =
219 [[MDCPalette cr_redPalette] tint600];
220 break;
221 }
222 default:
223 break;
224 }
225
226 return cell;
227 }
228
229 #pragma mark MDCCollectionViewStylingDelegate
230
231 - (CGFloat)collectionView:(UICollectionView*)collectionView
232 cellHeightAtIndexPath:(NSIndexPath*)indexPath {
233 CollectionViewItem* item =
234 [self.collectionViewModel itemAtIndexPath:indexPath];
235
236 if ([item isKindOfClass:[AutofillEditItem class]] ||
237 item.type == ItemTypeErrorMessage) {
238 return [MDCCollectionViewCell
239 cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds)
240 forItem:item];
241 }
242
243 NOTREACHED();
244 return MDCCellDefaultOneLineHeight;
245 }
246
247 - (BOOL)collectionView:(UICollectionView*)collectionView
248 hidesInkViewAtIndexPath:(NSIndexPath*)indexPath {
249 NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath];
250 switch (type) {
251 case ItemTypeErrorMessage:
252 return YES;
253 default:
254 return NO;
255 }
256 }
257
258 #pragma mark - Protected methods
259
260 - (std::vector<EditorField>)editorFields {
261 return std::vector<EditorField>();
262 }
263
264 #pragma mark - Helper methods
265
266 - (NSIndexPath*)indexPathForCurrentTextField {
267 DCHECK(_currentEditingCell);
268 NSIndexPath* indexPath =
269 [[self collectionView] indexPathForCell:_currentEditingCell];
270 DCHECK(indexPath);
271 return indexPath;
272 }
273
274 - (NSString*)validateValue:(NSString*)value
275 autofillType:(autofill::ServerFieldType)autofillType
276 required:(BOOL)required {
277 if (required && value.length == 0) {
278 return l10n_util::GetNSString(
279 IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE);
280 }
281 return @"";
282 }
283
284 - (void)addOrRemoveErrorMessage:(NSString*)errorMessage
285 inSectionWithIdentifier:(NSInteger)sectionIdentifier {
286 CollectionViewModel* model = self.collectionViewModel;
287 if ([model hasItemForItemType:ItemTypeErrorMessage
288 sectionIdentifier:sectionIdentifier]) {
289 NSIndexPath* indexPath = [model indexPathForItemType:ItemTypeErrorMessage
290 sectionIdentifier:sectionIdentifier];
291 if (errorMessage.length == 0) {
292 // Remove the item at the index path.
293 [model removeItemWithType:ItemTypeErrorMessage
294 fromSectionWithIdentifier:sectionIdentifier];
295 [self.collectionView deleteItemsAtIndexPaths:@[ indexPath ]];
296 } else {
297 // Reload the item at the index path.
298 PaymentsTextItem* item = base::mac::ObjCCastStrict<PaymentsTextItem>(
299 [model itemAtIndexPath:indexPath]);
300 item.text = errorMessage;
301 [self.collectionView reloadItemsAtIndexPaths:@[ indexPath ]];
302 }
303 } else if (errorMessage.length != 0) {
304 // Insert an item at the index path.
305 PaymentsTextItem* errorMessageItem =
306 [[PaymentsTextItem alloc] initWithType:ItemTypeErrorMessage];
307 errorMessageItem.text = errorMessage;
308 errorMessageItem.image = NativeImage(IDR_IOS_PAYMENTS_WARNING);
309 [model addItem:errorMessageItem toSectionWithIdentifier:sectionIdentifier];
310 NSIndexPath* indexPath = [model indexPathForItemType:ItemTypeErrorMessage
311 sectionIdentifier:sectionIdentifier];
312 [self.collectionView insertItemsAtIndexPaths:@[ indexPath ]];
313 }
314 }
315
316 - (NSIndexPath*)indexPathWithSectionOffset:(NSInteger)offset
317 fromPath:(NSIndexPath*)indexPath {
318 DCHECK(indexPath);
319 DCHECK(offset);
320 NSInteger nextSection = [indexPath section] + offset;
321 if (nextSection >= 0 &&
322 nextSection < [[self collectionView] numberOfSections]) {
323 return [NSIndexPath indexPathForRow:[indexPath row] inSection:nextSection];
324 }
325 return nil;
326 }
327
328 - (AutofillEditCell*)nextTextFieldWithOffset:(NSInteger)offset {
329 UICollectionView* collectionView = [self collectionView];
330 NSIndexPath* currentCellPath = [self indexPathForCurrentTextField];
331 DCHECK(currentCellPath);
332 NSIndexPath* nextCellPath =
333 [self indexPathWithSectionOffset:offset fromPath:currentCellPath];
334 if (nextCellPath) {
335 id nextCell = [collectionView cellForItemAtIndexPath:nextCellPath];
336 if ([nextCell isKindOfClass:[AutofillEditCell class]]) {
337 return base::mac::ObjCCastStrict<AutofillEditCell>(
338 [collectionView cellForItemAtIndexPath:nextCellPath]);
339 }
340 }
341 return nil;
342 }
343
344 - (void)updateAccessoryViewButtonsStates {
345 AutofillEditCell* previousCell = [self nextTextFieldWithOffset:-1];
346 [[_accessoryView previousButton] setEnabled:previousCell != nil];
347
348 AutofillEditCell* nextCell = [self nextTextFieldWithOffset:1];
349 [[_accessoryView nextButton] setEnabled:nextCell != nil];
350 }
351
352 #pragma mark - Keyboard handling
353
354 - (void)keyboardDidShow {
355 [self.collectionView scrollRectToVisible:[_currentEditingCell frame]
356 animated:YES];
357 }
358
359 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698