@@ -49,7 +49,7 @@ + (PHFetchOptions *)PHFetchOptionsFromMediaType:(NSString *)mediaType
4949 NSString *const lowercase = [mediaType lowercaseString ];
5050 NSMutableArray *format = [NSMutableArray new ];
5151 NSMutableArray *arguments = [NSMutableArray new ];
52-
52+
5353 if ([lowercase isEqualToString: @" photos" ]) {
5454 [format addObject: @" mediaType = %d " ];
5555 [arguments addObject: @(PHAssetMediaTypeImage)];
@@ -62,7 +62,7 @@ + (PHFetchOptions *)PHFetchOptionsFromMediaType:(NSString *)mediaType
6262 " 'videos' or 'all'." , mediaType);
6363 }
6464 }
65-
65+
6666 if (fromTime > 0 ) {
6767 NSDate * fromDate = [NSDate dateWithTimeIntervalSince1970: fromTime/1000 ];
6868 [format addObject: @" creationDate > %@ " ];
@@ -73,7 +73,7 @@ + (PHFetchOptions *)PHFetchOptionsFromMediaType:(NSString *)mediaType
7373 [format addObject: @" creationDate <= %@ " ];
7474 [arguments addObject: toDate];
7575 }
76-
76+
7777 // This case includes the "all" mediatype
7878 PHFetchOptions *const options = [PHFetchOptions new ];
7979 if ([format count ] > 0 ) {
@@ -96,18 +96,34 @@ @implementation RNCCameraRollManager
9696static NSString *const kErrorAuthRestricted = @" E_PHOTO_LIBRARY_AUTH_RESTRICTED" ;
9797static NSString *const kErrorAuthDenied = @" E_PHOTO_LIBRARY_AUTH_DENIED" ;
9898
99- typedef void (^PhotosAuthorizedBlock)(void );
99+ typedef void (^PhotosAuthorizedBlock)(bool isLimited );
100100
101101static void requestPhotoLibraryAccess (RCTPromiseRejectBlock reject, PhotosAuthorizedBlock authorizedBlock) {
102- PHAuthorizationStatus authStatus = [PHPhotoLibrary authorizationStatus ];
102+ PHAuthorizationStatus authStatus;
103+ if (@available (iOS 14 , *)) {
104+ authStatus = [PHPhotoLibrary authorizationStatusForAccessLevel: PHAccessLevelReadWrite];
105+ } else {
106+ authStatus = [PHPhotoLibrary authorizationStatus ];
107+ }
103108 if (authStatus == PHAuthorizationStatusRestricted) {
104109 reject (kErrorAuthRestricted , @" Access to photo library is restricted" , nil );
105110 } else if (authStatus == PHAuthorizationStatusAuthorized) {
106- authorizedBlock ();
111+ authorizedBlock (false );
112+ #pragma clang diagnostic push
113+ #pragma clang diagnostic ignored "-Wunguarded-availability-new"
114+ } else if (authStatus == PHAuthorizationStatusLimited) {
115+ #pragma clang diagnostic pop
116+ authorizedBlock (true );
107117 } else if (authStatus == PHAuthorizationStatusNotDetermined) {
108- [PHPhotoLibrary requestAuthorization: ^(PHAuthorizationStatus status) {
109- requestPhotoLibraryAccess (reject, authorizedBlock);
110- }];
118+ if (@available (iOS 14 , *)) {
119+ [PHPhotoLibrary requestAuthorizationForAccessLevel: PHAccessLevelReadWrite handler: ^(PHAuthorizationStatus status) {
120+ requestPhotoLibraryAccess (reject, authorizedBlock);
121+ }];
122+ } else {
123+ [PHPhotoLibrary requestAuthorization: ^(PHAuthorizationStatus status) {
124+ requestPhotoLibraryAccess (reject, authorizedBlock);
125+ }];
126+ }
111127 } else {
112128 reject (kErrorAuthDenied , @" Access to photo library was denied" , nil );
113129 }
@@ -159,7 +175,7 @@ static void requestPhotoLibraryAccess(RCTPromiseRejectBlock reject, PhotosAuthor
159175 };
160176 void (^saveWithOptions)(void ) = ^void () {
161177 if (![options[@" album" ] isEqualToString: @" " ]) {
162-
178+
163179 PHFetchOptions *fetchOptions = [[PHFetchOptions alloc ] init ];
164180 fetchOptions.predicate = [NSPredicate predicateWithFormat: @" title = %@" , options[@" album" ] ];
165181 collection = [PHAssetCollection fetchAssetCollectionsWithType: PHAssetCollectionTypeAlbum
@@ -188,7 +204,7 @@ static void requestPhotoLibraryAccess(RCTPromiseRejectBlock reject, PhotosAuthor
188204 }
189205 };
190206
191- void (^loadBlock)(void ) = ^void () {
207+ void (^loadBlock)(bool isLimited ) = ^void (bool isLimited ) {
192208 inputURI = request.URL ;
193209 saveWithOptions ();
194210 };
@@ -220,14 +236,16 @@ static void requestPhotoLibraryAccess(RCTPromiseRejectBlock reject, PhotosAuthor
220236
221237static void RCTResolvePromise (RCTPromiseResolveBlock resolve,
222238 NSArray <NSDictionary <NSString *, id > *> *assets,
223- BOOL hasNextPage)
239+ BOOL hasNextPage,
240+ bool isLimited)
224241{
225242 if (!assets.count ) {
226243 resolve (@{
227244 @" edges" : assets,
228245 @" page_info" : @{
229246 @" has_next_page" : @NO ,
230- }
247+ },
248+ @" limited" : @(isLimited)
231249 });
232250 return ;
233251 }
@@ -237,7 +255,8 @@ static void RCTResolvePromise(RCTPromiseResolveBlock resolve,
237255 @" start_cursor" : assets[0 ][@" node" ][@" image" ][@" uri" ],
238256 @" end_cursor" : assets[assets.count - 1 ][@" node" ][@" image" ][@" uri" ],
239257 @" has_next_page" : @(hasNextPage),
240- }
258+ },
259+ @" limited" : @(isLimited)
241260 });
242261}
243262
@@ -262,14 +281,14 @@ static void RCTResolvePromise(RCTPromiseResolveBlock resolve,
262281 BOOL __block includeLocation = [include indexOfObject: @" location" ] != NSNotFound ;
263282 BOOL __block includeImageSize = [include indexOfObject: @" imageSize" ] != NSNotFound ;
264283 BOOL __block includePlayableDuration = [include indexOfObject: @" playableDuration" ] != NSNotFound ;
265-
284+
266285 // If groupTypes is "all", we want to fetch the SmartAlbum "all photos". Otherwise, all
267286 // other groupTypes values require the "album" collection type.
268287 PHAssetCollectionType const collectionType = ([groupTypes isEqualToString: @" all" ]
269288 ? PHAssetCollectionTypeSmartAlbum
270289 : PHAssetCollectionTypeAlbum);
271290 PHAssetCollectionSubtype const collectionSubtype = [RCTConvert PHAssetCollectionSubtype: groupTypes];
272-
291+
273292 // Predicate for fetching assets within a collection
274293 PHFetchOptions *const assetFetchOptions = [RCTConvert PHFetchOptionsFromMediaType: mediaType fromTime: fromTime toTime: toTime];
275294 // We can directly set the limit if we guarantee every image fetched will be
@@ -285,29 +304,29 @@ static void RCTResolvePromise(RCTPromiseResolveBlock resolve,
285304 assetFetchOptions.fetchLimit = first + 1 ;
286305 }
287306 assetFetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]] ;
288-
307+
289308 BOOL __block foundAfter = NO ;
290309 BOOL __block hasNextPage = NO ;
291310 BOOL __block resolvedPromise = NO ;
292311 NSMutableArray <NSDictionary <NSString *, id > *> *assets = [NSMutableArray new ];
293-
312+
294313 // Filter collection name ("group")
295314 PHFetchOptions *const collectionFetchOptions = [PHFetchOptions new ];
296315 collectionFetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"endDate" ascending:NO]] ;
297316 if (groupName != nil ) {
298317 collectionFetchOptions.predicate = [NSPredicate predicateWithFormat: @" localizedTitle = %@" , groupName];
299318 }
300-
319+
301320 BOOL __block stopCollections_;
302321 NSString __block *currentCollectionName;
303322
304- requestPhotoLibraryAccess (reject, ^{
323+ requestPhotoLibraryAccess (reject, ^( bool isLimited) {
305324 void (^collectAsset)(PHAsset*, NSUInteger , BOOL *) = ^(PHAsset * _Nonnull asset, NSUInteger assetIdx, BOOL * _Nonnull stopAssets) {
306325 NSString *const uri = [NSString stringWithFormat: @" ph://%@ " , [asset localIdentifier ]];
307326 NSString *_Nullable originalFilename = NULL ;
308327 PHAssetResource *_Nullable resource = NULL ;
309328 NSNumber * fileSize = [NSNumber numberWithInt: 0 ];
310-
329+
311330 if (includeFilename || includeFileSize || [mimeTypes count ] > 0 ) {
312331 // Get underlying resources of an asset - this includes files as well as details about edited PHAssets
313332 // This is required for the filename and mimeType filtering
@@ -316,7 +335,7 @@ static void RCTResolvePromise(RCTPromiseResolveBlock resolve,
316335 originalFilename = resource.originalFilename ;
317336 fileSize = [resource valueForKey: @" fileSize" ];
318337 }
319-
338+
320339 // WARNING: If you add any code to `collectAsset` that may skip adding an
321340 // asset to the `assets` output array, you should do it inside this
322341 // block and ensure the logic for `collectAssetMayOmitAsset` above is
@@ -354,7 +373,7 @@ static void RCTResolvePromise(RCTPromiseResolveBlock resolve,
354373 stopCollections_ = YES ;
355374 hasNextPage = YES ;
356375 RCTAssert (resolvedPromise == NO , @" Resolved the promise before we finished processing the results." );
357- RCTResolvePromise (resolve, assets, hasNextPage);
376+ RCTResolvePromise (resolve, assets, hasNextPage, isLimited );
358377 resolvedPromise = YES ;
359378 return ;
360379 }
@@ -412,7 +431,7 @@ static void RCTResolvePromise(RCTPromiseResolveBlock resolve,
412431 // If we get this far and haven't resolved the promise yet, we reached the end of the list of photos
413432 if (!resolvedPromise) {
414433 hasNextPage = NO ;
415- RCTResolvePromise (resolve, assets, hasNextPage);
434+ RCTResolvePromise (resolve, assets, hasNextPage, isLimited );
416435 resolvedPromise = YES ;
417436 }
418437 });
@@ -423,7 +442,7 @@ static void RCTResolvePromise(RCTPromiseResolveBlock resolve,
423442 reject:(RCTPromiseRejectBlock)reject)
424443{
425444 NSMutableArray *convertedAssets = [NSMutableArray array ];
426-
445+
427446 for (NSString *asset in assets) {
428447 [convertedAssets addObject: [asset stringByReplacingOccurrencesOfString: @" ph://" withString: @" " ]];
429448 }
0 commit comments