mirror of
https://github.com/status-im/react-native-camera-roll.git
synced 2026-08-27 11:21:08 +00:00
feat(iOS): added getPhotoThumbnail (#536)
This commit is contained in:
@@ -152,6 +152,7 @@ async function savePicture() {
|
||||
* [`deletePhotos`](#deletephotos)
|
||||
* [`iosGetImageDataById`](#iosgetimagedatabyid)
|
||||
* [`useCameraRoll`](#usecameraroll)
|
||||
* [`getPhotoThumbnail`](#getphotothumbnail) **iOS only**
|
||||
|
||||
---
|
||||
|
||||
@@ -527,6 +528,82 @@ function Example() {
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
### `getPhotoThumbnail()`
|
||||
|
||||
**iOS only**
|
||||
|
||||
Returns a Promise with thumbnail photo.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| ------------ | --------------------- | -------- | -------------------------------------------------- |
|
||||
| internalID | string | Yes | Ios internal ID 'PH://xxxx'. |
|
||||
| options | PhotoThumbnailOptions | Yes | Expects an options with the shape described below. |
|
||||
|
||||
* `allowNetworkAccess` : {boolean} : **default = false** : Specifies whether the requested image can be downloaded from iCloud. **iOS only**
|
||||
* `targetSize` : {ThumbnailSize} : Expects a targetSize with the shape desribed below:
|
||||
* `height` : {number} : **default = 400**
|
||||
* `width` : {number} : **default = 400**
|
||||
* `quality` : {number} : **default = 1.0** : jpeg quality used for compression (a value from 0.0 to 1.0). A value of 0.0 is maximum compression (or lowest quality). A value of 1.0 is least compression (or best quality).
|
||||
|
||||
**Returns:**
|
||||
|
||||
| Type | Description |
|
||||
| ------------------------- | ------------------------------------------------------------- |
|
||||
| Promise\<PhotoThumbnail\> | A Promise with PhotoThumbnail with the shape described below. |
|
||||
|
||||
* `thumbnailBase64` : {string}
|
||||
|
||||
#### Example
|
||||
|
||||
Loading a thumbnail:
|
||||
|
||||
```javascript
|
||||
export default function Thumbnail(props) {
|
||||
const [base64Image, setBase64Image] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const getThumbnail = async () => {
|
||||
const options = {
|
||||
allowNetworkAccess: true,
|
||||
targetSize: {
|
||||
height: 80,
|
||||
width: 80
|
||||
},
|
||||
quality: 1.0
|
||||
};
|
||||
|
||||
const thumbnailResponse = await CameraRoll.getPhotoThumbnail(props.image.uri, options);
|
||||
|
||||
setBase64Image(thumbnailResponse.thumbnailBase64);
|
||||
};
|
||||
|
||||
getThumbnail();
|
||||
}, []);
|
||||
|
||||
const extension = props.image.extension;
|
||||
let prefix;
|
||||
|
||||
switch (extension) {
|
||||
case 'png':
|
||||
prefix = 'data:image/png;base64,';
|
||||
break;
|
||||
default:
|
||||
//all others can use jpeg
|
||||
prefix = 'data:image/jpeg;base64,';
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<Image
|
||||
source={{ uri: `${prefix}${base64Image}` }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Known issues
|
||||
|
||||
#### IOS
|
||||
|
||||
@@ -693,6 +693,70 @@ RCT_EXPORT_METHOD(getPhotoByInternalID:(NSString *)internalId
|
||||
}, false);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getPhotoThumbnail:(NSString *)internalId
|
||||
options:(NSDictionary *)options
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
reject:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
checkPhotoLibraryConfig();
|
||||
|
||||
BOOL const allowNetworkAccess = options[@"allowNetworkAccess"] == nil ? NO : [RCTConvert BOOL:options[@"allowNetworkAccess"]];
|
||||
|
||||
NSDictionary *const targetSize = [RCTConvert NSDictionary:options[@"targetSize"]];
|
||||
CGFloat const targetHeight = targetSize[@"height"] == nil ? 400 : [RCTConvert CGFloat:targetSize[@"height"]];
|
||||
CGFloat const targetWidth = targetSize[@"width"] == nil ? 400 : [RCTConvert CGFloat:targetSize[@"width"]];
|
||||
|
||||
CGFloat quality = options[@"quality"] == nil ? 1.0 : [RCTConvert CGFloat:options[@"quality"]];
|
||||
|
||||
requestPhotoLibraryAccess(reject, ^(bool isLimited){
|
||||
|
||||
PHFetchResult<PHAsset *> *fetchResult;
|
||||
PHAsset *asset;
|
||||
NSString *mediaIdentifier = internalId;
|
||||
|
||||
if ([internalId rangeOfString:@"ph://"].location != NSNotFound) {
|
||||
mediaIdentifier = [internalId stringByReplacingOccurrencesOfString:@"ph://"
|
||||
withString:@""];
|
||||
}
|
||||
|
||||
fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:@[mediaIdentifier] options:nil];
|
||||
if(fetchResult){
|
||||
asset = fetchResult.firstObject;//only object in the array.
|
||||
}
|
||||
|
||||
if(asset){
|
||||
PHImageRequestOptions *const requestOptions = [PHImageRequestOptions new];
|
||||
requestOptions.networkAccessAllowed = allowNetworkAccess;
|
||||
requestOptions.version = PHImageRequestOptionsVersionUnadjusted;
|
||||
requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
|
||||
|
||||
CGSize const thumbnailSize = CGSizeMake(targetWidth, targetHeight);
|
||||
[[PHImageManager defaultManager] requestImageForAsset:asset
|
||||
targetSize:thumbnailSize
|
||||
contentMode:PHImageContentModeAspectFill
|
||||
options:requestOptions
|
||||
resultHandler:^(UIImage * _Nullable image,
|
||||
NSDictionary * _Nullable info) {
|
||||
NSError *const error = [info objectForKey:PHImageErrorKey];
|
||||
if (error) {
|
||||
reject(@"Error while getting thumbnail image",@"Error while getting thumbnail image",error);
|
||||
}
|
||||
|
||||
NSString *thumbnailBase64 = [UIImageJPEGRepresentation(image, quality) base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
|
||||
|
||||
resolve(@{
|
||||
@"thumbnailBase64": thumbnailBase64
|
||||
});
|
||||
}];
|
||||
} else {
|
||||
NSString *errorMessage = [NSString stringWithFormat:@"Failed to load asset"
|
||||
" with localIdentifier %@ with no error message.", internalId];
|
||||
NSError *error = RCTErrorWithMessage(errorMessage);
|
||||
reject(@"No asset found",@"No asset found",error);
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
NSString *subTypeLabelForCollection(PHAssetCollection *assetCollection) {
|
||||
PHAssetCollectionSubtype subtype = assetCollection.assetCollectionSubtype;
|
||||
|
||||
|
||||
@@ -179,6 +179,21 @@ export type Album = {
|
||||
subtype?: AlbumSubType;
|
||||
};
|
||||
|
||||
export type ThumbnailSize = {
|
||||
height: number,
|
||||
width: number
|
||||
};
|
||||
|
||||
export type PhotoThumbnailOptions = {
|
||||
allowNetworkAccess: boolean, //iOS only
|
||||
targetSize: ThumbnailSize,
|
||||
quality: number
|
||||
};
|
||||
|
||||
export type PhotoThumbnail = {
|
||||
thumbnailBase64: string,
|
||||
};
|
||||
|
||||
/**
|
||||
* `CameraRoll` provides access to the local camera roll or photo library.
|
||||
*
|
||||
@@ -272,4 +287,15 @@ export class CameraRoll {
|
||||
};
|
||||
return RNCCameraRoll.getPhotoByInternalID(internalID, conversionOption);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Promise with thumbnail photo.
|
||||
*
|
||||
* @param internalID - PH photo internal ID.
|
||||
* @param options - thumbnail photo options.
|
||||
* @returns Promise<PhotoThumbnail>
|
||||
*/
|
||||
static getPhotoThumbnail(internalID: string, options: PhotoThumbnailOptions): Promise<PhotoThumbnail> {
|
||||
return RNCCameraRoll.getPhotoThumbnail(internalID, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// we use Object type because methods on the native side use NSDictionary and ReadableMap
|
||||
// and we want to stay compatible with those
|
||||
import {TurboModuleRegistry, TurboModule} from 'react-native';
|
||||
import type { PhotoThumbnail } from './CameraRoll';
|
||||
|
||||
export type AlbumSubType =
|
||||
| 'AlbumRegular'
|
||||
@@ -76,6 +77,10 @@ export interface Spec extends TurboModule {
|
||||
internalID: string,
|
||||
options: Object,
|
||||
): Promise<PhotoIdentifier>;
|
||||
getPhotoThumbnail(
|
||||
internalID: string,
|
||||
options: Object
|
||||
): Promise<PhotoThumbnail>
|
||||
}
|
||||
|
||||
export default TurboModuleRegistry.getEnforcing<Spec>('RNCCameraRoll');
|
||||
|
||||
Reference in New Issue
Block a user