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

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

Issue 2744823003: [Payment Request] Generic edit form (Closed)
Patch Set: Generic edit form + showCase 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 } // namespace
42
43 @interface PaymentRequestEditViewController ()<AutofillEditAccessoryDelegate> {
44 // The currently focused cell. May be nil.
45 __weak AutofillEditCell* _currentEditingCell;
46
47 AutofillEditAccessoryView* _accessoryView;
48 }
49
50 // Returns the index path for the cell associated with the currently focused
51 // text field.
52 - (NSIndexPath*)indexPathForCurrentTextField;
53
54 // Returns the validation error string for |value| for the autofill type
55 // |autofillType|. |required| indicates wether this is a required field or not.
lpromero 2017/03/16 13:01:07 s/wether/whether. I just found out "wether" exists
Moe 2017/03/16 15:44:23 Done! definitely whether and not wether here :)
56 // If there are no validation errors an empty string is returned.
lpromero 2017/03/16 13:01:07 Comma after "errors".
Moe 2017/03/16 15:44:23 Done.
57 - (NSString*)validateValue:(NSString*)value
58 autofillType:(autofill::ServerFieldType)autofillType
59 required:(BOOL)required;
60
61 // Adds an error message item in the section |sectionIdentifier| if
62 // |errorMessage| is non-empty. Otherwise removes such an item if one exists.
63 - (void)addOrRemoveErrorMessage:(NSString*)errorMessage
64 inSectionWithIdentifier:(NSInteger)sectionIdentifier;
65
66 // Returns the indexPath for the same row as that of |indexPath| in a section
67 // with the given offset relative to that of |indexPath|. It may return nil.
68 - (NSIndexPath*)indexPathWithSectionOffset:(NSInteger)offset
69 fromPath:(NSIndexPath*)indexPath;
70
71 // Returns the text field with the given offset relative to the currently
72 // focused text field. May return nil.
73 - (AutofillEditCell*)nextTextFieldWithOffset:(NSInteger)offset;
74
75 // Enables or Disables the accessory view's previous and next buttons depending
lpromero 2017/03/16 13:01:06 s/Disables/disables
Moe 2017/03/16 15:44:22 Done.
76 // on wehther there is a text field before and after the currently focused text
lpromero 2017/03/16 13:01:06 whether
Moe 2017/03/16 15:44:23 Done. Sorry. dyslexia...
77 // field.
78 - (void)updateAccessoryViewButtonState;
lpromero 2017/03/16 13:01:06 There should be a plural somewhere. updateAccessor
Moe 2017/03/16 15:44:23 Done.
79
80 @end
81
82 @implementation PaymentRequestEditViewController
83
84 - (instancetype)init {
85 self = [super initWithStyle:CollectionViewControllerStyleAppBar];
86 if (self) {
87 _accessoryView = [[AutofillEditAccessoryView alloc] initWithDelegate:self];
88 }
89 return self;
90 }
91
92 - (void)viewDidAppear:(BOOL)animated {
93 [super viewDidAppear:animated];
94 [[NSNotificationCenter defaultCenter]
95 addObserver:self
96 selector:@selector(keyboardDidShow)
97 name:UIKeyboardDidShowNotification
98 object:nil];
99 }
100
101 - (void)viewWillDisappear:(BOOL)animated {
102 [super viewWillDisappear:animated];
103 [[NSNotificationCenter defaultCenter]
104 removeObserver:self
105 name:UIKeyboardDidShowNotification
106 object:nil];
107 }
108
109 #pragma mark - CollectionViewController methods
110
111 - (void)loadModel {
112 [super loadModel];
113 CollectionViewModel* model = self.collectionViewModel;
114
115 // Iterate over the field definitions and add the respective sections and
116 // items.
117 for (const auto& field : [self editorFields]) {
118 [model addSectionWithIdentifier:field.section_id];
119 AutofillEditItem* item =
120 [[AutofillEditItem alloc] initWithType:field.item_type];
121 NSString* labelFormat = field.required ? @"%@*" : @"%@";
lpromero 2017/03/16 13:01:07 Since AutofillEditItem knows about the "required"
Moe 2017/03/16 15:44:23 Done.
122 item.textFieldName = [NSString
123 stringWithFormat:labelFormat, base::SysUTF16ToNSString(field.label)];
124 item.textFieldEnabled = YES;
125 item.required = field.required;
126 item.autofillType = field.data_type;
127 [model addItem:item toSectionWithIdentifier:field.section_id];
128 }
129 }
130
131 - (void)viewDidLoad {
132 [super viewDidLoad];
133
134 // Customize collection view settings.
135 self.styler.cellStyle = MDCCollectionViewCellStyleCard;
136 self.styler.separatorInset =
137 UIEdgeInsetsMake(0, kSeparatorEdgeInset, 0, kSeparatorEdgeInset);
138 }
139
140 #pragma mark - UITextFieldDelegate
141
142 - (void)textFieldDidBeginEditing:(UITextField*)textField {
143 _currentEditingCell = AutofillEditCellForTextField(textField);
144 [textField setInputAccessoryView:_accessoryView];
145 [self updateAccessoryViewButtonState];
146 }
147
148 - (void)textFieldDidEndEditing:(UITextField*)textField {
149 DCHECK(_currentEditingCell == AutofillEditCellForTextField(textField));
150 CollectionViewModel* model = self.collectionViewModel;
151
152 // Validate the text field. If there is a validation error, display an error
153 // message item in the same section as the field. Otherwise remove the error
154 // message item in that section.
155 NSIndexPath* indexPath = [self indexPathForCurrentTextField];
156 AutofillEditItem* item = base::mac::ObjCCastStrict<AutofillEditItem>(
157 [model itemAtIndexPath:indexPath]);
158
159 NSString* errorMessage = [self validateValue:textField.text
160 autofillType:item.autofillType
161 required:item.required];
162 NSInteger sectionIdentifier =
163 [model sectionIdentifierForSection:[indexPath section]];
164 [self addOrRemoveErrorMessage:errorMessage
165 inSectionWithIdentifier:sectionIdentifier];
166
167 [textField setInputAccessoryView:nil];
168 _currentEditingCell = nil;
169 }
170
171 - (BOOL)textFieldShouldReturn:(UITextField*)textField {
172 DCHECK([_currentEditingCell textField] == textField);
173 [self nextPressed];
174 return NO;
175 }
176
177 #pragma mark - AutofillEditAccessoryDelegate
178
179 - (void)nextPressed {
180 AutofillEditCell* nextCell = [self nextTextFieldWithOffset:1];
181 if (nextCell)
182 [nextCell.textField becomeFirstResponder];
183 }
184
185 - (void)previousPressed {
186 AutofillEditCell* previousCell = [self nextTextFieldWithOffset:-1];
187 if (previousCell)
188 [previousCell.textField becomeFirstResponder];
189 }
190
191 - (void)closePressed {
192 [[_currentEditingCell textField] resignFirstResponder];
193 }
194
195 #pragma mark - UICollectionViewDataSource
196
197 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
198 cellForItemAtIndexPath:(NSIndexPath*)indexPath {
199 UICollectionViewCell* cell =
200 [super collectionView:collectionView cellForItemAtIndexPath:indexPath];
201
202 if ([cell isMemberOfClass:[AutofillEditCell class]]) {
lpromero 2017/03/16 13:01:07 Optional nit: is isKindOfClass more appropriate? W
Moe 2017/03/16 15:44:23 Done.
203 AutofillEditCell* autofillEditCell =
204 base::mac::ObjCCast<AutofillEditCell>(cell);
205 autofillEditCell.textField.delegate = self;
206 }
207
208 NSInteger itemType =
209 [self.collectionViewModel itemTypeForIndexPath:indexPath];
210 switch (itemType) {
211 case ItemTypeErrorMessage: {
212 PaymentsTextCell* errorMessageCell =
213 base::mac::ObjCCastStrict<PaymentsTextCell>(cell);
214 errorMessageCell.textLabel.font = [MDCTypography body1Font];
215 errorMessageCell.textLabel.textColor =
216 [[MDCPalette cr_redPalette] tint600];
217 break;
218 }
219 default:
220 break;
221 }
222
223 return cell;
224 }
225
226 #pragma mark MDCCollectionViewStylingDelegate
227
228 - (CGFloat)collectionView:(UICollectionView*)collectionView
229 cellHeightAtIndexPath:(NSIndexPath*)indexPath {
230 CollectionViewItem* item =
231 [self.collectionViewModel itemAtIndexPath:indexPath];
232 switch (item.type) {
233 case ItemTypeErrorMessage:
234 return [MDCCollectionViewCell
235 cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds)
236 forItem:item];
237 default:
238 NOTREACHED();
lpromero 2017/03/16 13:01:06 How come we don't reach this section? The text fie
Moe 2017/03/16 15:44:22 That's the subclasses who decide what the item typ
239 return MDCCellDefaultOneLineHeight;
240 }
241 }
242
243 - (BOOL)collectionView:(UICollectionView*)collectionView
244 hidesInkViewAtIndexPath:(NSIndexPath*)indexPath {
245 NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath];
246 switch (type) {
247 case ItemTypeErrorMessage:
248 return YES;
249 default:
250 return NO;
251 }
252 }
253
254 #pragma mark - Protected methods
255
256 - (std::vector<EditorField>)editorFields {
257 return std::vector<EditorField>();
258 }
259
260 - (NSIndexPath*)indexPathForCurrentTextField {
lpromero 2017/03/16 13:01:07 This one is not protected, it is private.
Moe 2017/03/16 15:44:23 Done.
261 DCHECK(_currentEditingCell);
262 NSIndexPath* indexPath =
263 [[self collectionView] indexPathForCell:_currentEditingCell];
264 DCHECK(indexPath);
265 return indexPath;
266 }
267
268 #pragma mark - Helper methods
269
270 - (NSString*)validateValue:(NSString*)value
271 autofillType:(autofill::ServerFieldType)autofillType
272 required:(BOOL)required {
273 if (required && value.length == 0) {
274 return l10n_util::GetNSString(
275 IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE);
276 }
277 return @"";
278 }
279
280 - (void)addOrRemoveErrorMessage:(NSString*)errorMessage
281 inSectionWithIdentifier:(NSInteger)sectionIdentifier {
282 CollectionViewModel* model = self.collectionViewModel;
283 if ([model hasItemForItemType:ItemTypeErrorMessage
284 sectionIdentifier:sectionIdentifier]) {
285 [model removeItemWithType:ItemTypeErrorMessage
286 fromSectionWithIdentifier:sectionIdentifier];
287 }
288
289 if (errorMessage.length != 0) {
290 PaymentsTextItem* errorMessageItem =
291 [[PaymentsTextItem alloc] initWithType:ItemTypeErrorMessage];
292 errorMessageItem.text = errorMessage;
293 errorMessageItem.image = NativeImage(IDR_IOS_PAYMENTS_WARNING);
294 [model addItem:errorMessageItem toSectionWithIdentifier:sectionIdentifier];
295 }
296
297 // Update the entire section.
298 NSInteger section = [model sectionForSectionIdentifier:sectionIdentifier];
299 [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:section]];
lpromero 2017/03/16 13:01:07 Does that mean that the error message doesn't anim
Moe 2017/03/16 15:44:23 Done.
300 }
301
302 - (NSIndexPath*)indexPathWithSectionOffset:(NSInteger)offset
303 fromPath:(NSIndexPath*)indexPath {
304 DCHECK(indexPath);
305 DCHECK(offset);
306 NSInteger nextSection = [indexPath section] + offset;
307 if (nextSection >= 0 &&
308 nextSection < [[self collectionView] numberOfSections]) {
309 return [NSIndexPath indexPathForRow:[indexPath row] inSection:nextSection];
310 }
311 return nil;
312 }
313
314 - (AutofillEditCell*)nextTextFieldWithOffset:(NSInteger)offset {
315 UICollectionView* collectionView = [self collectionView];
316 NSIndexPath* currentCellPath = [self indexPathForCurrentTextField];
317 DCHECK(currentCellPath);
318 NSIndexPath* nextCellPath =
319 [self indexPathWithSectionOffset:offset fromPath:currentCellPath];
320 if (nextCellPath) {
321 id nextCell = [collectionView cellForItemAtIndexPath:nextCellPath];
322 if ([nextCell isMemberOfClass:[AutofillEditCell class]]) {
lpromero 2017/03/16 13:01:06 Idem, would isKindOfClass be better?
Moe 2017/03/16 15:44:23 Done.
323 return base::mac::ObjCCastStrict<AutofillEditCell>(
324 [collectionView cellForItemAtIndexPath:nextCellPath]);
325 }
326 }
327 return nil;
328 }
329
330 - (void)updateAccessoryViewButtonState {
331 AutofillEditCell* previousCell = [self nextTextFieldWithOffset:-1];
332 [[_accessoryView previousButton] setEnabled:previousCell != nil];
333
334 AutofillEditCell* nextCell = [self nextTextFieldWithOffset:1];
335 [[_accessoryView nextButton] setEnabled:nextCell != nil];
336 }
337
338 #pragma mark - Keyboard handling
339
340 - (void)keyboardDidShow {
lpromero 2017/03/16 13:01:06 Weird, I thought UICollectionViewController alread
341 [self.collectionView scrollRectToVisible:[_currentEditingCell frame]
lpromero 2017/03/16 13:01:06 No action needed: I wonder if using [self.collecti
Moe 2017/03/16 15:44:23 I copied this from autofill_edit_collection_view_c
lpromero 2017/03/16 16:08:08 Ok. For the up/down button, it doesn't apply here,
Moe 2017/03/21 04:22:14 true about the up/down buttons. If I'm understandi
342 animated:YES];
343 }
344
345 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698