@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.
- package/.github/workflows/npm-publish.yml +33 -0
- package/CHANGELOG.md +57 -0
- package/RCTSelectContact.podspec +23 -0
- package/README.md +177 -0
- package/android/build.gradle +52 -0
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +5 -0
- package/android/gradlew +188 -0
- package/android/gradlew.bat +100 -0
- package/android/keystores/BUCK +8 -0
- package/android/keystores/debug.keystore.properties +4 -0
- package/android/src/main/AndroidManifest.xml +6 -0
- package/android/src/main/java/com/streem/selectcontact/SelectContactModule.java +271 -0
- package/android/src/main/java/com/streem/selectcontact/SelectContactPackage.java +25 -0
- package/android/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/android/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/android/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/android/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/android/src/main/res/values/strings.xml +3 -0
- package/android/src/main/res/values/styles.xml +8 -0
- package/index.js +204 -0
- package/ios/RCTSelectContact/RCTSelectContact.h +13 -0
- package/ios/RCTSelectContact/RCTSelectContact.m +107 -0
- package/ios/SelectContact.xcodeproj/project.pbxproj +291 -0
- package/ios/SelectContact.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/ios/SelectContact.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/ios/SelectContact.xcodeproj/project.xcworkspace/xcuserdata/oliverjacobs.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/SelectContact.xcodeproj/project.xcworkspace/xcuserdata/seanadkinson.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/SelectContact.xcodeproj/xcshareddata/xcschemes/SelectContact.xcscheme +115 -0
- package/ios/SelectContact.xcodeproj/xcuserdata/oliverjacobs.xcuserdatad/xcschemes/RCTContactsWrapper.xcscheme +80 -0
- package/ios/SelectContact.xcodeproj/xcuserdata/oliverjacobs.xcuserdatad/xcschemes/xcschememanagement.plist +37 -0
- package/ios/SelectContact.xcodeproj/xcuserdata/seanadkinson.xcuserdatad/xcschemes/RCTSelectContact.xcscheme +80 -0
- package/ios/SelectContact.xcodeproj/xcuserdata/seanadkinson.xcuserdatad/xcschemes/xcschememanagement.plist +27 -0
- package/license.txt +21 -0
- package/package.json +44 -0
- package/react-native-select-contact.podspec +19 -0
- 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
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
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,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
|