Index: ios/chrome/browser/ui/payments/address_edit_mediator.mm |
diff --git a/ios/chrome/browser/ui/payments/address_edit_mediator.mm b/ios/chrome/browser/ui/payments/address_edit_mediator.mm |
index cbb1291afcf70085ad1be9c75681cf8e5e1d7547..33be542413ad53f32de0fb543eee11d84d7bfe2f 100644 |
--- a/ios/chrome/browser/ui/payments/address_edit_mediator.mm |
+++ b/ios/chrome/browser/ui/payments/address_edit_mediator.mm |
@@ -4,15 +4,39 @@ |
#import "ios/chrome/browser/ui/payments/address_edit_mediator.h" |
+#include <map> |
+#include <memory> |
+#include <string> |
+#include <utility> |
+ |
+#include "base/callback.h" |
+#include "base/memory/ptr_util.h" |
+#include "base/strings/sys_string_conversions.h" |
+#include "base/values.h" |
+#include "components/autofill/core/browser/autofill_address_util.h" |
+#include "components/autofill/core/browser/autofill_country.h" |
#include "components/autofill/core/browser/autofill_profile.h" |
+#include "components/autofill/core/browser/country_combobox_model.h" |
+#include "components/autofill/core/browser/field_types.h" |
+#include "components/autofill/core/browser/personal_data_manager.h" |
+#include "components/strings/grit/components_strings.h" |
+#include "ios/chrome/browser/application_context.h" |
#include "ios/chrome/browser/payments/payment_request.h" |
+#import "ios/chrome/browser/ui/autofill/autofill_ui_type.h" |
+#import "ios/chrome/browser/ui/autofill/autofill_ui_type_util.h" |
#import "ios/chrome/browser/ui/payments/payment_request_edit_consumer.h" |
+#import "ios/chrome/browser/ui/payments/payment_request_editor_field.h" |
+#include "ios/chrome/grit/ios_strings.h" |
+#include "third_party/libaddressinput/messages.h" |
+#include "ui/base/l10n/l10n_util.h" |
#if !defined(__has_feature) || !__has_feature(objc_arc) |
#error "This file requires ARC support." |
#endif |
-@interface AddressEditMediator () |
+@interface AddressEditMediator () { |
+ std::unique_ptr<RegionDataLoader> _regionDataLoader; |
+} |
// The PaymentRequest object owning an instance of web::PaymentRequest as |
// provided by the page invoking the Payment Request API. This is a weak |
@@ -23,14 +47,32 @@ |
// should outlive it. |
@property(nonatomic, assign) autofill::AutofillProfile* address; |
+// The map of autofill types to the cached editor fields. Helps reuse the editor |
+// fields and therefore maintain their existing values when the selected country |
+// changes and the editor fields get updated. |
+@property(nonatomic, strong) |
+ NSMutableDictionary<NSNumber*, EditorField*>* fieldsMap; |
+ |
+// The list of current editor fields. |
+@property(nonatomic, strong) NSMutableArray<EditorField*>* fields; |
+ |
+// The reference to the autofill::ADDRESS_HOME_STATE field, if any. |
+@property(nonatomic, strong) EditorField* regionField; |
+ |
@end |
@implementation AddressEditMediator |
@synthesize state = _state; |
@synthesize consumer = _consumer; |
+@synthesize countries = _countries; |
+@synthesize selectedCountryCode = _selectedCountryCode; |
+@synthesize regions = _regions; |
@synthesize paymentRequest = _paymentRequest; |
@synthesize address = _address; |
+@synthesize fieldsMap = _fieldsMap; |
+@synthesize fields = _fields; |
+@synthesize regionField = _regionField; |
- (instancetype)initWithPaymentRequest:(PaymentRequest*)paymentRequest |
address:(autofill::AutofillProfile*)address { |
@@ -40,6 +82,8 @@ |
_address = address; |
_state = |
_address ? EditViewControllerStateEdit : EditViewControllerStateCreate; |
+ _fieldsMap = [[NSMutableDictionary alloc] init]; |
+ [self loadCountries]; |
} |
return self; |
} |
@@ -48,7 +92,20 @@ |
- (void)setConsumer:(id<PaymentRequestEditConsumer>)consumer { |
_consumer = consumer; |
+ |
+ [self.consumer setEditorFields:[self createEditorFields]]; |
+ if (self.regionField) |
+ [self loadRegions]; |
+} |
+ |
+- (void)setSelectedCountryCode:(NSString*)selectedCountryCode { |
+ if (_selectedCountryCode == selectedCountryCode) |
+ return; |
+ _selectedCountryCode = selectedCountryCode; |
+ |
[self.consumer setEditorFields:[self createEditorFields]]; |
+ if (self.regionField) |
+ [self loadRegions]; |
} |
#pragma mark - CreditCardEditViewControllerDataSource |
@@ -61,10 +118,159 @@ |
return NO; |
} |
+#pragma mark - RegionDataLoaderConsumer |
+ |
+- (void)regionDataLoaderDidSucceedWithRegions: |
+ (NSMutableArray<NSString*>*)regions { |
+ self.regions = regions; |
+ // Notify the view controller asynchronously to allow for the view to update. |
+ __weak AddressEditMediator* weakSelf = self; |
+ dispatch_async(dispatch_get_main_queue(), ^{ |
+ [weakSelf.consumer setOptions:weakSelf.regions |
+ forEditorField:weakSelf.regionField]; |
+ }); |
+} |
+ |
#pragma mark - Helper methods |
+// Loads the country codes and names and sets the default selected country code. |
+- (void)loadCountries { |
+ autofill::CountryComboboxModel countryModel; |
+ countryModel.SetCountries(*_paymentRequest->GetPersonalDataManager(), |
+ base::Callback<bool(const std::string&)>(), |
+ GetApplicationContext()->GetApplicationLocale()); |
+ const autofill::CountryComboboxModel::CountryVector& countriesVector = |
+ countryModel.countries(); |
+ |
+ NSMutableDictionary<NSString*, NSString*>* countries = |
+ [[NSMutableDictionary alloc] |
+ initWithCapacity:static_cast<NSUInteger>(countriesVector.size())]; |
+ for (size_t i = 0; i < countriesVector.size(); ++i) { |
+ if (countriesVector[i].get()) { |
+ [countries setObject:base::SysUTF16ToNSString(countriesVector[i]->name()) |
+ forKey:base::SysUTF8ToNSString( |
+ countriesVector[i]->country_code())]; |
+ } |
+ } |
+ _countries = countries; |
+ _selectedCountryCode = |
+ base::SysUTF8ToNSString(countryModel.GetDefaultCountryCode()); |
+} |
+ |
+// Queries the region names based on the selected country code. |
+- (void)loadRegions { |
+ _regionDataLoader = base::MakeUnique<RegionDataLoader>(self); |
+ _regionDataLoader->LoadRegionData( |
+ base::SysNSStringToUTF8(self.selectedCountryCode), |
+ _paymentRequest->GetRegionDataLoader()); |
+} |
+ |
+// Returns an array of editor fields based on the selected country code. Caches |
+// the fields to be reused when the selected country code changes. |
- (NSArray<EditorField*>*)createEditorFields { |
- return @[]; |
+ self.fields = [[NSMutableArray alloc] init]; |
+ |
+ self.regionField = nil; |
+ |
+ base::ListValue addressComponents; |
+ std::string unused; |
+ autofill::GetAddressComponents( |
+ base::SysNSStringToUTF8(self.selectedCountryCode), |
+ GetApplicationContext()->GetApplicationLocale(), &addressComponents, |
+ &unused); |
+ |
+ for (size_t lineIndex = 0; lineIndex < addressComponents.GetSize(); |
+ ++lineIndex) { |
+ const base::ListValue* line = nullptr; |
+ if (!addressComponents.GetList(lineIndex, &line)) { |
+ NOTREACHED(); |
+ return @[]; |
+ } |
+ for (size_t componentIndex = 0; componentIndex < line->GetSize(); |
+ ++componentIndex) { |
+ const base::DictionaryValue* component = nullptr; |
+ if (!line->GetDictionary(componentIndex, &component)) { |
+ NOTREACHED(); |
+ return @[]; |
+ } |
+ |
+ std::string autofillType; |
+ if (!component->GetString(autofill::kFieldTypeKey, &autofillType)) { |
+ NOTREACHED(); |
+ return @[]; |
+ } |
+ AutofillUIType autofillUIType = AutofillUITypeFromAutofillType( |
+ autofill::GetFieldTypeFromString(autofillType)); |
+ |
+ NSNumber* fieldKey = [NSNumber numberWithInt:autofillUIType]; |
+ EditorField* field = self.fieldsMap[fieldKey]; |
+ if (!field) { |
+ BOOL required = autofillUIType != AutofillUITypeProfileCompanyName; |
+ field = |
+ [[EditorField alloc] initWithAutofillUIType:autofillUIType |
+ fieldType:EditorFieldTypeTextField |
+ label:nil |
+ value:nil |
+ required:required]; |
+ [self.fieldsMap setObject:field forKey:fieldKey]; |
+ } |
+ |
+ std::string fieldLabel; |
+ if (!component->GetString(autofill::kFieldNameKey, &fieldLabel)) { |
+ NOTREACHED(); |
+ return @[]; |
+ } |
+ field.label = base::SysUTF8ToNSString(fieldLabel); |
+ |
+ // Keep a reference to the field for the autofill::ADDRESS_HOME_STATE. Set |
+ // its value to "Loading..." and disable it until the regions are loaded. |
+ if (autofillUIType == AutofillUITypeProfileHomeAddressState) { |
+ self.regionField = field; |
+ field.value = l10n_util::GetNSString(IDS_AUTOFILL_LOADING_REGIONS); |
+ field.enabled = NO; |
+ } |
+ |
+ [self.fields addObject:field]; |
+ |
+ // Insert the country field right after the full name field. |
+ if (autofillUIType == AutofillUITypeProfileFullName) { |
+ NSNumber* countryFieldKey = |
+ [NSNumber numberWithInt:AutofillUITypeProfileHomeAddressCountry]; |
+ EditorField* field = self.fieldsMap[countryFieldKey]; |
+ if (!field) { |
+ NSString* label = l10n_util::GetNSString( |
+ IDS_LIBADDRESSINPUT_COUNTRY_OR_REGION_LABEL); |
+ field = [[EditorField alloc] |
+ initWithAutofillUIType:AutofillUITypeProfileHomeAddressCountry |
+ fieldType:EditorFieldTypeSelector |
+ label:label |
+ value:nil |
+ required:YES]; |
+ [self.fieldsMap setObject:field forKey:countryFieldKey]; |
+ } |
+ field.value = self.selectedCountryCode; |
+ field.displayValue = self.countries[self.selectedCountryCode]; |
+ [self.fields addObject:field]; |
+ } |
+ } |
+ } |
+ |
+ // Always add phone number field at the end. |
+ NSNumber* phoneNumberFieldKey = |
+ [NSNumber numberWithInt:AutofillUITypeProfileHomePhoneWholeNumber]; |
+ EditorField* field = self.fieldsMap[phoneNumberFieldKey]; |
+ if (!field) { |
+ field = [[EditorField alloc] |
+ initWithAutofillUIType:AutofillUITypeProfileHomePhoneWholeNumber |
+ fieldType:EditorFieldTypeTextField |
+ label:l10n_util::GetNSString(IDS_IOS_AUTOFILL_PHONE) |
+ value:nil |
+ required:YES]; |
+ [self.fieldsMap setObject:field forKey:phoneNumberFieldKey]; |
+ } |
+ [self.fields addObject:field]; |
+ |
+ return self.fields; |
} |
@end |