@chen834921478/react-native-select-contact 1.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/.github/workflows/npm-publish.yml +33 -0
  2. package/CHANGELOG.md +57 -0
  3. package/RCTSelectContact.podspec +23 -0
  4. package/README.md +177 -0
  5. package/android/build.gradle +52 -0
  6. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  7. package/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  8. package/android/gradlew +188 -0
  9. package/android/gradlew.bat +100 -0
  10. package/android/keystores/BUCK +8 -0
  11. package/android/keystores/debug.keystore.properties +4 -0
  12. package/android/src/main/AndroidManifest.xml +6 -0
  13. package/android/src/main/java/com/streem/selectcontact/SelectContactModule.java +271 -0
  14. package/android/src/main/java/com/streem/selectcontact/SelectContactPackage.java +25 -0
  15. package/android/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  16. package/android/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  17. package/android/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  18. package/android/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  19. package/android/src/main/res/values/strings.xml +3 -0
  20. package/android/src/main/res/values/styles.xml +8 -0
  21. package/index.js +204 -0
  22. package/ios/RCTSelectContact/RCTSelectContact.h +13 -0
  23. package/ios/RCTSelectContact/RCTSelectContact.m +107 -0
  24. package/ios/SelectContact.xcodeproj/project.pbxproj +291 -0
  25. package/ios/SelectContact.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  26. package/ios/SelectContact.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  27. package/ios/SelectContact.xcodeproj/project.xcworkspace/xcuserdata/oliverjacobs.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  28. package/ios/SelectContact.xcodeproj/project.xcworkspace/xcuserdata/seanadkinson.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  29. package/ios/SelectContact.xcodeproj/xcshareddata/xcschemes/SelectContact.xcscheme +115 -0
  30. package/ios/SelectContact.xcodeproj/xcuserdata/oliverjacobs.xcuserdatad/xcschemes/RCTContactsWrapper.xcscheme +80 -0
  31. package/ios/SelectContact.xcodeproj/xcuserdata/oliverjacobs.xcuserdatad/xcschemes/xcschememanagement.plist +37 -0
  32. package/ios/SelectContact.xcodeproj/xcuserdata/seanadkinson.xcuserdatad/xcschemes/RCTSelectContact.xcscheme +80 -0
  33. package/ios/SelectContact.xcodeproj/xcuserdata/seanadkinson.xcuserdatad/xcschemes/xcschememanagement.plist +27 -0
  34. package/license.txt +21 -0
  35. package/package.json +44 -0
  36. package/react-native-select-contact.podspec +19 -0
  37. package/selectContact.d.ts +62 -0
@@ -0,0 +1,271 @@
1
+ package com.streem.selectcontact;
2
+
3
+ import android.app.Activity;
4
+ import android.content.ContentResolver;
5
+ import android.content.Intent;
6
+ import android.database.Cursor;
7
+ import android.net.Uri;
8
+ import android.provider.ContactsContract;
9
+ import android.provider.ContactsContract.CommonDataKinds.Email;
10
+ import android.provider.ContactsContract.CommonDataKinds.Phone;
11
+ import android.provider.ContactsContract.CommonDataKinds.StructuredName;
12
+ import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
13
+ import android.provider.ContactsContract.Contacts;
14
+ import android.provider.ContactsContract.Contacts.Entity;
15
+ import android.util.Log;
16
+
17
+ import com.facebook.react.bridge.ActivityEventListener;
18
+ import com.facebook.react.bridge.Arguments;
19
+ import com.facebook.react.bridge.Promise;
20
+ import com.facebook.react.bridge.ReactApplicationContext;
21
+ import com.facebook.react.bridge.ReactContextBaseJavaModule;
22
+ import com.facebook.react.bridge.ReactMethod;
23
+ import com.facebook.react.bridge.WritableArray;
24
+ import com.facebook.react.bridge.WritableMap;
25
+
26
+ public class SelectContactModule extends ReactContextBaseJavaModule implements ActivityEventListener {
27
+
28
+ private static final String TAG = "SelectContactModule";
29
+ private static final int CONTACT_REQUEST = 11112;
30
+ public static final String E_CONTACT_CANCELLED = "E_CONTACT_CANCELLED";
31
+ public static final String E_CONTACT_NO_DATA = "E_CONTACT_NO_DATA";
32
+ public static final String E_CONTACT_EXCEPTION = "E_CONTACT_EXCEPTION";
33
+ public static final String E_CONTACT_PERMISSION = "E_CONTACT_PERMISSION";
34
+ private Promise mContactsPromise;
35
+ private final ContentResolver contentResolver;
36
+
37
+ public SelectContactModule(ReactApplicationContext reactContext) {
38
+ super(reactContext);
39
+ this.contentResolver = getReactApplicationContext().getContentResolver();
40
+ reactContext.addActivityEventListener(this);
41
+ }
42
+
43
+ @Override
44
+ public String getName() {
45
+ return "SelectContact";
46
+ }
47
+
48
+
49
+ @ReactMethod
50
+ public void openContactSelection(Promise contactsPromise) {
51
+ launchPicker(contactsPromise, CONTACT_REQUEST);
52
+ }
53
+
54
+ /**
55
+ * Lanch the contact picker, with the specified requestCode for returned data.
56
+ *
57
+ * @param contactsPromise - promise passed in from React Native.
58
+ * @param requestCode - request code to specify what contact data to return
59
+ */
60
+ private void launchPicker(Promise contactsPromise, int requestCode) {
61
+ mContactsPromise = contactsPromise;
62
+
63
+ Intent intent = new Intent(Intent.ACTION_PICK,ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
64
+ // intent.setType(Contacts.CONTENT_TYPE);
65
+ Activity activity = getCurrentActivity();
66
+ if (intent.resolveActivity(activity.getPackageManager()) != null) {
67
+ activity.startActivityForResult(intent, requestCode);
68
+ }
69
+
70
+ }
71
+
72
+ @Override
73
+ public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) {
74
+ if (mContactsPromise == null || requestCode != CONTACT_REQUEST) {
75
+ return;
76
+ }
77
+
78
+ //Request was cancelled
79
+ if (resultCode != Activity.RESULT_OK) {
80
+ mContactsPromise.reject(E_CONTACT_CANCELLED, "Cancelled");
81
+ return;
82
+ }
83
+
84
+ // Retrieve all possible data about contact and return as a JS object
85
+ WritableMap contactData = Arguments.createMap();
86
+
87
+ try {
88
+ // String id = getContactId(intent.getData());
89
+ // contactData.putString("recordId", id);
90
+ // Uri contactUri = buildContactUri(id);
91
+ boolean foundData = false;
92
+
93
+ WritableArray phones = Arguments.createArray();
94
+ WritableArray emails = Arguments.createArray();
95
+ WritableArray postalAddresses = Arguments.createArray();
96
+
97
+
98
+
99
+ Cursor cursor = activity.getContentResolver().query(intent.getData(), null, null, null, null);
100
+ if (cursor != null && cursor.moveToFirst()) {
101
+
102
+ int any=cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
103
+ String number = cursor.getString(any);
104
+ String fullName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
105
+ foundData= true;
106
+ addPhoneEntry(phones, cursor, activity);
107
+ contactData.putArray("phones", phones);
108
+ contactData.putString("phone", number);
109
+ contactData.putString("name", fullName);
110
+
111
+ }
112
+ cursor.close();
113
+
114
+
115
+ // contactData.putArray("postalAddresses", postalAddresses);
116
+
117
+ if (foundData) {
118
+ mContactsPromise.resolve(contactData);
119
+ } else {
120
+ mContactsPromise.reject(E_CONTACT_NO_DATA, "No data found for contact");
121
+ }
122
+ // } catch (SelectContactException e) {
123
+ // mContactsPromise.reject(E_CONTACT_EXCEPTION, e.getMessage());
124
+ } catch (Exception e) {
125
+ Log.e(TAG, "Unexpected exception reading from contacts", e);
126
+ mContactsPromise.reject(E_CONTACT_EXCEPTION, e.getMessage());
127
+ }
128
+ }
129
+
130
+ private String getContactId(Uri contactUri) throws SelectContactException {
131
+ Cursor cursor = this.contentResolver.query(contactUri, null, null, null, null);
132
+ if (cursor == null || !cursor.moveToFirst()) {
133
+ throw new SelectContactException(E_CONTACT_NO_DATA, "Contact Data Not Found");
134
+ }
135
+
136
+ return cursor.getString(cursor.getColumnIndex(Contacts._ID));
137
+ }
138
+
139
+ private Uri buildContactUri(String id) {
140
+ return Uri
141
+ .withAppendedPath(Contacts.CONTENT_URI, id)
142
+ .buildUpon()
143
+ .appendPath(Entity.CONTENT_DIRECTORY)
144
+ .build();
145
+ }
146
+
147
+ private Cursor openContactQuery(Uri contactUri) throws SelectContactException {
148
+ String[] projection = {
149
+ Entity.MIMETYPE,
150
+ Entity.DATA1,
151
+ Entity.DATA2,
152
+ Entity.DATA3
153
+ };
154
+ String sortOrder = Entity.RAW_CONTACT_ID + " ASC";
155
+ Cursor cursor = this.contentResolver.query(contactUri, projection, null, null, sortOrder);
156
+ if (cursor == null) {
157
+ throw new SelectContactException(E_CONTACT_EXCEPTION, "Could not query contacts data. Unable to create cursor.");
158
+ }
159
+
160
+ return cursor;
161
+ }
162
+
163
+ private void addNameData(WritableMap contactData, Cursor cursor) {
164
+ int displayNameIndex = cursor.getColumnIndex(StructuredName.DISPLAY_NAME);
165
+ contactData.putString("name", cursor.getString(displayNameIndex));
166
+
167
+ int givenNameColumn = cursor.getColumnIndex(StructuredName.GIVEN_NAME);
168
+ if (givenNameColumn != -1) {
169
+ String givenName = cursor.getString(givenNameColumn);
170
+ contactData.putString("givenName", givenName);
171
+ }
172
+
173
+ int familyNameColumn = cursor.getColumnIndex(StructuredName.FAMILY_NAME);
174
+ if (familyNameColumn != -1) {
175
+ String familyName = cursor.getString(cursor.getColumnIndex(StructuredName.FAMILY_NAME));
176
+ contactData.putString("familyName", familyName);
177
+ }
178
+
179
+ int middleNameColumn = cursor.getColumnIndex(StructuredName.MIDDLE_NAME);
180
+ if (middleNameColumn != -1) {
181
+ String middleName = cursor.getString(middleNameColumn);
182
+ contactData.putString("middleName", middleName);
183
+ }
184
+ }
185
+
186
+ private void addPostalData(WritableArray postalAddresses, Cursor cursor, Activity activity) {
187
+ // we need to see if the postal address columns exist, if so, add them
188
+ int formattedAddressColumn = cursor.getColumnIndex(StructuredPostal.FORMATTED_ADDRESS);
189
+ int streetColumn = cursor.getColumnIndex(StructuredPostal.STREET);
190
+ int cityColumn = cursor.getColumnIndex(StructuredPostal.CITY);
191
+ int stateColumn = cursor.getColumnIndex(StructuredPostal.REGION);
192
+ int postalCodeColumn = cursor.getColumnIndex(StructuredPostal.POSTCODE);
193
+ int isoCountryCodeColumn = cursor.getColumnIndex(StructuredPostal.COUNTRY);
194
+
195
+ WritableMap addressEntry = Arguments.createMap();
196
+ if (formattedAddressColumn != -1) {
197
+ addressEntry.putString("formattedAddress", cursor.getString(formattedAddressColumn));
198
+ }
199
+ if (streetColumn != -1) {
200
+ addressEntry.putString("street", cursor.getString(streetColumn));
201
+ }
202
+ if (cityColumn != -1) {
203
+ addressEntry.putString("city", cursor.getString(cityColumn));
204
+ }
205
+ if (stateColumn != -1) {
206
+ addressEntry.putString("state", cursor.getString(stateColumn));
207
+ }
208
+ if (postalCodeColumn != -1) {
209
+ addressEntry.putString("postalCode", cursor.getString(postalCodeColumn));
210
+ }
211
+ if (isoCountryCodeColumn != -1) {
212
+ addressEntry.putString("isoCountryCode", cursor.getString(isoCountryCodeColumn));
213
+ }
214
+
215
+ // add the address type here
216
+ int addressTypeColumn = cursor.getColumnIndex(StructuredPostal.TYPE);
217
+ int addressLabelColumn = cursor.getColumnIndex(StructuredPostal.LABEL);
218
+ if (addressTypeColumn != -1 && addressLabelColumn != -1) {
219
+ String addressLabel = cursor.getString(addressLabelColumn);
220
+ int addressType = cursor.getInt(addressTypeColumn);
221
+ CharSequence typeLabel = StructuredPostal.getTypeLabel(activity.getResources(), addressType, addressLabel);
222
+ addressEntry.putString("type", String.valueOf(typeLabel));
223
+ }
224
+
225
+ postalAddresses.pushMap(addressEntry);
226
+ }
227
+
228
+ private void addPhoneEntry(WritableArray phones, Cursor cursor, Activity activity) {
229
+ String phoneNumber = cursor.getString(cursor.getColumnIndex(Phone.NUMBER));
230
+ int phoneType = cursor.getInt(cursor.getColumnIndex(Phone.TYPE));
231
+ String phoneLabel = cursor.getString(cursor.getColumnIndex(Phone.LABEL));
232
+ CharSequence typeLabel = Phone.getTypeLabel(activity.getResources(), phoneType, phoneLabel);
233
+
234
+ WritableMap phoneEntry = Arguments.createMap();
235
+ phoneEntry.putString("number", phoneNumber);
236
+ phoneEntry.putString("type", String.valueOf(typeLabel));
237
+
238
+ phones.pushMap(phoneEntry);
239
+ }
240
+
241
+ private void addEmailEntry(WritableArray emails, Cursor cursor, Activity activity) {
242
+ String emailAddress = cursor.getString(cursor.getColumnIndex(Email.ADDRESS));
243
+ int emailType = cursor.getInt(cursor.getColumnIndex(Email.TYPE));
244
+ String emailLabel = cursor.getString(cursor.getColumnIndex(Email.LABEL));
245
+ CharSequence typeLabel = Email.getTypeLabel(activity.getResources(), emailType, emailLabel);
246
+
247
+ WritableMap emailEntry = Arguments.createMap();
248
+ emailEntry.putString("address", emailAddress);
249
+ emailEntry.putString("type", String.valueOf(typeLabel));
250
+
251
+ emails.pushMap(emailEntry);
252
+ }
253
+
254
+ @Override
255
+ public void onNewIntent(Intent intent) {
256
+
257
+ }
258
+
259
+ public static class SelectContactException extends Exception {
260
+ private final String errorCode;
261
+
262
+ public SelectContactException(String errorCode, String errorMessage) {
263
+ super(errorMessage);
264
+ this.errorCode = errorCode;
265
+ }
266
+
267
+ public String getErrorCode() {
268
+ return errorCode;
269
+ }
270
+ }
271
+ }
@@ -0,0 +1,25 @@
1
+ package com.streem.selectcontact;
2
+
3
+ import com.facebook.react.ReactPackage;
4
+ import com.facebook.react.bridge.NativeModule;
5
+ import com.facebook.react.bridge.ReactApplicationContext;
6
+ import com.facebook.react.uimanager.ViewManager;
7
+
8
+ import java.util.ArrayList;
9
+ import java.util.Collections;
10
+ import java.util.List;
11
+
12
+ public class SelectContactPackage implements ReactPackage {
13
+
14
+ @Override
15
+ public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
16
+ return Collections.emptyList();
17
+ }
18
+
19
+ @Override
20
+ public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
21
+ List<NativeModule> modules = new ArrayList<>();
22
+ modules.add(new SelectContactModule(reactContext));
23
+ return modules;
24
+ }
25
+ }
@@ -0,0 +1,3 @@
1
+ <resources>
2
+ <string name="app_name">ContactsWrapper</string>
3
+ </resources>
@@ -0,0 +1,8 @@
1
+ <resources>
2
+
3
+ <!-- Base application theme. -->
4
+ <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
5
+ <!-- Customize your theme here. -->
6
+ </style>
7
+
8
+ </resources>
package/index.js ADDED
@@ -0,0 +1,204 @@
1
+ 'use strict';
2
+
3
+ import {
4
+ Alert,
5
+ ActionSheetIOS,
6
+ NativeModules,
7
+ Platform
8
+ } from 'react-native';
9
+
10
+ const { SelectContact, ActionSheetAndroid } = NativeModules;
11
+ const ActionSheet = Platform.select({
12
+ ios: ActionSheetIOS,
13
+ android: ActionSheetAndroid
14
+ });
15
+
16
+ const DEFAULT_TEXT_OPTIONS = {
17
+ cancel: "Cancel",
18
+ selectPhone: "Select Phone",
19
+ selectPostalAddress: "Select Postal Address",
20
+ selectEmail: "Select Email",
21
+ errorNoPhoneNumbersTitle: "No Phone Numbers",
22
+ errorNoPhoneNumbersBody: "We could not find any phone numbers for ",
23
+ errorNoAddressTitle: "No Postal Addresses",
24
+ errorNoAddressBody: "We could not find any postal addresses for ",
25
+ errorNoEmailTitle: "No Email Addresses",
26
+ errorNoEmailBody: "We could not find any email addresses for ",
27
+ errorOpenTwice: "Cannot open the contact selector twice",
28
+ }
29
+
30
+ let currentlyOpen = false;
31
+
32
+ const SelectContactApi = {
33
+
34
+ selectContact(textOptions=DEFAULT_TEXT_OPTIONS) {
35
+ if (currentlyOpen) {
36
+ return Promise.reject(new Error(textOptions.errorOpenTwice));
37
+ }
38
+
39
+ currentlyOpen = true;
40
+
41
+ return SelectContact.openContactSelection()
42
+ .then(contact => {
43
+ currentlyOpen = false;
44
+ return contact;
45
+ })
46
+ .catch(err => {
47
+ currentlyOpen = false;
48
+
49
+ // Resolve to null when cancelled
50
+ if (err.code === 'E_CONTACT_CANCELLED') {
51
+ return null;
52
+ }
53
+
54
+ throw err;
55
+ });
56
+ },
57
+
58
+ selectContactPostalAddress(textOptions=DEFAULT_TEXT_OPTIONS) {
59
+ return SelectContactApi.selectContact(textOptions)
60
+ .then(contact => {
61
+ if (!contact) {
62
+ return null;
63
+ }
64
+
65
+ let addresses = contact && contact.postalAddresses || [];
66
+ if (addresses.length === 0) {
67
+ Alert.alert(textOptions.errorNoAddressTitle, `${textOptions.errorNoAddressBody}${contact.name}`);
68
+ return null;
69
+ }
70
+
71
+ return selectPostalAddress(addresses)
72
+ .then(selectedAddress => {
73
+ return selectedAddress ? { contact, selectedAddress } : null;
74
+ });
75
+ })
76
+ },
77
+
78
+ selectContactPhone(textOptions=DEFAULT_TEXT_OPTIONS) {
79
+ return SelectContactApi.selectContact(textOptions)
80
+ .then(contact => {
81
+ if (!contact) {
82
+ return null;
83
+ }
84
+
85
+ let phones = contact && contact.phones || [];
86
+ if (phones.length === 0) {
87
+ Alert.alert(textOptions.errorNoPhoneNumbersTitle, `${textOptions.errorNoPhoneNumbersBody}${contact.name}`);
88
+ return null;
89
+ }
90
+
91
+ return selectPhone(phones, textOptions)
92
+ .then(selectedPhone => {
93
+ return selectedPhone ? { contact, selectedPhone } : null;
94
+ });
95
+ });
96
+ },
97
+
98
+ selectContactEmail(textOptions=DEFAULT_TEXT_OPTIONS) {
99
+ return SelectContactApi.selectContact(textOptions)
100
+ .then(contact => {
101
+ if (!contact) {
102
+ return null;
103
+ }
104
+
105
+ let emails = contact && contact.emails || [];
106
+ if (emails.length === 0) {
107
+ Alert.alert(textOptions.errorNoEmailTitle, `${textOptions.errorNoEmailBody}${contact.name}`);
108
+ return null;
109
+ }
110
+
111
+ return selectEmail(emails)
112
+ .then(selectedEmail => {
113
+ return selectedEmail ? { contact, selectedEmail } : null;
114
+ });
115
+ });
116
+ }
117
+
118
+ };
119
+
120
+ module.exports = SelectContactApi;
121
+
122
+
123
+ function selectPhone(phones, textOptions) {
124
+ if (phones.length < 2 || !ActionSheet) {
125
+ return Promise.resolve(phones[0]);
126
+ }
127
+
128
+ let options = phones.map(phone => {
129
+ let { number, type } = phone;
130
+ return number + (type ? ` - ${type}` : '');
131
+ });
132
+
133
+ if (Platform.OS === 'ios') {
134
+ options.push(textOptions.cancel);
135
+ }
136
+
137
+ return new Promise(((resolve) => {
138
+ ActionSheet.showActionSheetWithOptions({
139
+ title: textOptions.selectPhone,
140
+ options: options,
141
+ cancelButtonIndex: options.length - 1,
142
+ },
143
+ (buttonIndex) => {
144
+ resolve(phones[buttonIndex]);
145
+ });
146
+ }));
147
+ }
148
+
149
+ function selectPostalAddress(addresses, textOptions) {
150
+ if (addresses.length < 2 || !ActionSheet) {
151
+ return Promise.resolve(addresses[0]);
152
+ }
153
+
154
+ let options = addresses.map(address => {
155
+ let { formattedAddress, street, city, state, postalCode, isoCountryCode } = address;
156
+
157
+ if (formattedAddress) {
158
+ return formattedAddress;
159
+ }
160
+
161
+ return `${street} ${city}, ${state} ${postalCode} ${isoCountryCode}`;
162
+ });
163
+
164
+ if (Platform.OS === 'ios') {
165
+ options.push(textOptions.cancel);
166
+ }
167
+
168
+ return new Promise(((resolve) => {
169
+ ActionSheet.showActionSheetWithOptions({
170
+ title: textOptions.selectPostalAddress,
171
+ options: options,
172
+ cancelButtonIndex: options.length - 1,
173
+ },
174
+ (buttonIndex) => {
175
+ resolve(addresses[buttonIndex]);
176
+ });
177
+ }));
178
+ }
179
+
180
+ function selectEmail(emails) {
181
+ if (emails.length < 2 || !ActionSheet) {
182
+ return Promise.resolve(emails[0]);
183
+ }
184
+
185
+ let options = emails.map(email => {
186
+ let { address, type } = email;
187
+ return address + (type ? ` - ${type}` : '');
188
+ });
189
+
190
+ if (Platform.OS === 'ios') {
191
+ options.push(textOptions.cancel);
192
+ }
193
+
194
+ return new Promise(((resolve) => {
195
+ ActionSheet.showActionSheetWithOptions({
196
+ title: textOptions.selectEmail,
197
+ options: options,
198
+ cancelButtonIndex: options.length - 1,
199
+ },
200
+ (buttonIndex) => {
201
+ resolve(emails[buttonIndex]);
202
+ });
203
+ }));
204
+ }
@@ -0,0 +1,13 @@
1
+ //
2
+ // RCTSelectContact.h
3
+ // RCTSelectContact
4
+ //
5
+
6
+ #import <React/RCTBridgeModule.h>
7
+
8
+ @import Contacts;
9
+ @import ContactsUI;
10
+
11
+ @interface RCTSelectContact : NSObject <RCTBridgeModule, CNContactPickerDelegate>
12
+
13
+ @end
@@ -0,0 +1,107 @@
1
+ //
2
+ // RCTSelectContact.m
3
+ // RCTSelectContact
4
+ //
5
+
6
+ @import Foundation;
7
+ #import "RCTSelectContact.h"
8
+ @interface RCTSelectContact()
9
+
10
+ @property(nonatomic, retain) RCTPromiseResolveBlock _resolve;
11
+ @property(nonatomic, retain) RCTPromiseRejectBlock _reject;
12
+
13
+ @end
14
+
15
+
16
+ @implementation RCTSelectContact
17
+
18
+ RCT_EXPORT_MODULE(SelectContact);
19
+
20
+ RCT_EXPORT_METHOD(openContactSelection:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
21
+ self._resolve = resolve;
22
+ self._reject = reject;
23
+
24
+ UIViewController *picker = [[CNContactPickerViewController alloc] init];
25
+ ((CNContactPickerViewController *)picker).delegate = self;
26
+
27
+ // Launch Contact Picker
28
+ UIViewController *root = [[[UIApplication sharedApplication] delegate] window].rootViewController;
29
+ while(root.presentedViewController) {
30
+ root = root.presentedViewController;
31
+ }
32
+ [root presentViewController:picker animated:YES completion:nil];
33
+ }
34
+
35
+ - (NSMutableDictionary *) emptyContactDict {
36
+ NSMutableArray *phones = [[NSMutableArray alloc] init];
37
+ NSMutableArray *emails = [[NSMutableArray alloc] init];
38
+ NSMutableArray *addresses = [[NSMutableArray alloc] init];
39
+ return [[NSMutableDictionary alloc] initWithObjects:@[@"", @"", @"", @"", phones, emails, addresses]
40
+ forKeys:@[@"name", @"givenName", @"middleName", @"familyName", @"phones", @"emails", @"postalAddresses"]];
41
+ }
42
+
43
+ #pragma mark - CNContactPickerDelegate
44
+ - (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact {
45
+
46
+ /* Return NSDictionary ans JS Object to RN, containing basic contact data
47
+ This is a starting point, in future more fields should be added, as required.
48
+ */
49
+ NSMutableDictionary *contactData = [self emptyContactDict];
50
+
51
+ [contactData setValue:contact.identifier forKey:@"recordId"];
52
+ //Return name
53
+ NSString *fullName = [self getFullNameForFirst:contact.givenName middle:contact.middleName last:contact.familyName ];
54
+ [contactData setValue:fullName forKey:@"name"];
55
+ [contactData setValue:contact.givenName forKey:@"givenName"];
56
+ [contactData setValue:contact.middleName forKey:@"middleName"];
57
+ [contactData setValue:contact.familyName forKey:@"familyName"];
58
+
59
+ //Return phone numbers
60
+ NSMutableArray* phoneEntries = [contactData valueForKey:@"phones"];
61
+ for (CNLabeledValue<CNPhoneNumber*> *phone in contact.phoneNumbers) {
62
+ CNPhoneNumber* phoneNumber = [phone value];
63
+ NSString* phoneLabel = [phone label];
64
+ NSMutableDictionary<NSString*, NSString*>* phoneEntry = [[NSMutableDictionary alloc] initWithCapacity:2];
65
+ [phoneEntry setValue:[phoneNumber stringValue] forKey:@"number"];
66
+ [phoneEntry setValue:[CNLabeledValue localizedStringForLabel:phoneLabel] forKey:@"type"];
67
+ [phoneEntries addObject:phoneEntry];
68
+ }
69
+
70
+ //Return email addresses
71
+ NSMutableArray* emailEntries = [contactData valueForKey:@"emails"];
72
+ for (CNLabeledValue<NSString*> *email in contact.emailAddresses) {
73
+ NSString* emailAddress = [email value];
74
+ NSString* emailLabel = [email label];
75
+ NSMutableDictionary<NSString*, NSString*>* emailEntry = [[NSMutableDictionary alloc] initWithCapacity:2];
76
+ [emailEntry setValue:emailAddress forKey:@"address"];
77
+ [emailEntry setValue:[CNLabeledValue localizedStringForLabel:emailLabel] forKey:@"type"];
78
+ [emailEntries addObject:emailEntry];
79
+ }
80
+
81
+ // Return postal addresses
82
+ NSMutableArray* addressEntries = [contactData valueForKey:@"postalAddresses"];
83
+ for (CNLabeledValue<CNPostalAddress*> *postalAddress in contact.postalAddresses) {
84
+ CNPostalAddress* addressInfo = [postalAddress value];
85
+ NSMutableDictionary<NSString*, NSString*>* addressEntry = [[NSMutableDictionary alloc] init];
86
+ [addressEntry setValue:[addressInfo street] forKey:@"street"];
87
+ [addressEntry setValue:[addressInfo city] forKey:@"city"];
88
+ [addressEntry setValue:[addressInfo state] forKey:@"state"];
89
+ [addressEntry setValue:[addressInfo postalCode] forKey:@"postalCode"];
90
+ [addressEntry setValue:[addressInfo ISOCountryCode] forKey:@"isoCountryCode"];
91
+ [addressEntries addObject:addressEntry];
92
+ }
93
+
94
+ self._resolve(contactData);
95
+ }
96
+
97
+ -(NSString *) getFullNameForFirst:(NSString *)fName middle:(NSString *)mName last:(NSString *)lName {
98
+ //Check whether to include middle name or not
99
+ NSArray *names = (mName.length > 0) ? [NSArray arrayWithObjects:fName, mName, lName, nil] : [NSArray arrayWithObjects:fName, lName, nil];
100
+ return [names componentsJoinedByString:@" "];
101
+ }
102
+
103
+ - (void)contactPickerDidCancel:(CNContactPickerViewController *)picker {
104
+ self._reject(@"E_CONTACT_CANCELLED", @"Cancelled", nil);
105
+ }
106
+
107
+ @end