Commit 9fb7181f authored by mahmadi's avatar mahmadi Committed by Commit bot

Selected shipping option in payment summary view + shipping option selection view

BUG=602666

Review-Url: https://codereview.chromium.org/2621453002
Cr-Commit-Position: refs/heads/master@{#443226}
parent 2555903a
......@@ -963,6 +963,9 @@ Handoff must also be enabled in the General section of Settings, and your device
<message name="IDS_IOS_PAYMENT_REQUEST_SHIPPING_ADDRESS_SELECTION_TITLE" desc="Title of the view that allows the user to select the shipping address for satisfying a payment request [iOS only].">
Shipping Address
</message>
<message name="IDS_IOS_PAYMENT_REQUEST_SHIPPING_OPTION_SELECTION_TITLE" desc="Title of the view that allows the user to select the shipping option for satisfying a payment request [iOS only].">
Shipping Method
</message>
<message name="IDS_IOS_PAYMENT_REQUEST_SHIPPING_ADDRESS_SELECTION_ADD_BUTTON" desc="Label of the button to add a new shipping address [iOS only].">
Add Address...
</message>
......
......@@ -36,6 +36,10 @@ source_set("payments") {
"shipping_address_selection_coordinator.mm",
"shipping_address_selection_view_controller.h",
"shipping_address_selection_view_controller.mm",
"shipping_option_selection_coordinator.h",
"shipping_option_selection_coordinator.mm",
"shipping_option_selection_view_controller.h",
"shipping_option_selection_view_controller.mm",
]
deps = [
":injected_js",
......@@ -67,6 +71,8 @@ source_set("unit_tests") {
"payment_items_display_view_controller_unittest.mm",
"shipping_address_selection_coordinator_unittest.mm",
"shipping_address_selection_view_controller_unittest.mm",
"shipping_option_selection_coordinator_unittest.mm",
"shipping_option_selection_view_controller_unittest.mm",
]
deps = [
":payments",
......
......@@ -9,6 +9,7 @@
#include "base/mac/scoped_nsobject.h"
#include "base/strings/sys_string_conversions.h"
#include "components/autofill/core/browser/credit_card.h"
#import "ios/chrome/browser/payments/payment_request_utils.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_detail_item.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
#import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
......@@ -17,7 +18,6 @@
#include "ios/chrome/browser/ui/rtl_geometry.h"
#include "ios/chrome/grit/ios_strings.h"
#import "ios/third_party/material_components_ios/src/components/Buttons/src/MaterialButtons.h"
#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
#import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoFontLoader.h"
#include "ui/base/l10n/l10n_util.h"
......@@ -137,10 +137,6 @@ typedef NS_ENUM(NSInteger, ItemType) {
CollectionViewModel* model = self.collectionViewModel;
[model addSectionWithIdentifier:SectionIdentifierPayment];
NSNumberFormatter* currencyFormatter =
[[[NSNumberFormatter alloc] init] autorelease];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
// Add the total entry.
CollectionViewDetailItem* totalItem = [[[CollectionViewDetailItem alloc]
initWithType:ItemTypePaymentItemTotal] autorelease];
......@@ -149,8 +145,8 @@ typedef NS_ENUM(NSInteger, ItemType) {
NSString* currencyCode = base::SysUTF16ToNSString(_total.amount.currency);
NSDecimalNumber* value = [NSDecimalNumber
decimalNumberWithString:SysUTF16ToNSString(_total.amount.value)];
[currencyFormatter setCurrencyCode:currencyCode];
totalItem.detailText = [currencyFormatter stringFromNumber:value];
totalItem.detailText =
payment_request_utils::FormattedCurrencyString(value, currencyCode);
[model addItem:totalItem toSectionWithIdentifier:SectionIdentifierPayment];
......@@ -166,9 +162,8 @@ typedef NS_ENUM(NSInteger, ItemType) {
base::SysUTF16ToNSString(paymentItem.amount.currency);
NSDecimalNumber* value = [NSDecimalNumber
decimalNumberWithString:SysUTF16ToNSString(paymentItem.amount.value)];
[currencyFormatter setCurrencyCode:currencyCode];
paymentItemItem.detailText = [currencyFormatter stringFromNumber:value];
paymentItemItem.detailText =
payment_request_utils::FormattedCurrencyString(value, currencyCode);
[model addItem:paymentItemItem
toSectionWithIdentifier:SectionIdentifierPayment];
}
......
......@@ -19,9 +19,13 @@ class CreditCard;
@protocol PaymentMethodSelectionCoordinatorDelegate<NSObject>
// Notifies the delegate that the user has selected a payment method.
- (void)paymentMethodSelectionCoordinator:
(PaymentMethodSelectionCoordinator*)coordinator
selectedPaymentMethod:(autofill::CreditCard*)paymentMethod;
// Notifies the delegate that the user has chosen to return to the previous
// screen without making a selection.
- (void)paymentMethodSelectionCoordinatorDidReturn:
(PaymentMethodSelectionCoordinator*)coordinator;
......
......@@ -12,6 +12,7 @@
#import "ios/chrome/browser/payments/payment_method_selection_coordinator.h"
#import "ios/chrome/browser/payments/payment_request_view_controller.h"
#import "ios/chrome/browser/payments/shipping_address_selection_coordinator.h"
#import "ios/chrome/browser/payments/shipping_option_selection_coordinator.h"
#include "ios/web/public/payments/payment_request.h"
namespace autofill {
......@@ -34,7 +35,8 @@ class PersonalDataManager;
: ChromeCoordinator<PaymentRequestViewControllerDelegate,
PaymentItemsDisplayCoordinatorDelegate,
PaymentMethodSelectionCoordinatorDelegate,
ShippingAddressSelectionCoordinatorDelegate>
ShippingAddressSelectionCoordinatorDelegate,
ShippingOptionSelectionCoordinatorDelegate>
// Creates a Payment Request coordinator that will present UI on
// |viewController| using data available from |personalDataManager|.
......@@ -66,6 +68,10 @@ class PersonalDataManager;
@property(nonatomic, readonly)
autofill::AutofillProfile* selectedShippingAddress;
// The currently selected shipping option, if any.
@property(nonatomic, readonly)
web::PaymentShippingOption* selectedShippingOption;
// The payment method selected by the user, if any.
@property(nonatomic, readonly) autofill::CreditCard* selectedPaymentMethod;
......
......@@ -27,6 +27,8 @@
_itemsDisplayCoordinator;
base::scoped_nsobject<ShippingAddressSelectionCoordinator>
_shippingAddressSelectionCoordinator;
base::scoped_nsobject<ShippingOptionSelectionCoordinator>
_shippingOptionSelectionCoordinator;
base::scoped_nsobject<PaymentMethodSelectionCoordinator>
_methodSelectionCoordinator;
......@@ -46,6 +48,8 @@
@synthesize pageTitle = _pageTitle;
@synthesize pageHost = _pageHost;
@synthesize selectedShippingAddress = _selectedShippingAddress;
@synthesize selectedShippingOption = _selectedShippingOption;
@synthesize selectedPaymentMethod = _selectedPaymentMethod;
- (instancetype)initWithBaseViewController:(UIViewController*)baseViewController
......@@ -73,6 +77,16 @@
if (addresses.size() > 0)
_selectedShippingAddress = addresses[0];
for (size_t i = 0; i < _paymentRequest.details.shipping_options.size(); ++i) {
web::PaymentShippingOption* shippingOption =
&_paymentRequest.details.shipping_options[i];
if (shippingOption->selected) {
// If more than one option has |selected| set, the last one in the
// sequence should be treated as the selected item.
_selectedShippingOption = shippingOption;
}
}
const std::vector<autofill::CreditCard*> cards = [self supportedMethods];
if (cards.size() > 0)
_selectedPaymentMethod = cards[0];
......@@ -83,6 +97,7 @@
[_viewController setPageTitle:_pageTitle];
[_viewController setPageHost:_pageHost];
[_viewController setSelectedShippingAddress:_selectedShippingAddress];
[_viewController setSelectedShippingOption:_selectedShippingOption];
[_viewController setSelectedPaymentMethod:_selectedPaymentMethod];
[_viewController setDelegate:self];
[_viewController loadModel];
......@@ -100,6 +115,7 @@
[_navigationController dismissViewControllerAnimated:YES completion:nil];
_itemsDisplayCoordinator.reset();
_shippingAddressSelectionCoordinator.reset();
_shippingOptionSelectionCoordinator.reset();
_methodSelectionCoordinator.reset();
_navigationController.reset();
_viewController.reset();
......@@ -218,6 +234,26 @@
[_shippingAddressSelectionCoordinator start];
}
- (void)paymentRequestViewControllerSelectShippingOption {
_shippingOptionSelectionCoordinator.reset(
[[ShippingOptionSelectionCoordinator alloc]
initWithBaseViewController:_viewController]);
std::vector<web::PaymentShippingOption*> shippingOptions;
shippingOptions.reserve(_paymentRequest.details.shipping_options.size());
std::transform(std::begin(_paymentRequest.details.shipping_options),
std::end(_paymentRequest.details.shipping_options),
std::back_inserter(shippingOptions),
[](web::PaymentShippingOption& option) { return &option; });
[_shippingOptionSelectionCoordinator setShippingOptions:shippingOptions];
[_shippingOptionSelectionCoordinator
setSelectedShippingOption:_selectedShippingOption];
[_shippingOptionSelectionCoordinator setDelegate:self];
[_shippingOptionSelectionCoordinator start];
}
- (void)paymentRequestViewControllerSelectPaymentMethod {
_methodSelectionCoordinator.reset([[PaymentMethodSelectionCoordinator alloc]
initWithBaseViewController:_viewController]);
......@@ -260,6 +296,25 @@
_shippingAddressSelectionCoordinator.reset();
}
#pragma mark - ShippingOptionSelectionCoordinatorDelegate
- (void)shippingOptionSelectionCoordinator:
(ShippingOptionSelectionCoordinator*)coordinator
selectedShippingOption:
(web::PaymentShippingOption*)shippingOption {
_selectedShippingOption = shippingOption;
[_viewController updateSelectedShippingOption:shippingOption];
[_shippingOptionSelectionCoordinator stop];
_shippingOptionSelectionCoordinator.reset();
}
- (void)shippingOptionSelectionCoordinatorDidReturn:
(ShippingAddressSelectionCoordinator*)coordinator {
[_shippingOptionSelectionCoordinator stop];
_shippingOptionSelectionCoordinator.reset();
}
#pragma mark - PaymentMethodSelectionCoordinatorDelegate
- (void)paymentMethodSelectionCoordinator:
......
......@@ -16,6 +16,11 @@ namespace payment_request_utils {
// Helper function to get the shipping address label from an autofill profile.
NSString* AddressLabelFromAutofillProfile(autofill::AutofillProfile* profile);
// Helper function that returns a formatted currency string given the value and
// the currency code.
NSString* FormattedCurrencyString(NSDecimalNumber* value,
NSString* currencyCode);
} // namespace payment_request_utils
#endif // IOS_CHROME_BROWSER_PAYMENTS_PAYMENT_REQUEST_UTILS_H_
......@@ -26,4 +26,13 @@ NSString* AddressLabelFromAutofillProfile(autofill::AutofillProfile* profile) {
GetApplicationContext()->GetApplicationLocale()));
}
NSString* FormattedCurrencyString(NSDecimalNumber* value,
NSString* currencyCode) {
NSNumberFormatter* currencyFormatter =
[[[NSNumberFormatter alloc] init] autorelease];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[currencyFormatter setCurrencyCode:currencyCode];
return [currencyFormatter stringFromNumber:value];
}
} // namespace payment_request_utils
......@@ -20,6 +20,7 @@ class CreditCard;
- (void)paymentRequestViewControllerDidConfirm;
- (void)paymentRequestViewControllerDisplayPaymentItems;
- (void)paymentRequestViewControllerSelectShippingAddress;
- (void)paymentRequestViewControllerSelectShippingOption;
- (void)paymentRequestViewControllerSelectPaymentMethod;
@end
......@@ -44,6 +45,9 @@ class CreditCard;
// The currently selected and displayed shipping address, if any.
@property(nonatomic, assign) autofill::AutofillProfile* selectedShippingAddress;
// The currently selected and displayed shipping option, if any.
@property(nonatomic, assign) web::PaymentShippingOption* selectedShippingOption;
// The currently selected and displayed payment method, if any.
@property(nonatomic, assign) autofill::CreditCard* selectedPaymentMethod;
......@@ -54,6 +58,10 @@ class CreditCard;
- (void)updateSelectedShippingAddress:
(autofill::AutofillProfile*)shippingAddress;
// Sets the selected shipping option and updates the UI.
- (void)updateSelectedShippingOption:
(web::PaymentShippingOption*)shippingOption;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithStyle:(CollectionViewControllerStyle)style
......
......@@ -19,16 +19,17 @@
#import "ios/chrome/browser/payments/cells/payment_method_item.h"
#import "ios/chrome/browser/payments/cells/shipping_address_item.h"
#import "ios/chrome/browser/payments/payment_request_utils.h"
#import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrome.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_detail_item.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h"
#import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrome.h"
#import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
#include "ios/chrome/browser/ui/rtl_geometry.h"
#include "ios/chrome/grit/ios_strings.h"
#import "ios/third_party/material_components_ios/src/components/Buttons/src/MaterialButtons.h"
#import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
#import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoFontLoader.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
......@@ -54,6 +55,8 @@ typedef NS_ENUM(NSInteger, ItemType) {
ItemTypeShippingTitle,
ItemTypeShippingAddress,
ItemTypeAddShippingAddress,
ItemTypeShippingOption,
ItemTypeSelectShippingOption,
ItemTypePaymentTitle,
ItemTypePaymentMethod,
};
......@@ -66,6 +69,7 @@ typedef NS_ENUM(NSInteger, ItemType) {
base::scoped_nsobject<MDCFlatButton> _payButton;
ShippingAddressItem* _selectedShippingAddressItem;
CollectionViewTextItem* _selectedShippingOptionItem;
base::mac::ObjCPropertyReleaser
_propertyReleaser_PaymentRequestViewController;
......@@ -85,8 +89,9 @@ typedef NS_ENUM(NSInteger, ItemType) {
@synthesize pageFavicon = _pageFavicon;
@synthesize pageTitle = _pageTitle;
@synthesize pageHost = _pageHost;
@synthesize selectedPaymentMethod = _selectedPaymentMethod;
@synthesize selectedShippingAddress = _selectedShippingAddress;
@synthesize selectedShippingOption = _selectedShippingOption;
@synthesize selectedPaymentMethod = _selectedPaymentMethod;
- (instancetype)init {
if ((self = [super initWithStyle:CollectionViewControllerStyleAppBar])) {
......@@ -201,11 +206,8 @@ typedef NS_ENUM(NSInteger, ItemType) {
NSDecimalNumber* value = [NSDecimalNumber
decimalNumberWithString:SysUTF16ToNSString(
_paymentRequest.details.total.amount.value)];
NSNumberFormatter* currencyFormatter =
[[[NSNumberFormatter alloc] init] autorelease];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[currencyFormatter setCurrencyCode:currencyCode];
total.detailText = [currencyFormatter stringFromNumber:value];
total.detailText =
payment_request_utils::FormattedCurrencyString(value, currencyCode);
if (!_paymentRequest.details.display_items.empty()) {
total.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator;
total.accessibilityTraits |= UIAccessibilityTraitButton;
......@@ -221,11 +223,11 @@ typedef NS_ENUM(NSInteger, ItemType) {
[model setHeader:shippingTitle
forSectionWithIdentifier:SectionIdentifierShipping];
CollectionViewItem* shippingItem = nil;
CollectionViewItem* shippingAddressItem = nil;
if (_selectedShippingAddress) {
_selectedShippingAddressItem = [[[ShippingAddressItem alloc]
initWithType:ItemTypeShippingAddress] autorelease];
shippingItem = _selectedShippingAddressItem;
shippingAddressItem = _selectedShippingAddressItem;
[self fillShippingAddressItem:_selectedShippingAddressItem
withAddress:_selectedShippingAddress];
_selectedShippingAddressItem.accessoryType =
......@@ -235,15 +237,34 @@ typedef NS_ENUM(NSInteger, ItemType) {
CollectionViewDetailItem* addAddressItem =
[[[CollectionViewDetailItem alloc]
initWithType:ItemTypeAddShippingAddress] autorelease];
shippingItem = addAddressItem;
shippingAddressItem = addAddressItem;
addAddressItem.text = l10n_util::GetNSString(
IDS_IOS_PAYMENT_REQUEST_SHIPPING_ADDRESS_SELECTION_TITLE);
addAddressItem.detailText = [l10n_util::GetNSString(
IDS_IOS_PAYMENT_REQUEST_ADD_SHIPPING_ADDRESS_BUTTON)
uppercaseStringWithLocale:[NSLocale currentLocale]];
}
shippingItem.accessibilityTraits |= UIAccessibilityTraitButton;
[model addItem:shippingItem
shippingAddressItem.accessibilityTraits |= UIAccessibilityTraitButton;
[model addItem:shippingAddressItem
toSectionWithIdentifier:SectionIdentifierShipping];
CollectionViewTextItem* shippingOptionItem = nil;
if (_selectedShippingOption) {
_selectedShippingOptionItem = [[[CollectionViewTextItem alloc]
initWithType:ItemTypeShippingOption] autorelease];
shippingOptionItem = _selectedShippingOptionItem;
[self fillShippingOptionItem:_selectedShippingOptionItem
withOption:_selectedShippingOption];
} else {
shippingOptionItem = [[[CollectionViewTextItem alloc]
initWithType:ItemTypeSelectShippingOption] autorelease];
shippingOptionItem.text = l10n_util::GetNSString(
IDS_IOS_PAYMENT_REQUEST_SHIPPING_OPTION_SELECTION_TITLE);
}
shippingOptionItem.accessoryType =
MDCCollectionViewCellAccessoryDisclosureIndicator;
shippingOptionItem.accessibilityTraits |= UIAccessibilityTraitButton;
[model addItem:shippingOptionItem
toSectionWithIdentifier:SectionIdentifierShipping];
// Payment method section.
......@@ -312,6 +333,17 @@ typedef NS_ENUM(NSInteger, ItemType) {
[self.collectionView reloadItemsAtIndexPaths:@[ indexPath ]];
}
- (void)updateSelectedShippingOption:
(web::PaymentShippingOption*)shippingOption {
[self setSelectedShippingOption:shippingOption];
[self fillShippingOptionItem:_selectedShippingOptionItem
withOption:shippingOption];
NSIndexPath* indexPath =
[self.collectionViewModel indexPathForItem:_selectedShippingOptionItem
inSectionWithIdentifier:SectionIdentifierShipping];
[self.collectionView reloadItemsAtIndexPaths:@[ indexPath ]];
}
#pragma mark - Helper methods
- (void)fillShippingAddressItem:(ShippingAddressItem*)item
......@@ -324,7 +356,18 @@ typedef NS_ENUM(NSInteger, ItemType) {
address->GetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER));
}
- (void)fillShippingOptionItem:(CollectionViewTextItem*)item
withOption:(web::PaymentShippingOption*)option {
item.text = base::SysUTF16ToNSString(option->label);
NSString* currencyCode = base::SysUTF16ToNSString(option->amount.currency);
NSDecimalNumber* value = [NSDecimalNumber
decimalNumberWithString:SysUTF16ToNSString(option->amount.value)];
item.detailText =
payment_request_utils::FormattedCurrencyString(value, currencyCode);
}
#pragma mark UICollectionViewDataSource
- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
cellForItemAtIndexPath:(nonnull NSIndexPath*)indexPath {
UICollectionViewCell* cell =
......@@ -336,12 +379,20 @@ typedef NS_ENUM(NSInteger, ItemType) {
case ItemTypeAddShippingAddress: {
CollectionViewDetailCell* detailCell =
base::mac::ObjCCastStrict<CollectionViewDetailCell>(cell);
detailCell.detailTextLabel.font =
[[MDFRobotoFontLoader sharedInstance] mediumFontOfSize:14];
detailCell.detailTextLabel.font = [MDCTypography body2Font];
detailCell.detailTextLabel.textColor =
[[MDCPalette cr_bluePalette] tint700];
break;
}
case ItemTypeShippingOption: {
MDCCollectionViewTextCell* textCell =
base::mac::ObjCCastStrict<MDCCollectionViewTextCell>(cell);
textCell.textLabel.font = [MDCTypography body2Font];
textCell.textLabel.textColor = [[MDCPalette greyPalette] tint900];
textCell.detailTextLabel.font = [MDCTypography body1Font];
textCell.detailTextLabel.textColor = [[MDCPalette greyPalette] tint900];
break;
}
default:
break;
}
......@@ -365,6 +416,10 @@ typedef NS_ENUM(NSInteger, ItemType) {
case ItemTypeAddShippingAddress:
[_delegate paymentRequestViewControllerSelectShippingAddress];
break;
case ItemTypeShippingOption:
case ItemTypeSelectShippingOption:
[_delegate paymentRequestViewControllerSelectShippingOption];
break;
case ItemTypePaymentMethod:
[_delegate paymentRequestViewControllerSelectPaymentMethod];
break;
......@@ -386,6 +441,8 @@ typedef NS_ENUM(NSInteger, ItemType) {
return [MDCCollectionViewCell
cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds)
forItem:item];
} else if (type == ItemTypeShippingOption) {
return MDCCellDefaultTwoLineHeight;
} else {
return MDCCellDefaultOneLineHeight;
}
......
......@@ -18,10 +18,14 @@ class AutofillProfile;
@protocol ShippingAddressSelectionCoordinatorDelegate<NSObject>
// Notifies the delegate that the user has selected a shipping address.
- (void)shippingAddressSelectionCoordinator:
(ShippingAddressSelectionCoordinator*)coordinator
selectedShippingAddress:
(autofill::AutofillProfile*)shippingAddress;
// Notifies the delegate that the user has chosen to return to the previous
// screen without making a selection.
- (void)shippingAddressSelectionCoordinatorDidReturn:
(ShippingAddressSelectionCoordinator*)coordinator;
......
......@@ -36,4 +36,5 @@ TEST(ShippingAddressSelectionCoordinatorTest, StartAndStop) {
EXPECT_EQ(1u, [navigation_controller.viewControllers count]);
}
}
}
} // namespace
......@@ -140,24 +140,29 @@ typedef NS_ENUM(NSInteger, ItemType) {
CollectionViewModel* model = self.collectionViewModel;
NSInteger itemType =
[self.collectionViewModel itemTypeForIndexPath:indexPath];
CollectionViewItem* item =
[self.collectionViewModel itemAtIndexPath:indexPath];
NSInteger itemType = [model itemTypeForIndexPath:indexPath];
if (itemType == ItemTypeShippingAddress) {
NSInteger index = [model indexInItemTypeForIndexPath:indexPath];
DCHECK(index < (NSInteger)_shippingAddresses.size());
self.selectedShippingAddress = _shippingAddresses[index];
ShippingAddressItem* shippingAddressItem =
base::mac::ObjCCastStrict<ShippingAddressItem>(item);
shippingAddressItem.accessoryType = MDCCollectionViewCellAccessoryCheckmark;
_selectedItem.accessoryType = MDCCollectionViewCellAccessoryNone;
[self reconfigureCellsForItems:@[ _selectedItem, shippingAddressItem ]
inSectionWithIdentifier:SectionIdentifierShippingAddress];
_selectedItem = shippingAddressItem;
NSIndexPath* currentlySelectedIndexPath = [self.collectionViewModel
indexPathForItem:_selectedItem
inSectionWithIdentifier:SectionIdentifierShippingAddress];
if (currentlySelectedIndexPath != indexPath) {
// Update the cells.
CollectionViewItem* item = [model itemAtIndexPath:indexPath];
ShippingAddressItem* newlySelectedItem =
base::mac::ObjCCastStrict<ShippingAddressItem>(item);
newlySelectedItem.accessoryType = MDCCollectionViewCellAccessoryCheckmark;
_selectedItem.accessoryType = MDCCollectionViewCellAccessoryNone;
[self reconfigureCellsForItems:@[ _selectedItem, newlySelectedItem ]
inSectionWithIdentifier:SectionIdentifierShippingAddress];
// Update the selected shipping address and its respective item.
NSInteger index = [model indexInItemTypeForIndexPath:indexPath];
DCHECK(index < (NSInteger)_shippingAddresses.size());
self.selectedShippingAddress = _shippingAddresses[index];
_selectedItem = newlySelectedItem;
}
[_delegate
shippingAddressSelectionViewController:self
selectedShippingAddress:self.selectedShippingAddress];
......
// Copyright 2017 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.
#ifndef IOS_CHROME_BROWSER_PAYMENTS_SHIPPING_OPTION_SELECTION_COORDINATOR_H_
#define IOS_CHROME_BROWSER_PAYMENTS_SHIPPING_OPTION_SELECTION_COORDINATOR_H_
#import <UIKit/UIKit.h>
#include <vector>
#import "ios/chrome/browser/chrome_coordinator.h"
#include "ios/web/public/payments/payment_request.h"
@class ShippingOptionSelectionCoordinator;
@protocol ShippingOptionSelectionCoordinatorDelegate<NSObject>
// Notifies the delegate that the user has selected a shipping option.
- (void)shippingOptionSelectionCoordinator:
(ShippingOptionSelectionCoordinator*)coordinator
selectedShippingOption:
(web::PaymentShippingOption*)shippingOption;
// Notifies the delegate that the user has chosen to return to the previous
// screen without making a selection.
- (void)shippingOptionSelectionCoordinatorDidReturn:
(ShippingOptionSelectionCoordinator*)coordinator;
@end
// Coordinator responsible for creating and presenting the shipping option
// selection view controller. This view controller will be presented by the view
// controller provided in the initializer.
@interface ShippingOptionSelectionCoordinator : ChromeCoordinator
// The available shipping options to fulfill the payment request.
@property(nonatomic, assign) std::vector<web::PaymentShippingOption*>
shippingOptions;
// The shipping option selected by the user, if any.
@property(nonatomic, assign) web::PaymentShippingOption* selectedShippingOption;
// The delegate to be notified when the user selects a shipping option or
// returns without selecting one.
@property(nonatomic, weak) id<ShippingOptionSelectionCoordinatorDelegate>
delegate;
@end
#endif // IOS_CHROME_BROWSER_PAYMENTS_SHIPPING_OPTION_SELECTION_COORDINATOR_H_
// Copyright 2017 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.
#import "ios/chrome/browser/payments/shipping_option_selection_coordinator.h"
#import "base/ios/weak_nsobject.h"
#include "base/mac/scoped_nsobject.h"
#import "ios/chrome/browser/payments/shipping_option_selection_view_controller.h"
@interface ShippingOptionSelectionCoordinator ()<
ShippingOptionSelectionViewControllerDelegate> {
base::WeakNSProtocol<id<ShippingOptionSelectionCoordinatorDelegate>>
_delegate;
base::scoped_nsobject<ShippingOptionSelectionViewController> _viewController;
}
// Called when the user selects a shipping option. The cell is checked, the
// UI is locked so that the user can't interact with it, then the delegate is
// notified. The delay is here to let the user get a visual feedback of the
// selection before this view disappears.
- (void)delayedNotifyDelegateOfSelection;
@end
@implementation ShippingOptionSelectionCoordinator
@synthesize shippingOptions = _shippingOptions;
@synthesize selectedShippingOption = _selectedShippingOption;
- (id<ShippingOptionSelectionCoordinatorDelegate>)delegate {
return _delegate.get();
}
- (void)setDelegate:(id<ShippingOptionSelectionCoordinatorDelegate>)delegate {
_delegate.reset(delegate);
}
- (void)start {
_viewController.reset([[ShippingOptionSelectionViewController alloc] init]);
[_viewController setShippingOptions:_shippingOptions];
[_viewController setSelectedShippingOption:_selectedShippingOption];
[_viewController setDelegate:self];
[_viewController loadModel];
DCHECK(self.baseViewController.navigationController);
[self.baseViewController.navigationController
pushViewController:_viewController
animated:YES];
}
- (void)stop {
[self.baseViewController.navigationController popViewControllerAnimated:YES];
_viewController.reset();
}
#pragma mark - ShippingOptionSelectionViewControllerDelegate
- (void)shippingOptionSelectionViewController:
(ShippingOptionSelectionViewController*)controller
selectedShippingOption:
(web::PaymentShippingOption*)shippingOption {
_selectedShippingOption = shippingOption;
[self delayedNotifyDelegateOfSelection];
}
- (void)shippingOptionSelectionViewControllerDidReturn:
(ShippingOptionSelectionViewController*)controller {
[_delegate shippingOptionSelectionCoordinatorDidReturn:self];
}
- (void)delayedNotifyDelegateOfSelection {
_viewController.get().view.userInteractionEnabled = NO;
base::WeakNSObject<ShippingOptionSelectionCoordinator> weakSelf(self);
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.2 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
base::scoped_nsobject<ShippingOptionSelectionCoordinator> strongSelf(
[weakSelf retain]);
// Early return if the coordinator has been deallocated.
if (!strongSelf)
return;
_viewController.get().view.userInteractionEnabled = YES;
[_delegate shippingOptionSelectionCoordinator:self
selectedShippingOption:_selectedShippingOption];
});
}
@end
// Copyright 2017 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 "base/test/ios/wait_util.h"
#import "ios/chrome/browser/payments/shipping_option_selection_coordinator.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// Tests that invoking start and stop on the coordinator presents and dismisses
// the ShippingOptionSelectionViewController, respectively.
TEST(ShippingOptionSelectionCoordinatorTest, StartAndStop) {
@autoreleasepool {
UIViewController* base_view_controller =
[[[UIViewController alloc] init] autorelease];
UINavigationController* navigation_controller =
[[[UINavigationController alloc]
initWithRootViewController:base_view_controller] autorelease];
ShippingOptionSelectionCoordinator* coordinator =
[[[ShippingOptionSelectionCoordinator alloc]
initWithBaseViewController:base_view_controller] autorelease];
EXPECT_EQ(1u, [navigation_controller.viewControllers count]);
[coordinator start];
// Short delay to allow animation to complete.
base::test::ios::SpinRunLoopWithMaxDelay(
base::TimeDelta::FromSecondsD(1.0));
EXPECT_EQ(2u, [navigation_controller.viewControllers count]);
[coordinator stop];
// Short delay to allow animation to complete.
base::test::ios::SpinRunLoopWithMaxDelay(
base::TimeDelta::FromSecondsD(1.0));
EXPECT_EQ(1u, [navigation_controller.viewControllers count]);
}
}
} // namespace
// Copyright 2017 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.
#ifndef IOS_CHROME_BROWSER_PAYMENTS_SHIPPING_OPTION_SELECTION_VIEW_CONTROLLER_H_
#define IOS_CHROME_BROWSER_PAYMENTS_SHIPPING_OPTION_SELECTION_VIEW_CONTROLLER_H_
#import <UIKit/UIKit.h>
#import "ios/chrome/browser/ui/collection_view/collection_view_controller.h"
#include "ios/web/public/payments/payment_request.h"
@class ShippingOptionSelectionViewController;
@protocol ShippingOptionSelectionViewControllerDelegate<NSObject>
- (void)shippingOptionSelectionViewController:
(ShippingOptionSelectionViewController*)controller
selectedShippingOption:
(web::PaymentShippingOption*)shippingOption;
- (void)shippingOptionSelectionViewControllerDidReturn:
(ShippingOptionSelectionViewController*)controller;
@end
// View controller responsible for presenting the available shipping options
// for selection by the user and communicating their choice to the supplied
// delegate.
@interface ShippingOptionSelectionViewController : CollectionViewController
// The available shipping options to fulfill the payment request.
@property(nonatomic, assign) std::vector<web::PaymentShippingOption*>
shippingOptions;
// The shipping option selected by the user, if any.
@property(nonatomic, assign) web::PaymentShippingOption* selectedShippingOption;
// The delegate to be notified when the user selects a shipping option or
// returns without selecting one.
@property(nonatomic, weak) id<ShippingOptionSelectionViewControllerDelegate>
delegate;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithStyle:(CollectionViewControllerStyle)style
NS_UNAVAILABLE;
@end
#endif // IOS_CHROME_BROWSER_PAYMENTS_SHIPPING_OPTION_SELECTION_VIEW_CONTROLLER_H_
// Copyright 2017 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.
#import "ios/chrome/browser/payments/shipping_option_selection_view_controller.h"
#import "base/ios/weak_nsobject.h"
#include "base/mac/foundation_util.h"
#include "base/strings/sys_string_conversions.h"
#include "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/payments/payment_request_utils.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h"
#import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
#import "ios/chrome/browser/ui/icons/chrome_icon.h"
#include "ios/chrome/grit/ios_strings.h"
#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
#include "ui/base/l10n/l10n_util.h"
NSString* const kShippingOptionSelectionCollectionViewId =
@"kShippingOptionSelectionCollectionViewId";
namespace {
const CGFloat kSeparatorEdgeInset = 14;
typedef NS_ENUM(NSInteger, SectionIdentifier) {
SectionIdentifierShippingOption = kSectionIdentifierEnumZero,
};
typedef NS_ENUM(NSInteger, ItemType) {
ItemTypeShippingOption = kItemTypeEnumZero, // This is a repeated item type.
};
} // namespace
@interface ShippingOptionSelectionViewController () {
base::WeakNSProtocol<id<ShippingOptionSelectionViewControllerDelegate>>
_delegate;
CollectionViewTextItem* _selectedItem;
}
// Called when the user presses the return button.
- (void)onReturn;
@end
@implementation ShippingOptionSelectionViewController
@synthesize shippingOptions = _shippingOptions;
@synthesize selectedShippingOption = _selectedShippingOption;
- (instancetype)init {
if ((self = [super initWithStyle:CollectionViewControllerStyleAppBar])) {
self.title = l10n_util::GetNSString(
IDS_IOS_PAYMENT_REQUEST_SHIPPING_OPTION_SELECTION_TITLE);
UIBarButtonItem* returnButton =
[ChromeIcon templateBarButtonItemWithImage:[ChromeIcon backIcon]
target:nil
action:@selector(onReturn)];
returnButton.accessibilityLabel = l10n_util::GetNSString(IDS_ACCNAME_BACK);
self.navigationItem.leftBarButtonItem = returnButton;
}
return self;
}
- (id<ShippingOptionSelectionViewControllerDelegate>)delegate {
return _delegate.get();
}
- (void)setDelegate:
(id<ShippingOptionSelectionViewControllerDelegate>)delegate {
_delegate.reset(delegate);
}
- (void)onReturn {
[_delegate shippingOptionSelectionViewControllerDidReturn:self];
}
#pragma mark - CollectionViewController methods
- (void)loadModel {
[super loadModel];
CollectionViewModel* model = self.collectionViewModel;
[model addSectionWithIdentifier:SectionIdentifierShippingOption];
for (size_t i = 0; i < _shippingOptions.size(); ++i) {
web::PaymentShippingOption* shippingOption = _shippingOptions[i];
CollectionViewTextItem* item = [[[CollectionViewTextItem alloc]
initWithType:ItemTypeShippingOption] autorelease];
item.text = base::SysUTF16ToNSString(shippingOption->label);
NSString* currencyCode =
base::SysUTF16ToNSString(shippingOption->amount.currency);
NSDecimalNumber* value = [NSDecimalNumber
decimalNumberWithString:SysUTF16ToNSString(
shippingOption->amount.value)];
item.detailText =
payment_request_utils::FormattedCurrencyString(value, currencyCode);
if (_selectedShippingOption == shippingOption) {
item.accessoryType = MDCCollectionViewCellAccessoryCheckmark;
_selectedItem = item;
}
[model addItem:item
toSectionWithIdentifier:SectionIdentifierShippingOption];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
self.collectionView.accessibilityIdentifier =
kShippingOptionSelectionCollectionViewId;
// Customize collection view settings.
self.styler.cellStyle = MDCCollectionViewCellStyleCard;
self.styler.separatorInset =
UIEdgeInsetsMake(0, kSeparatorEdgeInset, 0, kSeparatorEdgeInset);
}
#pragma mark UICollectionViewDataSource
- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
cellForItemAtIndexPath:(nonnull NSIndexPath*)indexPath {
UICollectionViewCell* cell =
[super collectionView:collectionView cellForItemAtIndexPath:indexPath];
NSInteger itemType =
[self.collectionViewModel itemTypeForIndexPath:indexPath];
DCHECK(ItemTypeShippingOption == itemType);
MDCCollectionViewTextCell* textCell =
base::mac::ObjCCastStrict<MDCCollectionViewTextCell>(cell);
textCell.textLabel.font = [MDCTypography body2Font];
textCell.textLabel.textColor = [[MDCPalette greyPalette] tint900];
textCell.detailTextLabel.font = [MDCTypography body1Font];
textCell.detailTextLabel.textColor = [[MDCPalette greyPalette] tint900];
return cell;
}
#pragma mark UICollectionViewDelegate
- (void)collectionView:(UICollectionView*)collectionView
didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
[super collectionView:collectionView didSelectItemAtIndexPath:indexPath];
CollectionViewModel* model = self.collectionViewModel;
NSInteger itemType =
[self.collectionViewModel itemTypeForIndexPath:indexPath];
DCHECK(ItemTypeShippingOption == itemType);
NSIndexPath* currentlySelectedIndexPath = [self.collectionViewModel
indexPathForItem:_selectedItem
inSectionWithIdentifier:SectionIdentifierShippingOption];
if (currentlySelectedIndexPath != indexPath) {
// Update the cells.
CollectionViewItem* item = [model itemAtIndexPath:indexPath];
CollectionViewTextItem* newlySelectedItem =
base::mac::ObjCCastStrict<CollectionViewTextItem>(item);
newlySelectedItem.accessoryType = MDCCollectionViewCellAccessoryCheckmark;
_selectedItem.accessoryType = MDCCollectionViewCellAccessoryNone;
[self reconfigureCellsForItems:@[ _selectedItem, newlySelectedItem ]
inSectionWithIdentifier:SectionIdentifierShippingOption];
// Update the selected shipping option and its respective item.
NSInteger index = [model indexInItemTypeForIndexPath:indexPath];
DCHECK(index < (NSInteger)_shippingOptions.size());
self.selectedShippingOption = _shippingOptions[index];
_selectedItem = newlySelectedItem;
}
[_delegate shippingOptionSelectionViewController:self
selectedShippingOption:self.selectedShippingOption];
}
#pragma mark MDCCollectionViewStylingDelegate
- (CGFloat)collectionView:(UICollectionView*)collectionView
cellHeightAtIndexPath:(NSIndexPath*)indexPath {
return MDCCellDefaultTwoLineHeight;
}
@end
// Copyright 2017 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 <vector>
#include "base/mac/foundation_util.h"
#import "ios/chrome/browser/payments/shipping_option_selection_view_controller.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h"
#import "ios/chrome/browser/ui/collection_view/collection_view_controller_test.h"
#include "ios/chrome/grit/ios_strings.h"
#include "ios/web/public/payments/payment_request.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
class ShippingOptionSelectionViewControllerTest
: public CollectionViewControllerTest {
protected:
CollectionViewController* NewController() override NS_RETURNS_RETAINED {
return [[ShippingOptionSelectionViewController alloc] init];
}
ShippingOptionSelectionViewController* ShippingOptionSelectionController() {
return base::mac::ObjCCastStrict<ShippingOptionSelectionViewController>(
controller());
}
};
// Tests that the correct number of items are displayed after loading the model
// and that the correct item appears to be selected.
TEST_F(ShippingOptionSelectionViewControllerTest, TestModel) {
CreateController();
CheckController();
CheckTitleWithId(IDS_IOS_PAYMENT_REQUEST_SHIPPING_OPTION_SELECTION_TITLE);
std::unique_ptr<web::PaymentShippingOption> option1(
new web::PaymentShippingOption());
std::unique_ptr<web::PaymentShippingOption> option2(
new web::PaymentShippingOption());
std::vector<web::PaymentShippingOption*> shippingOptions;
shippingOptions.push_back(option1.get());
shippingOptions.push_back(option2.get());
[ShippingOptionSelectionController() setShippingOptions:shippingOptions];
[ShippingOptionSelectionController()
setSelectedShippingOption:shippingOptions[0]];
[ShippingOptionSelectionController() loadModel];
ASSERT_EQ(1, NumberOfSections());
// One item for each of shipping options.
EXPECT_EQ(shippingOptions.size(),
static_cast<unsigned int>(NumberOfItemsInSection(0)));
// The first option should appear to be selected.
CollectionViewTextItem* item = GetCollectionViewItem(0, 0);
EXPECT_EQ(MDCCollectionViewCellAccessoryCheckmark, item.accessoryType);
item = GetCollectionViewItem(0, 1);
EXPECT_EQ(MDCCollectionViewCellAccessoryNone, item.accessoryType);
}
} // namespace
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment