| Index: ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm
|
| diff --git a/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm b/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..04b6fd630ce80c6bd414a14ea1a38355f8472a20
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm
|
| @@ -0,0 +1,588 @@
|
| +// Copyright 2015 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.h"
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/ios/ios_util.h"
|
| +#include "base/location.h"
|
| +#include "base/mac/foundation_util.h"
|
| +#include "base/single_thread_task_runner.h"
|
| +#include "base/strings/sys_string_conversions.h"
|
| +#include "base/threading/thread_task_runner_handle.h"
|
| +#include "components/autofill/core/browser/ui/card_unmask_prompt_controller.h"
|
| +#include "components/strings/grit/components_strings.h"
|
| +#import "ios/chrome/browser/ui/autofill/cells/cvc_item.h"
|
| +#import "ios/chrome/browser/ui/autofill/cells/status_item.h"
|
| +#import "ios/chrome/browser/ui/autofill/cells/storage_switch_item.h"
|
| +#import "ios/chrome/browser/ui/autofill/storage_switch_tooltip.h"
|
| +#import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrome.h"
|
| +#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
|
| +#import "ios/chrome/browser/ui/collection_view/collection_view_controller.h"
|
| +#import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
|
| +#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
|
| +#import "ios/chrome/browser/ui/rtl_geometry.h"
|
| +#import "ios/third_party/material_components_ios/src/components/AppBar/src/MaterialAppBar.h"
|
| +#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
|
| +#include "ui/base/l10n/l10n_util.h"
|
| +
|
| +namespace {
|
| +
|
| +const CGFloat kTitleVerticalSpacing = 2.0f;
|
| +
|
| +typedef NS_ENUM(NSInteger, SectionIdentifier) {
|
| + SectionIdentifierMain = kSectionIdentifierEnumZero,
|
| +};
|
| +
|
| +typedef NS_ENUM(NSInteger, ItemType) {
|
| + ItemTypeCVC = kItemTypeEnumZero,
|
| + ItemTypeStatus,
|
| + ItemTypeStorageSwitch,
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +namespace autofill {
|
| +
|
| +#pragma mark CardUnmaskPromptViewBridge
|
| +
|
| +CardUnmaskPromptViewBridge::CardUnmaskPromptViewBridge(
|
| + CardUnmaskPromptController* controller)
|
| + : controller_(controller), weak_ptr_factory_(this) {
|
| + DCHECK(controller_);
|
| +}
|
| +
|
| +CardUnmaskPromptViewBridge::~CardUnmaskPromptViewBridge() {
|
| + if (controller_)
|
| + controller_->OnUnmaskDialogClosed();
|
| +}
|
| +
|
| +void CardUnmaskPromptViewBridge::Show() {
|
| + view_.reset([[CardUnmaskPromptViewIOS alloc] initWithBridge:this]);
|
| + // Present the view controller.
|
| + UIViewController* rootController =
|
| + [UIApplication sharedApplication].keyWindow.rootViewController;
|
| + [rootController presentViewController:view_ animated:YES completion:nil];
|
| +}
|
| +
|
| +void CardUnmaskPromptViewBridge::ControllerGone() {
|
| + controller_ = nullptr;
|
| + PerformClose();
|
| +}
|
| +
|
| +void CardUnmaskPromptViewBridge::DisableAndWaitForVerification() {
|
| + [view_ showSpinner];
|
| +}
|
| +
|
| +void CardUnmaskPromptViewBridge::GotVerificationResult(
|
| + const base::string16& error_message,
|
| + bool allow_retry) {
|
| + if (error_message.empty()) {
|
| + [view_ showSuccess];
|
| + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
|
| + FROM_HERE, base::Bind(&CardUnmaskPromptViewBridge::PerformClose,
|
| + weak_ptr_factory_.GetWeakPtr()),
|
| + controller_->GetSuccessMessageDuration());
|
| + } else {
|
| + if (allow_retry) {
|
| + [view_ showCVCInputFormWithError:SysUTF16ToNSString(error_message)];
|
| + } else {
|
| + [view_ showError:SysUTF16ToNSString(error_message)];
|
| + }
|
| + }
|
| +}
|
| +
|
| +CardUnmaskPromptController* CardUnmaskPromptViewBridge::GetController() {
|
| + return controller_;
|
| +}
|
| +
|
| +void CardUnmaskPromptViewBridge::PerformClose() {
|
| + [view_ dismissViewControllerAnimated:YES
|
| + completion:^{
|
| + this->DeleteSelf();
|
| + }];
|
| +}
|
| +
|
| +void CardUnmaskPromptViewBridge::DeleteSelf() {
|
| + delete this;
|
| +}
|
| +
|
| +} // autofill
|
| +
|
| +@interface CardUnmaskPromptViewIOS ()<UITextFieldDelegate> {
|
| + base::scoped_nsobject<UIBarButtonItem> _cancelButton;
|
| + base::scoped_nsobject<UIBarButtonItem> _verifyButton;
|
| + base::scoped_nsobject<CVCItem> _CVCItem;
|
| + base::scoped_nsobject<StatusItem> _statusItem;
|
| + base::scoped_nsobject<StorageSwitchItem> _storageSwitchItem;
|
| +
|
| + // The tooltip is added as a child of the collection view rather than the
|
| + // StorageSwitchContentView to allow it to overflow the bounds of the switch
|
| + // view.
|
| + base::scoped_nsobject<StorageSwitchTooltip> _storageSwitchTooltip;
|
| +
|
| + // Owns |self|.
|
| + autofill::CardUnmaskPromptViewBridge* _bridge; // weak
|
| +}
|
| +
|
| +@end
|
| +
|
| +@implementation CardUnmaskPromptViewIOS
|
| +
|
| +- (instancetype)initWithBridge:(autofill::CardUnmaskPromptViewBridge*)bridge {
|
| + DCHECK(bridge);
|
| + self = [super initWithStyle:CollectionViewControllerStyleAppBar];
|
| + if (self) {
|
| + _bridge = bridge;
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +- (void)viewDidLoad {
|
| + [super viewDidLoad];
|
| +
|
| + self.styler.cellStyle = MDCCollectionViewCellStyleCard;
|
| +
|
| + UILabel* titleLabel =
|
| + [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
|
| + titleLabel.text =
|
| + SysUTF16ToNSString(_bridge->GetController()->GetWindowTitle());
|
| + titleLabel.font = [UIFont boldSystemFontOfSize:16];
|
| + titleLabel.accessibilityTraits |= UIAccessibilityTraitHeader;
|
| + [titleLabel sizeToFit];
|
| +
|
| + UIView* titleView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
|
| + [titleView addSubview:titleLabel];
|
| + CGRect titleBounds = titleView.bounds;
|
| + titleBounds.origin.y -= kTitleVerticalSpacing;
|
| + titleView.bounds = titleBounds;
|
| + titleView.autoresizingMask = UIViewAutoresizingFlexibleLeadingMargin() |
|
| + UIViewAutoresizingFlexibleBottomMargin;
|
| + self.appBar.navigationBar.titleView = titleView;
|
| +
|
| + [self showCVCInputForm];
|
| +
|
| + // Add the navigation buttons.
|
| + _cancelButton.reset([[UIBarButtonItem alloc]
|
| + initWithTitle:l10n_util::GetNSString(IDS_CANCEL)
|
| + style:UIBarButtonItemStylePlain
|
| + target:self
|
| + action:@selector(onCancel:)]);
|
| + self.navigationItem.leftBarButtonItem = _cancelButton;
|
| +
|
| + NSString* verifyButtonText =
|
| + SysUTF16ToNSString(_bridge->GetController()->GetOkButtonLabel());
|
| + _verifyButton.reset([[UIBarButtonItem alloc]
|
| + initWithTitle:verifyButtonText
|
| + style:UIBarButtonItemStylePlain
|
| + target:self
|
| + action:@selector(onVerify:)]);
|
| + [_verifyButton setTitleTextAttributes:@{
|
| + NSForegroundColorAttributeName : [[MDCPalette cr_bluePalette] tint600]
|
| + }
|
| + forState:UIControlStateNormal];
|
| + [_verifyButton setTitleTextAttributes:@{
|
| + NSForegroundColorAttributeName : [UIColor lightGrayColor]
|
| + }
|
| + forState:UIControlStateDisabled];
|
| + [_verifyButton setEnabled:NO];
|
| + self.navigationItem.rightBarButtonItem = _verifyButton;
|
| +}
|
| +
|
| +- (void)viewWillLayoutSubviews {
|
| + [super viewWillLayoutSubviews];
|
| + NSIndexPath* CVCIndexPath =
|
| + [self.collectionViewModel indexPathForItem:_CVCItem
|
| + inSectionWithIdentifier:SectionIdentifierMain];
|
| + CVCCell* CVC = base::mac::ObjCCastStrict<CVCCell>(
|
| + [self.collectionView cellForItemAtIndexPath:CVCIndexPath]);
|
| + [self focusInputIfNeeded:CVC];
|
| +}
|
| +
|
| +#pragma mark - CollectionViewController
|
| +
|
| +- (void)loadModel {
|
| + [super loadModel];
|
| + CollectionViewModel* model = self.collectionViewModel;
|
| + [model addSectionWithIdentifier:SectionIdentifierMain];
|
| +
|
| + autofill::CardUnmaskPromptController* controller = _bridge->GetController();
|
| + NSString* instructions =
|
| + SysUTF16ToNSString(controller->GetInstructionsMessage());
|
| + int CVCImageResourceID = controller->GetCvcImageRid();
|
| + _CVCItem.reset([[CVCItem alloc] initWithType:ItemTypeCVC]);
|
| + _CVCItem.get().instructionsText = instructions;
|
| + _CVCItem.get().CVCImageResourceID = CVCImageResourceID;
|
| + [model addItem:_CVCItem toSectionWithIdentifier:SectionIdentifierMain];
|
| +
|
| + if (controller->CanStoreLocally()) {
|
| + _storageSwitchItem.reset(
|
| + [[StorageSwitchItem alloc] initWithType:ItemTypeStorageSwitch]);
|
| + _storageSwitchItem.get().on = controller->GetStoreLocallyStartState();
|
| + [model addItem:_storageSwitchItem
|
| + toSectionWithIdentifier:SectionIdentifierMain];
|
| +
|
| + _storageSwitchTooltip.reset([[StorageSwitchTooltip alloc] init]);
|
| + [_storageSwitchTooltip setHidden:YES];
|
| + [self.collectionView addSubview:_storageSwitchTooltip];
|
| + } else {
|
| + _storageSwitchItem.reset();
|
| + }
|
| +
|
| + // No status item when loading the model.
|
| + _statusItem.reset();
|
| +}
|
| +
|
| +#pragma mark - Private
|
| +
|
| +- (void)showCVCInputForm {
|
| + [self showCVCInputFormWithError:nil];
|
| +}
|
| +
|
| +- (void)showCVCInputFormWithError:(NSString*)errorMessage {
|
| + [_verifyButton setEnabled:NO];
|
| +
|
| + [self loadModel];
|
| + _CVCItem.get().errorMessage = errorMessage;
|
| + // If the server requested a new expiration date, show the date input. If it
|
| + // didn't and there was an error, show the "New card?" link which will show
|
| + // the date inputs on click. This link is intended to remind the user that
|
| + // they might have recently received a new card with updated expiration date
|
| + // and CVC. At the same time, we only put the CVC input in an error state if
|
| + // we're not requesting a new date. Because if we're asking the user for both,
|
| + // we don't know which is incorrect.
|
| + if (_bridge->GetController()->ShouldRequestExpirationDate()) {
|
| + _CVCItem.get().showDateInput = YES;
|
| + } else if (errorMessage) {
|
| + _CVCItem.get().showNewCardButton = YES;
|
| + _CVCItem.get().showCVCInputError = YES;
|
| + }
|
| +}
|
| +
|
| +- (void)showSpinner {
|
| + [_verifyButton setEnabled:NO];
|
| + [_storageSwitchTooltip setHidden:YES];
|
| +
|
| + [self
|
| + updateWithStatus:StatusItemState::VERIFYING
|
| + text:l10n_util::GetNSString(
|
| + IDS_AUTOFILL_CARD_UNMASK_VERIFICATION_IN_PROGRESS)];
|
| +}
|
| +
|
| +- (void)showSuccess {
|
| + [_verifyButton setEnabled:NO];
|
| +
|
| + [self updateWithStatus:StatusItemState::VERIFIED
|
| + text:l10n_util::GetNSString(
|
| + IDS_AUTOFILL_CARD_UNMASK_VERIFICATION_SUCCESS)];
|
| +}
|
| +
|
| +- (void)showError:(NSString*)errorMessage {
|
| + [_cancelButton setTitle:l10n_util::GetNSString(IDS_CLOSE)];
|
| + [_verifyButton setEnabled:NO];
|
| +
|
| + [self updateWithStatus:StatusItemState::ERROR text:errorMessage];
|
| +}
|
| +
|
| +- (void)updateWithStatus:(StatusItemState)state text:(NSString*)text {
|
| + if (!_statusItem) {
|
| + _statusItem.reset([[StatusItem alloc] initWithType:ItemTypeStatus]);
|
| + _statusItem.get().text = text;
|
| + _statusItem.get().state = state;
|
| + // Remove all the present items to replace them with the status item.
|
| + [self.collectionViewModel
|
| + removeSectionWithIdentifier:SectionIdentifierMain];
|
| + [self.collectionViewModel addSectionWithIdentifier:SectionIdentifierMain];
|
| + [self.collectionViewModel addItem:_statusItem
|
| + toSectionWithIdentifier:SectionIdentifierMain];
|
| + [self.collectionView reloadData];
|
| + } else {
|
| + _statusItem.get().text = text;
|
| + _statusItem.get().state = state;
|
| + [self reconfigureCellsForItems:@[ _statusItem.get() ]
|
| + inSectionWithIdentifier:SectionIdentifierMain];
|
| + [self.collectionViewLayout invalidateLayout];
|
| + }
|
| +}
|
| +
|
| +- (CGFloat)statusCellHeight {
|
| + const CGFloat collectionViewWidth =
|
| + CGRectGetWidth(self.collectionView.bounds);
|
| +
|
| + // The status cell replaces the previous content of the collection. So it is
|
| + // sized based on what appears when not loading.
|
| + const CGFloat preferredHeightForCVC =
|
| + [MDCCollectionViewCell cr_preferredHeightForWidth:collectionViewWidth
|
| + forItem:_CVCItem];
|
| + CGFloat preferredHeightForStorageSwitch = 0;
|
| + if (_storageSwitchItem) {
|
| + preferredHeightForStorageSwitch =
|
| + [MDCCollectionViewCell cr_preferredHeightForWidth:collectionViewWidth
|
| + forItem:_storageSwitchItem];
|
| + }
|
| + const CGFloat preferredHeightForStatus =
|
| + [MDCCollectionViewCell cr_preferredHeightForWidth:collectionViewWidth
|
| + forItem:_statusItem];
|
| + // Return the size of the replaced content, but make sure it is at least the
|
| + // minimal status cell height.
|
| + return MAX(preferredHeightForCVC + preferredHeightForStorageSwitch,
|
| + preferredHeightForStatus);
|
| +}
|
| +
|
| +- (void)layoutTooltipFromButton:(UIButton*)button {
|
| + const CGRect buttonFrameInCollectionView =
|
| + [self.collectionView convertRect:button.bounds fromView:button];
|
| + CGRect tooltipFrame = _storageSwitchTooltip.get().frame;
|
| +
|
| + // First, set the width and use sizeToFit to have the label flow the text and
|
| + // set the height appropriately.
|
| + const CGFloat kTooltipMargin = 8;
|
| + CGFloat availableWidth =
|
| + CGRectGetMinX(buttonFrameInCollectionView) - 2 * kTooltipMargin;
|
| + const CGFloat kMaxTooltipWidth = 210;
|
| + tooltipFrame.size.width = MIN(availableWidth, kMaxTooltipWidth);
|
| + _storageSwitchTooltip.get().frame = tooltipFrame;
|
| + [_storageSwitchTooltip sizeToFit];
|
| +
|
| + // Then use the size to position the tooltip appropriately, based on the
|
| + // button position.
|
| + tooltipFrame = _storageSwitchTooltip.get().frame;
|
| + tooltipFrame.origin.x = CGRectGetMinX(buttonFrameInCollectionView) -
|
| + kTooltipMargin - CGRectGetWidth(tooltipFrame);
|
| + tooltipFrame.origin.y = CGRectGetMaxY(buttonFrameInCollectionView) -
|
| + CGRectGetHeight(tooltipFrame);
|
| + _storageSwitchTooltip.get().frame = tooltipFrame;
|
| +}
|
| +
|
| +- (BOOL)inputCVCIsValid:(CVCItem*)item {
|
| + return _bridge->GetController()->InputCvcIsValid(
|
| + base::SysNSStringToUTF16(item.CVCText));
|
| +}
|
| +
|
| +- (BOOL)inputExpirationIsValid:(CVCItem*)item {
|
| + if (!item.showDateInput) {
|
| + return YES;
|
| + }
|
| +
|
| + return _bridge->GetController()->InputExpirationIsValid(
|
| + base::SysNSStringToUTF16(item.monthText),
|
| + base::SysNSStringToUTF16(item.yearText));
|
| +}
|
| +
|
| +- (void)inputsDidChange:(CVCItem*)item {
|
| + [_verifyButton setEnabled:[self inputCVCIsValid:item] &&
|
| + [self inputExpirationIsValid:item]];
|
| +}
|
| +
|
| +- (void)updateDateErrorState:(CVCItem*)item {
|
| + // Only change the error state if the inputs are of a length that can be
|
| + // interpreted as valid or not.
|
| + NSUInteger monthTextLength = item.monthText.length;
|
| + if (monthTextLength != 1 && monthTextLength != 2) {
|
| + return;
|
| + }
|
| + NSUInteger yearTextLength = item.yearText.length;
|
| + if (yearTextLength != 2 && yearTextLength != 4) {
|
| + return;
|
| + }
|
| +
|
| + if ([self inputExpirationIsValid:item]) {
|
| + item.showDateInputError = NO;
|
| + item.errorMessage = @"";
|
| + } else {
|
| + item.showDateInputError = NO;
|
| + item.errorMessage = l10n_util::GetNSString(
|
| + IDS_AUTOFILL_CARD_UNMASK_INVALID_EXPIRATION_DATE);
|
| + }
|
| +
|
| + [self reconfigureCellsForItems:@[ item ]
|
| + inSectionWithIdentifier:SectionIdentifierMain];
|
| + [self.collectionViewLayout invalidateLayout];
|
| +}
|
| +
|
| +- (void)focusInputIfNeeded:(CVCCell*)CVC {
|
| + // Focus the first visible input, unless the orientation is landscape. In
|
| + // landscape, the keyboard covers up the storage checkbox shown below this
|
| + // view and the user might never see it.
|
| + if (UIInterfaceOrientationIsPortrait(
|
| + [UIApplication sharedApplication].statusBarOrientation)) {
|
| + // Also check whether any of the inputs are already the first responder and
|
| + // are non-empty, in which case the focus should be left there.
|
| + if ((!CVC.monthInput.isFirstResponder || CVC.monthInput.text.length == 0) &&
|
| + (!CVC.yearInput.isFirstResponder || CVC.yearInput.text.length == 0) &&
|
| + (!CVC.CVCInput.isFirstResponder || CVC.CVCInput.text.length == 0)) {
|
| + if (_CVCItem.get().showDateInput) {
|
| + [CVC.monthInput becomeFirstResponder];
|
| + } else {
|
| + [CVC.CVCInput becomeFirstResponder];
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +#pragma mark - Actions
|
| +
|
| +- (void)onVerify:(id)sender {
|
| + autofill::CardUnmaskPromptController* controller = _bridge->GetController();
|
| + DCHECK(controller);
|
| +
|
| + // The controller requires a 4-digit year. Convert if necessary.
|
| + NSString* yearText = _CVCItem.get().yearText;
|
| + if (yearText.length == 2) {
|
| + NSInteger inputYear = yearText.integerValue;
|
| + NSInteger currentYear =
|
| + [[NSCalendar currentCalendar] components:NSCalendarUnitYear
|
| + fromDate:[NSDate date]]
|
| + .year;
|
| + inputYear += currentYear - (currentYear % 100);
|
| + yearText = [@(inputYear) stringValue];
|
| + }
|
| +
|
| + controller->OnUnmaskResponse(
|
| + base::SysNSStringToUTF16(_CVCItem.get().CVCText),
|
| + base::SysNSStringToUTF16(_CVCItem.get().monthText),
|
| + base::SysNSStringToUTF16(yearText), _storageSwitchItem.get().on);
|
| +}
|
| +
|
| +- (void)onCancel:(id)sender {
|
| + _bridge->PerformClose();
|
| +}
|
| +
|
| +- (void)onTooltipButtonTapped:(UIButton*)button {
|
| + BOOL shouldShowTooltip = !button.selected;
|
| + button.highlighted = shouldShowTooltip;
|
| + if (shouldShowTooltip) {
|
| + button.selected = YES;
|
| + [self layoutTooltipFromButton:button];
|
| + [_storageSwitchTooltip setHidden:NO];
|
| + } else {
|
| + button.selected = NO;
|
| + [_storageSwitchTooltip setHidden:YES];
|
| + }
|
| +}
|
| +
|
| +- (void)onStorageSwitchChanged:(UISwitch*)switchView {
|
| + // Update the item.
|
| + _storageSwitchItem.get().on = switchView.on;
|
| +}
|
| +
|
| +- (void)onNewCardLinkTapped:(UIButton*)button {
|
| + _bridge->GetController()->NewCardLinkClicked();
|
| + _CVCItem.get().instructionsText =
|
| + SysUTF16ToNSString(_bridge->GetController()->GetInstructionsMessage());
|
| + _CVCItem.get().monthText = @"";
|
| + _CVCItem.get().yearText = @"";
|
| + _CVCItem.get().CVCText = @"";
|
| + _CVCItem.get().errorMessage = @"";
|
| + _CVCItem.get().showDateInput = YES;
|
| + _CVCItem.get().showNewCardButton = NO;
|
| + _CVCItem.get().showDateInputError = NO;
|
| + _CVCItem.get().showCVCInputError = NO;
|
| +
|
| + [self reconfigureCellsForItems:@[ _CVCItem.get() ]
|
| + inSectionWithIdentifier:SectionIdentifierMain];
|
| + [self.collectionViewLayout invalidateLayout];
|
| +
|
| + [self inputsDidChange:_CVCItem];
|
| +}
|
| +
|
| +#pragma mark - UITextField Events
|
| +
|
| +- (void)monthInputDidChange:(UITextField*)textField {
|
| + _CVCItem.get().monthText = textField.text;
|
| + [self inputsDidChange:_CVCItem];
|
| + [self updateDateErrorState:_CVCItem];
|
| +}
|
| +
|
| +- (void)yearInputDidChange:(UITextField*)textField {
|
| + _CVCItem.get().yearText = textField.text;
|
| + [self inputsDidChange:_CVCItem];
|
| + [self updateDateErrorState:_CVCItem];
|
| +}
|
| +
|
| +- (void)CVCInputDidChange:(UITextField*)textField {
|
| + _CVCItem.get().CVCText = textField.text;
|
| + [self inputsDidChange:_CVCItem];
|
| + if (_bridge->GetController()->InputCvcIsValid(
|
| + base::SysNSStringToUTF16(textField.text))) {
|
| + _CVCItem.get().showCVCInputError = NO;
|
| + [self updateDateErrorState:_CVCItem];
|
| + }
|
| +}
|
| +
|
| +#pragma mark - MDCCollectionViewStylingDelegate
|
| +
|
| +- (CGFloat)collectionView:(UICollectionView*)collectionView
|
| + cellHeightAtIndexPath:(NSIndexPath*)indexPath {
|
| + CollectionViewItem* item =
|
| + [self.collectionViewModel itemAtIndexPath:indexPath];
|
| + if (item.type == ItemTypeStatus) {
|
| + return [self statusCellHeight];
|
| + }
|
| + return [MDCCollectionViewCell
|
| + cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds)
|
| + forItem:item];
|
| +}
|
| +
|
| +- (BOOL)collectionView:(UICollectionView*)collectionView
|
| + hidesInkViewAtIndexPath:(NSIndexPath*)indexPath {
|
| + return YES;
|
| +}
|
| +
|
| +#pragma mark - UICollectionViewDataSource
|
| +
|
| +- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
|
| + cellForItemAtIndexPath:(NSIndexPath*)indexPath {
|
| + UICollectionViewCell* cell =
|
| + [super collectionView:collectionView cellForItemAtIndexPath:indexPath];
|
| +
|
| + ItemType itemType = static_cast<ItemType>(
|
| + [self.collectionViewModel itemTypeForIndexPath:indexPath]);
|
| + switch (itemType) {
|
| + case ItemTypeCVC: {
|
| + CVCCell* cellForCVC = base::mac::ObjCCastStrict<CVCCell>(cell);
|
| + [cellForCVC.monthInput addTarget:self
|
| + action:@selector(monthInputDidChange:)
|
| + forControlEvents:UIControlEventEditingChanged];
|
| + [cellForCVC.yearInput addTarget:self
|
| + action:@selector(yearInputDidChange:)
|
| + forControlEvents:UIControlEventEditingChanged];
|
| + [cellForCVC.CVCInput addTarget:self
|
| + action:@selector(CVCInputDidChange:)
|
| + forControlEvents:UIControlEventEditingChanged];
|
| + [cellForCVC.buttonForNewCard addTarget:self
|
| + action:@selector(onNewCardLinkTapped:)
|
| + forControlEvents:UIControlEventTouchUpInside];
|
| + break;
|
| + }
|
| + case ItemTypeStorageSwitch: {
|
| + StorageSwitchCell* storageSwitchCell =
|
| + base::mac::ObjCCastStrict<StorageSwitchCell>(cell);
|
| + [storageSwitchCell.tooltipButton
|
| + addTarget:self
|
| + action:@selector(onTooltipButtonTapped:)
|
| + forControlEvents:UIControlEventTouchUpInside];
|
| + [storageSwitchCell.switchView addTarget:self
|
| + action:@selector(onStorageSwitchChanged:)
|
| + forControlEvents:UIControlEventValueChanged];
|
| + break;
|
| + }
|
| + default:
|
| + break;
|
| + }
|
| + return cell;
|
| +}
|
| +
|
| +#pragma mark - UICollectionViewDelegate
|
| +
|
| +- (void)collectionView:(UICollectionView*)collectionView
|
| + willDisplayCell:(UICollectionViewCell*)cell
|
| + forItemAtIndexPath:(NSIndexPath*)indexPath {
|
| + CVCCell* CVC = base::mac::ObjCCast<CVCCell>(cell);
|
| + if (CVC) {
|
| + [self focusInputIfNeeded:CVC];
|
| + }
|
| +}
|
| +
|
| +@end
|
|
|