OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "ios/chrome/browser/ui/payments/address_edit_mediator.h" | 5 #import "ios/chrome/browser/ui/payments/address_edit_mediator.h" |
6 | 6 |
| 7 #include <map> |
| 8 #include <memory> |
| 9 #include <string> |
| 10 #include <utility> |
| 11 |
| 12 #include "base/callback.h" |
| 13 #include "base/memory/ptr_util.h" |
| 14 #include "base/strings/sys_string_conversions.h" |
| 15 #include "base/values.h" |
| 16 #include "components/autofill/core/browser/autofill_address_util.h" |
| 17 #include "components/autofill/core/browser/autofill_country.h" |
7 #include "components/autofill/core/browser/autofill_profile.h" | 18 #include "components/autofill/core/browser/autofill_profile.h" |
| 19 #include "components/autofill/core/browser/country_combobox_model.h" |
| 20 #include "components/autofill/core/browser/field_types.h" |
| 21 #include "components/autofill/core/browser/personal_data_manager.h" |
| 22 #include "components/strings/grit/components_strings.h" |
| 23 #include "ios/chrome/browser/application_context.h" |
8 #include "ios/chrome/browser/payments/payment_request.h" | 24 #include "ios/chrome/browser/payments/payment_request.h" |
| 25 #import "ios/chrome/browser/ui/autofill/autofill_ui_type.h" |
| 26 #import "ios/chrome/browser/ui/autofill/autofill_ui_type_util.h" |
9 #import "ios/chrome/browser/ui/payments/payment_request_edit_consumer.h" | 27 #import "ios/chrome/browser/ui/payments/payment_request_edit_consumer.h" |
| 28 #import "ios/chrome/browser/ui/payments/payment_request_editor_field.h" |
| 29 #include "ios/chrome/grit/ios_strings.h" |
| 30 #include "third_party/libaddressinput/messages.h" |
| 31 #include "ui/base/l10n/l10n_util.h" |
10 | 32 |
11 #if !defined(__has_feature) || !__has_feature(objc_arc) | 33 #if !defined(__has_feature) || !__has_feature(objc_arc) |
12 #error "This file requires ARC support." | 34 #error "This file requires ARC support." |
13 #endif | 35 #endif |
14 | 36 |
15 @interface AddressEditMediator () | 37 @interface AddressEditMediator () { |
| 38 std::unique_ptr<RegionDataLoader> _regionDataLoader; |
| 39 } |
16 | 40 |
17 // The PaymentRequest object owning an instance of web::PaymentRequest as | 41 // The PaymentRequest object owning an instance of web::PaymentRequest as |
18 // provided by the page invoking the Payment Request API. This is a weak | 42 // provided by the page invoking the Payment Request API. This is a weak |
19 // pointer and should outlive this class. | 43 // pointer and should outlive this class. |
20 @property(nonatomic, assign) PaymentRequest* paymentRequest; | 44 @property(nonatomic, assign) PaymentRequest* paymentRequest; |
21 | 45 |
22 // The address to be edited, if any. This pointer is not owned by this class and | 46 // The address to be edited, if any. This pointer is not owned by this class and |
23 // should outlive it. | 47 // should outlive it. |
24 @property(nonatomic, assign) autofill::AutofillProfile* address; | 48 @property(nonatomic, assign) autofill::AutofillProfile* address; |
25 | 49 |
| 50 // The map of autofill types to the cached editor fields. Helps reuse the editor |
| 51 // fields and therefore maintain their existing values when the selected country |
| 52 // changes and the editor fields get updated. |
| 53 @property(nonatomic, strong) |
| 54 NSMutableDictionary<NSNumber*, EditorField*>* fieldsMap; |
| 55 |
| 56 // The list of current editor fields. |
| 57 @property(nonatomic, strong) NSMutableArray<EditorField*>* fields; |
| 58 |
| 59 // The reference to the autofill::ADDRESS_HOME_STATE field, if any. |
| 60 @property(nonatomic, strong) EditorField* regionField; |
| 61 |
26 @end | 62 @end |
27 | 63 |
28 @implementation AddressEditMediator | 64 @implementation AddressEditMediator |
29 | 65 |
30 @synthesize state = _state; | 66 @synthesize state = _state; |
31 @synthesize consumer = _consumer; | 67 @synthesize consumer = _consumer; |
| 68 @synthesize countries = _countries; |
| 69 @synthesize selectedCountryCode = _selectedCountryCode; |
| 70 @synthesize regions = _regions; |
32 @synthesize paymentRequest = _paymentRequest; | 71 @synthesize paymentRequest = _paymentRequest; |
33 @synthesize address = _address; | 72 @synthesize address = _address; |
| 73 @synthesize fieldsMap = _fieldsMap; |
| 74 @synthesize fields = _fields; |
| 75 @synthesize regionField = _regionField; |
34 | 76 |
35 - (instancetype)initWithPaymentRequest:(PaymentRequest*)paymentRequest | 77 - (instancetype)initWithPaymentRequest:(PaymentRequest*)paymentRequest |
36 address:(autofill::AutofillProfile*)address { | 78 address:(autofill::AutofillProfile*)address { |
37 self = [super init]; | 79 self = [super init]; |
38 if (self) { | 80 if (self) { |
39 _paymentRequest = paymentRequest; | 81 _paymentRequest = paymentRequest; |
40 _address = address; | 82 _address = address; |
41 _state = | 83 _state = |
42 _address ? EditViewControllerStateEdit : EditViewControllerStateCreate; | 84 _address ? EditViewControllerStateEdit : EditViewControllerStateCreate; |
| 85 _fieldsMap = [[NSMutableDictionary alloc] init]; |
| 86 [self loadCountries]; |
43 } | 87 } |
44 return self; | 88 return self; |
45 } | 89 } |
46 | 90 |
47 #pragma mark - Setters | 91 #pragma mark - Setters |
48 | 92 |
49 - (void)setConsumer:(id<PaymentRequestEditConsumer>)consumer { | 93 - (void)setConsumer:(id<PaymentRequestEditConsumer>)consumer { |
50 _consumer = consumer; | 94 _consumer = consumer; |
| 95 |
51 [self.consumer setEditorFields:[self createEditorFields]]; | 96 [self.consumer setEditorFields:[self createEditorFields]]; |
| 97 if (self.regionField) |
| 98 [self loadRegions]; |
| 99 } |
| 100 |
| 101 - (void)setSelectedCountryCode:(NSString*)selectedCountryCode { |
| 102 if (_selectedCountryCode == selectedCountryCode) |
| 103 return; |
| 104 _selectedCountryCode = selectedCountryCode; |
| 105 |
| 106 [self.consumer setEditorFields:[self createEditorFields]]; |
| 107 if (self.regionField) |
| 108 [self loadRegions]; |
52 } | 109 } |
53 | 110 |
54 #pragma mark - CreditCardEditViewControllerDataSource | 111 #pragma mark - CreditCardEditViewControllerDataSource |
55 | 112 |
56 - (CollectionViewItem*)headerItem { | 113 - (CollectionViewItem*)headerItem { |
57 return nil; | 114 return nil; |
58 } | 115 } |
59 | 116 |
60 - (BOOL)shouldHideBackgroundForHeaderItem { | 117 - (BOOL)shouldHideBackgroundForHeaderItem { |
61 return NO; | 118 return NO; |
62 } | 119 } |
63 | 120 |
| 121 #pragma mark - RegionDataLoaderConsumer |
| 122 |
| 123 - (void)regionDataLoaderDidSucceedWithRegions: |
| 124 (NSMutableArray<NSString*>*)regions { |
| 125 self.regions = regions; |
| 126 // Notify the view controller asynchronously to allow for the view to update. |
| 127 __weak AddressEditMediator* weakSelf = self; |
| 128 dispatch_async(dispatch_get_main_queue(), ^{ |
| 129 [weakSelf.consumer setOptions:weakSelf.regions |
| 130 forEditorField:weakSelf.regionField]; |
| 131 }); |
| 132 } |
| 133 |
64 #pragma mark - Helper methods | 134 #pragma mark - Helper methods |
65 | 135 |
| 136 // Loads the country codes and names and sets the default selected country code. |
| 137 - (void)loadCountries { |
| 138 autofill::CountryComboboxModel countryModel; |
| 139 countryModel.SetCountries(*_paymentRequest->GetPersonalDataManager(), |
| 140 base::Callback<bool(const std::string&)>(), |
| 141 GetApplicationContext()->GetApplicationLocale()); |
| 142 const autofill::CountryComboboxModel::CountryVector& countriesVector = |
| 143 countryModel.countries(); |
| 144 |
| 145 NSMutableDictionary<NSString*, NSString*>* countries = |
| 146 [[NSMutableDictionary alloc] |
| 147 initWithCapacity:static_cast<NSUInteger>(countriesVector.size())]; |
| 148 for (size_t i = 0; i < countriesVector.size(); ++i) { |
| 149 if (countriesVector[i].get()) { |
| 150 [countries setObject:base::SysUTF16ToNSString(countriesVector[i]->name()) |
| 151 forKey:base::SysUTF8ToNSString( |
| 152 countriesVector[i]->country_code())]; |
| 153 } |
| 154 } |
| 155 _countries = countries; |
| 156 _selectedCountryCode = |
| 157 base::SysUTF8ToNSString(countryModel.GetDefaultCountryCode()); |
| 158 } |
| 159 |
| 160 // Queries the region names based on the selected country code. |
| 161 - (void)loadRegions { |
| 162 _regionDataLoader = base::MakeUnique<RegionDataLoader>(self); |
| 163 _regionDataLoader->LoadRegionData( |
| 164 base::SysNSStringToUTF8(self.selectedCountryCode), |
| 165 _paymentRequest->GetRegionDataLoader()); |
| 166 } |
| 167 |
| 168 // Returns an array of editor fields based on the selected country code. Caches |
| 169 // the fields to be reused when the selected country code changes. |
66 - (NSArray<EditorField*>*)createEditorFields { | 170 - (NSArray<EditorField*>*)createEditorFields { |
67 return @[]; | 171 self.fields = [[NSMutableArray alloc] init]; |
| 172 |
| 173 self.regionField = nil; |
| 174 |
| 175 base::ListValue addressComponents; |
| 176 std::string unused; |
| 177 autofill::GetAddressComponents( |
| 178 base::SysNSStringToUTF8(self.selectedCountryCode), |
| 179 GetApplicationContext()->GetApplicationLocale(), &addressComponents, |
| 180 &unused); |
| 181 |
| 182 for (size_t lineIndex = 0; lineIndex < addressComponents.GetSize(); |
| 183 ++lineIndex) { |
| 184 const base::ListValue* line = nullptr; |
| 185 if (!addressComponents.GetList(lineIndex, &line)) { |
| 186 NOTREACHED(); |
| 187 return @[]; |
| 188 } |
| 189 for (size_t componentIndex = 0; componentIndex < line->GetSize(); |
| 190 ++componentIndex) { |
| 191 const base::DictionaryValue* component = nullptr; |
| 192 if (!line->GetDictionary(componentIndex, &component)) { |
| 193 NOTREACHED(); |
| 194 return @[]; |
| 195 } |
| 196 |
| 197 std::string autofillType; |
| 198 if (!component->GetString(autofill::kFieldTypeKey, &autofillType)) { |
| 199 NOTREACHED(); |
| 200 return @[]; |
| 201 } |
| 202 AutofillUIType autofillUIType = AutofillUITypeFromAutofillType( |
| 203 autofill::GetFieldTypeFromString(autofillType)); |
| 204 |
| 205 NSNumber* fieldKey = [NSNumber numberWithInt:autofillUIType]; |
| 206 EditorField* field = self.fieldsMap[fieldKey]; |
| 207 if (!field) { |
| 208 BOOL required = autofillUIType != AutofillUITypeProfileCompanyName; |
| 209 field = |
| 210 [[EditorField alloc] initWithAutofillUIType:autofillUIType |
| 211 fieldType:EditorFieldTypeTextField |
| 212 label:nil |
| 213 value:nil |
| 214 required:required]; |
| 215 [self.fieldsMap setObject:field forKey:fieldKey]; |
| 216 } |
| 217 |
| 218 std::string fieldLabel; |
| 219 if (!component->GetString(autofill::kFieldNameKey, &fieldLabel)) { |
| 220 NOTREACHED(); |
| 221 return @[]; |
| 222 } |
| 223 field.label = base::SysUTF8ToNSString(fieldLabel); |
| 224 |
| 225 // Keep a reference to the field for the autofill::ADDRESS_HOME_STATE. Set |
| 226 // its value to "Loading..." and disable it until the regions are loaded. |
| 227 if (autofillUIType == AutofillUITypeProfileHomeAddressState) { |
| 228 self.regionField = field; |
| 229 field.value = l10n_util::GetNSString(IDS_AUTOFILL_LOADING_REGIONS); |
| 230 field.enabled = NO; |
| 231 } |
| 232 |
| 233 [self.fields addObject:field]; |
| 234 |
| 235 // Insert the country field right after the full name field. |
| 236 if (autofillUIType == AutofillUITypeProfileFullName) { |
| 237 NSNumber* countryFieldKey = |
| 238 [NSNumber numberWithInt:AutofillUITypeProfileHomeAddressCountry]; |
| 239 EditorField* field = self.fieldsMap[countryFieldKey]; |
| 240 if (!field) { |
| 241 NSString* label = l10n_util::GetNSString( |
| 242 IDS_LIBADDRESSINPUT_COUNTRY_OR_REGION_LABEL); |
| 243 field = [[EditorField alloc] |
| 244 initWithAutofillUIType:AutofillUITypeProfileHomeAddressCountry |
| 245 fieldType:EditorFieldTypeSelector |
| 246 label:label |
| 247 value:nil |
| 248 required:YES]; |
| 249 [self.fieldsMap setObject:field forKey:countryFieldKey]; |
| 250 } |
| 251 field.value = self.selectedCountryCode; |
| 252 field.displayValue = self.countries[self.selectedCountryCode]; |
| 253 [self.fields addObject:field]; |
| 254 } |
| 255 } |
| 256 } |
| 257 |
| 258 // Always add phone number field at the end. |
| 259 NSNumber* phoneNumberFieldKey = |
| 260 [NSNumber numberWithInt:AutofillUITypeProfileHomePhoneWholeNumber]; |
| 261 EditorField* field = self.fieldsMap[phoneNumberFieldKey]; |
| 262 if (!field) { |
| 263 field = [[EditorField alloc] |
| 264 initWithAutofillUIType:AutofillUITypeProfileHomePhoneWholeNumber |
| 265 fieldType:EditorFieldTypeTextField |
| 266 label:l10n_util::GetNSString(IDS_IOS_AUTOFILL_PHONE) |
| 267 value:nil |
| 268 required:YES]; |
| 269 [self.fieldsMap setObject:field forKey:phoneNumberFieldKey]; |
| 270 } |
| 271 [self.fields addObject:field]; |
| 272 |
| 273 return self.fields; |
68 } | 274 } |
69 | 275 |
70 @end | 276 @end |
OLD | NEW |