react-native-contacts 7.0.7 → 8.0.0
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/android/build.gradle +33 -1
- package/android/src/main/java/com/rt2zz/reactnativecontacts/ContactsProvider.java +123 -129
- package/android/src/main/java/com/rt2zz/reactnativecontacts/ReactNativeContacts.java +32 -16
- package/android/src/newarch/com/rt2zz/reactnativecontacts/ContactsManager.java +1388 -0
- package/android/src/{main/java → oldarch}/com/rt2zz/reactnativecontacts/ContactsManager.java +100 -88
- package/index.d.ts +10 -10
- package/index.ts +113 -0
- package/ios/RCTContacts/RCTContacts.h +13 -1
- package/ios/RCTContacts/{RCTContacts.m → RCTContacts.mm} +519 -39
- package/package.json +19 -4
- package/react-native-contacts.podspec +27 -8
- package/src/NativeContacts.ts +27 -0
- package/type.ts +62 -0
- package/index.js +0 -2
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
#import "RCTContacts.h"
|
|
4
4
|
#import <AssetsLibrary/AssetsLibrary.h>
|
|
5
5
|
#import <React/RCTLog.h>
|
|
6
|
+
#import <Photos/Photos.h>
|
|
7
|
+
|
|
8
|
+
// #ifdef RCT_NEW_ARCH_ENABLED
|
|
9
|
+
// #import "RNContactsSpec.h"
|
|
10
|
+
// #endif
|
|
6
11
|
|
|
7
12
|
@implementation RCTContacts {
|
|
8
13
|
CNContactStore * contactStore;
|
|
@@ -43,6 +48,13 @@ RCT_EXPORT_MODULE();
|
|
|
43
48
|
};
|
|
44
49
|
}
|
|
45
50
|
|
|
51
|
+
RCT_REMAP_METHOD(getAll, withResolver:(RCTPromiseResolveBlock) resolve
|
|
52
|
+
withRejecter:(RCTPromiseRejectBlock) reject)
|
|
53
|
+
{
|
|
54
|
+
[self getAll:resolve reject:reject];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
46
58
|
RCT_EXPORT_METHOD(checkPermission:(RCTPromiseResolveBlock) resolve
|
|
47
59
|
rejecter:(RCTPromiseRejectBlock) __unused reject)
|
|
48
60
|
{
|
|
@@ -533,7 +545,7 @@ RCT_EXPORT_METHOD(getCount:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromise
|
|
|
533
545
|
|
|
534
546
|
- (NSString *)getPathForDirectory:(int)directory
|
|
535
547
|
{
|
|
536
|
-
NSArray *paths = NSSearchPathForDirectoriesInDomains(directory, NSUserDomainMask, YES);
|
|
548
|
+
NSArray *paths = NSSearchPathForDirectoriesInDomains((NSSearchPathDirectory)directory, NSUserDomainMask, YES);
|
|
537
549
|
return [paths firstObject];
|
|
538
550
|
}
|
|
539
551
|
|
|
@@ -597,7 +609,7 @@ RCT_EXPORT_METHOD(getContactById:(nonnull NSString *)recordID resolver:(RCTPromi
|
|
|
597
609
|
}
|
|
598
610
|
}
|
|
599
611
|
|
|
600
|
-
-(
|
|
612
|
+
-(NSDictionary *) getContact:(NSString *)recordID
|
|
601
613
|
addressBook:(CNContactStore*)addressBook
|
|
602
614
|
withThumbnails:(BOOL) withThumbnails
|
|
603
615
|
{
|
|
@@ -623,7 +635,7 @@ RCT_EXPORT_METHOD(getContactById:(nonnull NSString *)recordID resolver:(RCTPromi
|
|
|
623
635
|
CNContact* contact = [addressBook unifiedContactWithIdentifier:recordID keysToFetch:keysToFetch error:&contactError];
|
|
624
636
|
|
|
625
637
|
if(!contact)
|
|
626
|
-
|
|
638
|
+
return nil;
|
|
627
639
|
|
|
628
640
|
return [self contactToDictionary: contact withThumbnails:withThumbnails];
|
|
629
641
|
}
|
|
@@ -720,7 +732,8 @@ RCT_EXPORT_METHOD(openExistingContact:(NSDictionary *)contactData resolver:(RCTP
|
|
|
720
732
|
currentViewController = currentViewController.presentedViewController;
|
|
721
733
|
}
|
|
722
734
|
|
|
723
|
-
UIActivityIndicatorViewStyle
|
|
735
|
+
UIActivityIndicatorViewStyle activityIndicatorStyle;
|
|
736
|
+
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleMedium];
|
|
724
737
|
UIColor *activityIndicatorBackgroundColor;
|
|
725
738
|
if (@available(iOS 13, *)) {
|
|
726
739
|
activityIndicatorStyle = UIActivityIndicatorViewStyleMedium;
|
|
@@ -792,7 +805,7 @@ RCT_EXPORT_METHOD(viewExistingContact:(NSDictionary *)contactData resolver:(RCTP
|
|
|
792
805
|
CNContactViewController *contactViewController = [CNContactViewController viewControllerForContact:contact];
|
|
793
806
|
|
|
794
807
|
// Add a cancel button which will close the view
|
|
795
|
-
contactViewController.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:backTitle == nil ? @"Cancel" : backTitle style:
|
|
808
|
+
contactViewController.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:backTitle == nil ? @"Cancel" : backTitle style:UIBarButtonItemStylePlain target:self action:@selector(cancelContactForm)];
|
|
796
809
|
contactViewController.delegate = self;
|
|
797
810
|
|
|
798
811
|
|
|
@@ -881,7 +894,7 @@ RCT_EXPORT_METHOD(editExistingContact:(NSDictionary *)contactData resolver:(RCTP
|
|
|
881
894
|
//[controller presentViewController:alert animated:YES completion:nil];
|
|
882
895
|
|
|
883
896
|
// Add a cancel button which will close the view
|
|
884
|
-
controller.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:
|
|
897
|
+
controller.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStylePlain target:self action:@selector(doneContactForm)];
|
|
885
898
|
|
|
886
899
|
controller.delegate = self;
|
|
887
900
|
controller.allowsEditing = true;
|
|
@@ -1160,38 +1173,33 @@ RCT_EXPORT_METHOD(updateContact:(NSDictionary *)contactData resolver:(RCTPromise
|
|
|
1160
1173
|
|
|
1161
1174
|
enum { WDASSETURL_PENDINGREADS = 1, WDASSETURL_ALLFINISHED = 0};
|
|
1162
1175
|
|
|
1163
|
-
+ (NSData*)
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
[albumReadLock unlockWithCondition:WDASSETURL_ALLFINISHED];
|
|
1186
|
-
}];
|
|
1176
|
+
+ (NSData *)loadImageAsset:(NSURL *)assetURL {
|
|
1177
|
+
__block NSData *imageData = nil;
|
|
1178
|
+
|
|
1179
|
+
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
|
1180
|
+
|
|
1181
|
+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
1182
|
+
PHFetchResult<PHAsset *> *result = [PHAsset fetchAssetsWithALAssetURLs:@[assetURL] options:nil];
|
|
1183
|
+
if (result.count > 0) {
|
|
1184
|
+
PHAsset *asset = result.firstObject;
|
|
1185
|
+
PHImageManager *imageManager = [PHImageManager defaultManager];
|
|
1186
|
+
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
|
|
1187
|
+
options.synchronous = YES; // Load image synchronously
|
|
1188
|
+
|
|
1189
|
+
[imageManager requestImageDataForAsset:asset
|
|
1190
|
+
options:options
|
|
1191
|
+
resultHandler:^(NSData * _Nullable data, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
|
|
1192
|
+
imageData = data;
|
|
1193
|
+
dispatch_semaphore_signal(semaphore);
|
|
1194
|
+
}];
|
|
1195
|
+
} else {
|
|
1196
|
+
dispatch_semaphore_signal(semaphore);
|
|
1197
|
+
}
|
|
1187
1198
|
});
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
RCTLog(@"asset lookup finished: %@ %@", [assetURL absoluteString], (data ? @"exists" : @"does not exist"));
|
|
1193
|
-
|
|
1194
|
-
return data;
|
|
1199
|
+
|
|
1200
|
+
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
|
1201
|
+
|
|
1202
|
+
return imageData;
|
|
1195
1203
|
}
|
|
1196
1204
|
|
|
1197
1205
|
RCT_EXPORT_METHOD(deleteContact:(NSDictionary *)contactData resolver:(RCTPromiseResolveBlock) resolve
|
|
@@ -1221,9 +1229,13 @@ RCT_EXPORT_METHOD(deleteContact:(NSDictionary *)contactData resolver:(RCTPromise
|
|
|
1221
1229
|
}
|
|
1222
1230
|
}
|
|
1223
1231
|
|
|
1224
|
-
RCT_EXPORT_METHOD(writePhotoToPath:(
|
|
1232
|
+
RCT_EXPORT_METHOD(writePhotoToPath:(nonnull NSString *)path resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject)
|
|
1225
1233
|
{
|
|
1226
|
-
|
|
1234
|
+
@try {
|
|
1235
|
+
//Nothing is implemented here
|
|
1236
|
+
} @catch (NSException *exception) {
|
|
1237
|
+
reject(@"Error", @"not implemented", nil);
|
|
1238
|
+
}
|
|
1227
1239
|
}
|
|
1228
1240
|
|
|
1229
1241
|
-(CNContactStore*) contactsStore: (RCTPromiseRejectBlock) reject {
|
|
@@ -1253,4 +1265,472 @@ RCT_EXPORT_METHOD(writePhotoToPath:(RCTResponseSenderBlock) rejecter:(RCTPromise
|
|
|
1253
1265
|
return YES;
|
|
1254
1266
|
}
|
|
1255
1267
|
|
|
1268
|
+
// Thanks to this guard, we won't compile this code when we build for the old architecture.
|
|
1269
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
1270
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
1271
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
1272
|
+
{
|
|
1273
|
+
return std::make_shared<facebook::react::NativeContactsSpecJSI>(params);
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
#endif
|
|
1277
|
+
|
|
1278
|
+
- (void)getAll:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
1279
|
+
[self getAllContacts:resolve reject:reject withThumbnails:true];
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
- (void)checkPermission:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
1283
|
+
CNAuthorizationStatus authStatus = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
|
|
1284
|
+
if (authStatus == CNAuthorizationStatusDenied || authStatus == CNAuthorizationStatusRestricted){
|
|
1285
|
+
resolve(@"denied");
|
|
1286
|
+
} else if (authStatus == CNAuthorizationStatusAuthorized){
|
|
1287
|
+
resolve(@"authorized");
|
|
1288
|
+
} else {
|
|
1289
|
+
resolve(@"undefined");
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
|
|
1294
|
+
- (void)deleteContact:(NSDictionary *)contactData resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
1295
|
+
if(!contactStore) {
|
|
1296
|
+
contactStore = [[CNContactStore alloc] init];
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
NSString* recordID = [contactData valueForKey:@"recordID"];
|
|
1300
|
+
|
|
1301
|
+
NSArray *keys = @[CNContactIdentifierKey];
|
|
1302
|
+
|
|
1303
|
+
|
|
1304
|
+
@try {
|
|
1305
|
+
|
|
1306
|
+
CNMutableContact *contact = [[contactStore unifiedContactWithIdentifier:recordID keysToFetch:keys error:nil] mutableCopy];
|
|
1307
|
+
NSError *error;
|
|
1308
|
+
CNSaveRequest *saveRequest = [[CNSaveRequest alloc] init];
|
|
1309
|
+
[saveRequest deleteContact:contact];
|
|
1310
|
+
[contactStore executeSaveRequest:saveRequest error:&error];
|
|
1311
|
+
|
|
1312
|
+
resolve(recordID);
|
|
1313
|
+
}
|
|
1314
|
+
@catch (NSException *exception) {
|
|
1315
|
+
reject(@"Error", [exception reason], nil);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
|
|
1320
|
+
- (void)editExistingContact:(NSDictionary *)contactData resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
1321
|
+
CNContactStore* contactStore = [self contactsStore:reject];
|
|
1322
|
+
if(!contactStore)
|
|
1323
|
+
return;
|
|
1324
|
+
|
|
1325
|
+
NSError* contactError;
|
|
1326
|
+
selectedContact = nil;
|
|
1327
|
+
NSString* recordID = [contactData valueForKey:@"recordID"];
|
|
1328
|
+
NSArray * keysToFetch =@[
|
|
1329
|
+
CNContactEmailAddressesKey,
|
|
1330
|
+
CNContactPhoneNumbersKey,
|
|
1331
|
+
CNContactFamilyNameKey,
|
|
1332
|
+
CNContactGivenNameKey,
|
|
1333
|
+
CNContactMiddleNameKey,
|
|
1334
|
+
CNContactPostalAddressesKey,
|
|
1335
|
+
CNContactOrganizationNameKey,
|
|
1336
|
+
CNContactJobTitleKey,
|
|
1337
|
+
CNContactImageDataAvailableKey,
|
|
1338
|
+
CNContactThumbnailImageDataKey,
|
|
1339
|
+
CNContactImageDataKey,
|
|
1340
|
+
CNContactUrlAddressesKey,
|
|
1341
|
+
CNContactBirthdayKey,
|
|
1342
|
+
CNContactIdentifierKey,
|
|
1343
|
+
[CNContactFormatter descriptorForRequiredKeysForStyle:CNContactFormatterStyleFullName],
|
|
1344
|
+
[CNContactViewController descriptorForRequiredKeys]];
|
|
1345
|
+
|
|
1346
|
+
@try {
|
|
1347
|
+
CNMutableContact* record = [[contactStore unifiedContactWithIdentifier:recordID keysToFetch:keysToFetch error:&contactError] mutableCopy];
|
|
1348
|
+
|
|
1349
|
+
NSMutableArray *phoneNumbers = [[NSMutableArray alloc]init];
|
|
1350
|
+
phoneNumbers = [NSMutableArray arrayWithArray:record.phoneNumbers];
|
|
1351
|
+
|
|
1352
|
+
for (id phoneData in [contactData valueForKey:@"phoneNumbers"]) {
|
|
1353
|
+
NSString *number = [phoneData valueForKey:@"number"];
|
|
1354
|
+
|
|
1355
|
+
CNLabeledValue *contactPhoneNumber = [CNLabeledValue labeledValueWithLabel:CNLabelOther value:[CNPhoneNumber phoneNumberWithStringValue:number]];
|
|
1356
|
+
//record.phoneNumbers = @[contactPhoneNumber];
|
|
1357
|
+
[phoneNumbers addObject:contactPhoneNumber];
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
NSArray *phoneNumbersNew = [[NSArray alloc]init];
|
|
1361
|
+
phoneNumbersNew = [NSArray arrayWithArray:phoneNumbers];
|
|
1362
|
+
|
|
1363
|
+
|
|
1364
|
+
record.phoneNumbers = phoneNumbersNew;
|
|
1365
|
+
|
|
1366
|
+
CNSaveRequest *request = [[CNSaveRequest alloc] init];
|
|
1367
|
+
[request updateContact:record];
|
|
1368
|
+
|
|
1369
|
+
selectedContact = record;
|
|
1370
|
+
|
|
1371
|
+
[contactStore executeSaveRequest:request error:nil];
|
|
1372
|
+
|
|
1373
|
+
CNContactViewController *controller = [CNContactViewController viewControllerForContact:record];
|
|
1374
|
+
//controller.title = @"Saved!";
|
|
1375
|
+
UIAlertController *alert= [UIAlertController
|
|
1376
|
+
alertControllerWithTitle:@"Saved!"
|
|
1377
|
+
message:@"Number added to contact"
|
|
1378
|
+
preferredStyle:UIAlertControllerStyleAlert];
|
|
1379
|
+
//[controller presentViewController:alert animated:YES completion:nil];
|
|
1380
|
+
|
|
1381
|
+
// Add a cancel button which will close the view
|
|
1382
|
+
controller.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStylePlain target:self action:@selector(doneContactForm)];
|
|
1383
|
+
|
|
1384
|
+
controller.delegate = self;
|
|
1385
|
+
controller.allowsEditing = true;
|
|
1386
|
+
controller.allowsActions = true;
|
|
1387
|
+
|
|
1388
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
1389
|
+
UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:controller];
|
|
1390
|
+
UIViewController *viewController = (UIViewController*)[[[[UIApplication sharedApplication] delegate] window] rootViewController];
|
|
1391
|
+
|
|
1392
|
+
//navigation.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName : [UIColor redColor]};
|
|
1393
|
+
|
|
1394
|
+
while (viewController.presentedViewController)
|
|
1395
|
+
{
|
|
1396
|
+
viewController = viewController.presentedViewController;
|
|
1397
|
+
}
|
|
1398
|
+
[viewController presentViewController:navigation animated:YES completion:nil];
|
|
1399
|
+
[controller presentViewController:alert animated:YES completion:nil];
|
|
1400
|
+
|
|
1401
|
+
self->updateContactPromise = resolve;
|
|
1402
|
+
});
|
|
1403
|
+
|
|
1404
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
1405
|
+
|
|
1406
|
+
[alert dismissViewControllerAnimated:YES completion:^{
|
|
1407
|
+
|
|
1408
|
+
//Dismissed
|
|
1409
|
+
}];
|
|
1410
|
+
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
@catch (NSException *exception) {
|
|
1414
|
+
reject(@"Error", [exception reason], nil);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
|
|
1419
|
+
- (void)getAllWithoutPhotos:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
1420
|
+
[self getAllContacts:resolve reject:reject withThumbnails:false];
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
|
|
1424
|
+
- (void)getContactById:(nonnull NSString *)recordID resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
1425
|
+
CNContactStore* contactStore = [self contactsStore:reject];
|
|
1426
|
+
if(!contactStore)
|
|
1427
|
+
return;
|
|
1428
|
+
|
|
1429
|
+
CNEntityType entityType = CNEntityTypeContacts;
|
|
1430
|
+
if([CNContactStore authorizationStatusForEntityType:entityType] == CNAuthorizationStatusNotDetermined)
|
|
1431
|
+
{
|
|
1432
|
+
[contactStore requestAccessForEntityType:entityType completionHandler:^(BOOL granted, NSError * _Nullable error) {
|
|
1433
|
+
if(granted){
|
|
1434
|
+
resolve([self getContact:recordID addressBook:contactStore withThumbnails:false]);
|
|
1435
|
+
}
|
|
1436
|
+
}];
|
|
1437
|
+
}
|
|
1438
|
+
else if( [CNContactStore authorizationStatusForEntityType:entityType]== CNAuthorizationStatusAuthorized)
|
|
1439
|
+
{
|
|
1440
|
+
resolve([self getContact:recordID addressBook:contactStore withThumbnails:false]);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
|
|
1445
|
+
- (void)getContactsByEmailAddress:(NSString *)string resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
1446
|
+
CNContactStore *contactStore = [[CNContactStore alloc] init];
|
|
1447
|
+
if (!contactStore)
|
|
1448
|
+
return;
|
|
1449
|
+
[self getContactsFromAddressBook:contactStore byEmailAddress:string resolve:resolve];
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
|
|
1453
|
+
- (void)getContactsByPhoneNumber:(NSString *)string resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
1454
|
+
CNContactStore *contactStore = [[CNContactStore alloc] init];
|
|
1455
|
+
if (!contactStore)
|
|
1456
|
+
return;
|
|
1457
|
+
[self getContactsFromAddressBook:contactStore byPhoneNumber:string resolve:resolve];
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
|
|
1461
|
+
- (void)getContactsMatchingString:(NSString *)string resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
1462
|
+
CNContactStore *contactStore = [[CNContactStore alloc] init];
|
|
1463
|
+
if (!contactStore)
|
|
1464
|
+
return;
|
|
1465
|
+
[self getContactsFromAddressBook:contactStore matchingString:string resolve:resolve];
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
|
|
1469
|
+
- (void)getCount:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
1470
|
+
[self getAllContactsCount:resolve reject:reject];
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
|
|
1474
|
+
- (void)getPhotoForId:(nonnull NSString *)recordID resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
1475
|
+
CNContactStore* contactStore = [self contactsStore:reject];
|
|
1476
|
+
if(!contactStore)
|
|
1477
|
+
return;
|
|
1478
|
+
|
|
1479
|
+
CNEntityType entityType = CNEntityTypeContacts;
|
|
1480
|
+
if([CNContactStore authorizationStatusForEntityType:entityType] == CNAuthorizationStatusNotDetermined)
|
|
1481
|
+
{
|
|
1482
|
+
[contactStore requestAccessForEntityType:entityType completionHandler:^(BOOL granted, NSError * _Nullable error) {
|
|
1483
|
+
if(granted){
|
|
1484
|
+
resolve([self getFilePathForThumbnailImage:recordID addressBook:contactStore]);
|
|
1485
|
+
}
|
|
1486
|
+
}];
|
|
1487
|
+
}
|
|
1488
|
+
else if( [CNContactStore authorizationStatusForEntityType:entityType]== CNAuthorizationStatusAuthorized)
|
|
1489
|
+
{
|
|
1490
|
+
resolve([self getFilePathForThumbnailImage:recordID addressBook:contactStore]);
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
|
|
1495
|
+
- (void)openContactForm:(NSDictionary *)contactData resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
1496
|
+
CNMutableContact * contact = [[CNMutableContact alloc] init];
|
|
1497
|
+
|
|
1498
|
+
[self updateRecord:contact withData:contactData];
|
|
1499
|
+
|
|
1500
|
+
CNContactViewController *controller = [CNContactViewController viewControllerForNewContact:contact];
|
|
1501
|
+
|
|
1502
|
+
|
|
1503
|
+
controller.delegate = self;
|
|
1504
|
+
|
|
1505
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
1506
|
+
UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:controller];
|
|
1507
|
+
UIViewController *viewController = (UIViewController*)[[[[UIApplication sharedApplication] delegate] window] rootViewController];
|
|
1508
|
+
while (viewController.presentedViewController)
|
|
1509
|
+
{
|
|
1510
|
+
viewController = viewController.presentedViewController;
|
|
1511
|
+
}
|
|
1512
|
+
[viewController presentViewController:navigation animated:YES completion:nil];
|
|
1513
|
+
|
|
1514
|
+
self->updateContactPromise = resolve;
|
|
1515
|
+
});
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
|
|
1519
|
+
- (void)openExistingContact:(NSDictionary *)contactData resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
1520
|
+
if(!contactStore) {
|
|
1521
|
+
contactStore = [[CNContactStore alloc] init];
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
NSString* recordID = [contactData valueForKey:@"recordID"];
|
|
1525
|
+
NSString* backTitle = [contactData valueForKey:@"backTitle"];
|
|
1526
|
+
|
|
1527
|
+
NSArray *keys = @[CNContactIdentifierKey,
|
|
1528
|
+
CNContactEmailAddressesKey,
|
|
1529
|
+
CNContactBirthdayKey,
|
|
1530
|
+
CNContactImageDataKey,
|
|
1531
|
+
CNContactPhoneNumbersKey,
|
|
1532
|
+
[CNContactFormatter descriptorForRequiredKeysForStyle:CNContactFormatterStyleFullName],
|
|
1533
|
+
[CNContactViewController descriptorForRequiredKeys]];
|
|
1534
|
+
|
|
1535
|
+
@try {
|
|
1536
|
+
|
|
1537
|
+
CNContact *contact = [contactStore unifiedContactWithIdentifier:recordID keysToFetch:keys error:nil];
|
|
1538
|
+
|
|
1539
|
+
CNContactViewController *contactViewController = [CNContactViewController viewControllerForContact:contact];
|
|
1540
|
+
|
|
1541
|
+
// Add a cancel button which will close the view
|
|
1542
|
+
contactViewController.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:backTitle == nil ? @"Cancel" : backTitle style:UIBarButtonItemStyleDone target:self action:@selector(cancelContactForm)];
|
|
1543
|
+
contactViewController.delegate = self;
|
|
1544
|
+
|
|
1545
|
+
|
|
1546
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
1547
|
+
UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:contactViewController];
|
|
1548
|
+
|
|
1549
|
+
UIViewController *currentViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
|
|
1550
|
+
|
|
1551
|
+
while (currentViewController.presentedViewController)
|
|
1552
|
+
{
|
|
1553
|
+
currentViewController = currentViewController.presentedViewController;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
UIActivityIndicatorViewStyle activityIndicatorStyle;
|
|
1557
|
+
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleMedium];
|
|
1558
|
+
UIColor *activityIndicatorBackgroundColor;
|
|
1559
|
+
if (@available(iOS 13, *)) {
|
|
1560
|
+
activityIndicatorStyle = UIActivityIndicatorViewStyleMedium;
|
|
1561
|
+
activityIndicatorBackgroundColor = [UIColor secondarySystemGroupedBackgroundColor];
|
|
1562
|
+
} else {
|
|
1563
|
+
activityIndicatorStyle = UIActivityIndicatorViewStyleGray;
|
|
1564
|
+
activityIndicatorBackgroundColor = [UIColor whiteColor];;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
// Cover the contact view with an activity indicator so we can put it in edit mode without user seeing the transition
|
|
1568
|
+
UIActivityIndicatorView *activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:activityIndicatorStyle];
|
|
1569
|
+
activityIndicatorView.frame = UIApplication.sharedApplication.keyWindow.frame;
|
|
1570
|
+
[activityIndicatorView startAnimating];
|
|
1571
|
+
activityIndicatorView.backgroundColor = activityIndicatorBackgroundColor;
|
|
1572
|
+
[navigation.view addSubview:activityIndicatorView];
|
|
1573
|
+
|
|
1574
|
+
[currentViewController presentViewController:navigation animated:YES completion:nil];
|
|
1575
|
+
|
|
1576
|
+
|
|
1577
|
+
// TODO should this 'fake click' method be used? For a brief instance
|
|
1578
|
+
// Fake click edit button to enter edit mode
|
|
1579
|
+
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
1580
|
+
// SEL selector = contactViewController.navigationItem.rightBarButtonItem.action;
|
|
1581
|
+
// NSLog(@"!!!!!!!!!!!!!!!!!! FAKE CLICK!!! %@", NSStringFromSelector(selector));
|
|
1582
|
+
// id target = contactViewController.navigationItem.rightBarButtonItem.target;
|
|
1583
|
+
// [target performSelector:selector];
|
|
1584
|
+
// });
|
|
1585
|
+
|
|
1586
|
+
|
|
1587
|
+
// We need to wait for a short while otherwise contactViewController will not respond to the selector (it has not initialized)
|
|
1588
|
+
[contactViewController performSelector:@selector(toggleEditing:) withObject:nil afterDelay:0.1];
|
|
1589
|
+
|
|
1590
|
+
// remove the activity indicator after a delay so the underlying transition will have time to complete
|
|
1591
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
1592
|
+
[activityIndicatorView removeFromSuperview];
|
|
1593
|
+
});
|
|
1594
|
+
|
|
1595
|
+
updateContactPromise = resolve;
|
|
1596
|
+
});
|
|
1597
|
+
|
|
1598
|
+
}
|
|
1599
|
+
@catch (NSException *exception) {
|
|
1600
|
+
reject(@"Error", [exception reason], nil);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
|
|
1605
|
+
- (void)requestPermission:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
1606
|
+
CNContactStore* contactStore = [[CNContactStore alloc] init];
|
|
1607
|
+
|
|
1608
|
+
[contactStore requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
|
|
1609
|
+
[self checkPermission:resolve rejecter:reject];
|
|
1610
|
+
}];
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
|
|
1614
|
+
- (void)updateContact:(NSDictionary *)contactData resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
1615
|
+
CNContactStore* contactStore = [self contactsStore:reject];
|
|
1616
|
+
if(!contactStore)
|
|
1617
|
+
return;
|
|
1618
|
+
|
|
1619
|
+
NSError* contactError;
|
|
1620
|
+
NSString* recordID = [contactData valueForKey:@"recordID"];
|
|
1621
|
+
NSMutableArray * keysToFetch = [NSMutableArray arrayWithArray: @[
|
|
1622
|
+
CNContactEmailAddressesKey,
|
|
1623
|
+
CNContactPhoneNumbersKey,
|
|
1624
|
+
CNContactFamilyNameKey,
|
|
1625
|
+
CNContactGivenNameKey,
|
|
1626
|
+
CNContactMiddleNameKey,
|
|
1627
|
+
CNContactPostalAddressesKey,
|
|
1628
|
+
CNContactOrganizationNameKey,
|
|
1629
|
+
CNContactJobTitleKey,
|
|
1630
|
+
CNContactImageDataAvailableKey,
|
|
1631
|
+
CNContactThumbnailImageDataKey,
|
|
1632
|
+
CNContactImageDataKey,
|
|
1633
|
+
CNContactUrlAddressesKey,
|
|
1634
|
+
CNContactBirthdayKey,
|
|
1635
|
+
CNContactInstantMessageAddressesKey
|
|
1636
|
+
]];
|
|
1637
|
+
if(notesUsageEnabled) {
|
|
1638
|
+
[keysToFetch addObject: CNContactNoteKey];
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
@try {
|
|
1642
|
+
CNMutableContact* record = [[contactStore unifiedContactWithIdentifier:recordID keysToFetch:keysToFetch error:&contactError] mutableCopy];
|
|
1643
|
+
[self updateRecord:record withData:contactData];
|
|
1644
|
+
CNSaveRequest *request = [[CNSaveRequest alloc] init];
|
|
1645
|
+
[request updateContact:record];
|
|
1646
|
+
|
|
1647
|
+
[contactStore executeSaveRequest:request error:nil];
|
|
1648
|
+
|
|
1649
|
+
NSDictionary *contactDict = [self contactToDictionary:record withThumbnails:false];
|
|
1650
|
+
|
|
1651
|
+
resolve(contactDict);
|
|
1652
|
+
}
|
|
1653
|
+
@catch (NSException *exception) {
|
|
1654
|
+
reject(@"Error", [exception reason], nil);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
|
|
1659
|
+
- (void)viewExistingContact:(NSDictionary *)contactData resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
1660
|
+
if(!contactStore) {
|
|
1661
|
+
contactStore = [[CNContactStore alloc] init];
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
NSString* recordID = [contactData valueForKey:@"recordID"];
|
|
1665
|
+
NSString* backTitle = [contactData valueForKey:@"backTitle"];
|
|
1666
|
+
|
|
1667
|
+
NSArray *keys = @[CNContactIdentifierKey,
|
|
1668
|
+
CNContactEmailAddressesKey,
|
|
1669
|
+
CNContactBirthdayKey,
|
|
1670
|
+
CNContactImageDataKey,
|
|
1671
|
+
CNContactPhoneNumbersKey,
|
|
1672
|
+
[CNContactFormatter descriptorForRequiredKeysForStyle:CNContactFormatterStyleFullName],
|
|
1673
|
+
[CNContactViewController descriptorForRequiredKeys]];
|
|
1674
|
+
|
|
1675
|
+
@try {
|
|
1676
|
+
|
|
1677
|
+
CNContact *contact = [contactStore unifiedContactWithIdentifier:recordID keysToFetch:keys error:nil];
|
|
1678
|
+
|
|
1679
|
+
CNContactViewController *contactViewController = [CNContactViewController viewControllerForContact:contact];
|
|
1680
|
+
|
|
1681
|
+
// Add a cancel button which will close the view
|
|
1682
|
+
contactViewController.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:backTitle == nil ? @"Cancel" : backTitle style:UIBarButtonItemStylePlain target:self action:@selector(cancelContactForm)];
|
|
1683
|
+
contactViewController.delegate = self;
|
|
1684
|
+
|
|
1685
|
+
|
|
1686
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
1687
|
+
UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:contactViewController];
|
|
1688
|
+
|
|
1689
|
+
UIViewController *currentViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
|
|
1690
|
+
|
|
1691
|
+
while (currentViewController.presentedViewController)
|
|
1692
|
+
{
|
|
1693
|
+
currentViewController = currentViewController.presentedViewController;
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
[currentViewController presentViewController:navigation animated:YES completion:nil];
|
|
1697
|
+
|
|
1698
|
+
updateContactPromise = resolve;
|
|
1699
|
+
});
|
|
1700
|
+
|
|
1701
|
+
}
|
|
1702
|
+
@catch (NSException *exception) {
|
|
1703
|
+
reject(@"Error", [exception reason], nil);
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
|
|
1708
|
+
- (void)writePhotoToPath:(NSString *)contactId file:(NSString *)file resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
1709
|
+
reject(@"Error", @"not implemented", nil);
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
- (void)addContact:(NSDictionary *)contactData resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
1713
|
+
CNContactStore* contactStore = [self contactsStore:reject];
|
|
1714
|
+
if(!contactStore)
|
|
1715
|
+
return;
|
|
1716
|
+
|
|
1717
|
+
CNMutableContact * contact = [[CNMutableContact alloc] init];
|
|
1718
|
+
|
|
1719
|
+
[self updateRecord:contact withData:contactData];
|
|
1720
|
+
|
|
1721
|
+
@try {
|
|
1722
|
+
CNSaveRequest *request = [[CNSaveRequest alloc] init];
|
|
1723
|
+
[request addContact:contact toContainerWithIdentifier:nil];
|
|
1724
|
+
|
|
1725
|
+
[contactStore executeSaveRequest:request error:nil];
|
|
1726
|
+
|
|
1727
|
+
NSDictionary *contactDict = [self contactToDictionary:contact withThumbnails:false];
|
|
1728
|
+
|
|
1729
|
+
resolve(contactDict);
|
|
1730
|
+
}
|
|
1731
|
+
@catch (NSException *exception) {
|
|
1732
|
+
reject(@"Error", [exception reason], nil);
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1256
1736
|
@end
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"type": "git",
|
|
5
5
|
"url": "https://github.com/rt2zz/react-native-contacts.git"
|
|
6
6
|
},
|
|
7
|
-
"version": "
|
|
7
|
+
"version": "8.0.0",
|
|
8
8
|
"description": "React Native Contacts (android & ios)",
|
|
9
9
|
"nativePackage": true,
|
|
10
10
|
"keywords": [
|
|
@@ -19,9 +19,18 @@
|
|
|
19
19
|
"bugs": {
|
|
20
20
|
"url": "https://github.com/rt2zz/react-native-contacts/issues"
|
|
21
21
|
},
|
|
22
|
+
"@react-native-community/bob": {
|
|
23
|
+
"source": "src",
|
|
24
|
+
"output": "lib",
|
|
25
|
+
"targets": [
|
|
26
|
+
"commonjs",
|
|
27
|
+
"module",
|
|
28
|
+
"typescript"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
22
31
|
"homepage": "https://github.com/rt2zz/react-native-contacts",
|
|
23
|
-
"main": "index.
|
|
24
|
-
"types": "index.
|
|
32
|
+
"main": "index.ts",
|
|
33
|
+
"types": "index.ts",
|
|
25
34
|
"scripts": {
|
|
26
35
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
27
36
|
},
|
|
@@ -43,7 +52,13 @@
|
|
|
43
52
|
"devDependencies": {
|
|
44
53
|
"react-native-cli": "^2.0.1"
|
|
45
54
|
},
|
|
55
|
+
"codegenConfig": {
|
|
56
|
+
"name": "RNContactsSpec",
|
|
57
|
+
"type": "modules",
|
|
58
|
+
"jsSrcsDir": "src"
|
|
59
|
+
},
|
|
46
60
|
"peerDependencies": {
|
|
47
|
-
"react-native": "
|
|
61
|
+
"react-native": "*",
|
|
62
|
+
"react": "*"
|
|
48
63
|
}
|
|
49
64
|
}
|