Skip to content

BREAKING CHANGE: Use promise instead of callback #29

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,25 @@ or

`react-native link @react-native-community/image-editor`

## Api reference
## Usage

Start by importing the library:

```javascript
static cropImage(uri, cropData, success, failure)
import ImageEditor from "@react-native-community/image-editor";
```

Crop the image specified by the URI param. If URI points to a remote image, it will be downloaded automatically. If the image cannot be loaded/downloaded, the failure callback will be called.
### Crop image

Crop the image specified by the URI param. If URI points to a remote image, it will be downloaded automatically. If the image cannot be loaded/downloaded, the promise will be rejected.

If the cropping process is successful, the resultant cropped image will be stored in the cache path, and the URI returned in the promise will point to the image in the cache path. Remember to delete the cropped image from the cache path when you are done with it.

If the cropping process is successful, the resultant cropped image will be stored in the ImageStore, and the URI returned in the success callback will point to the image in the store. Remember to delete the cropped image from the ImageStore when you are done with it.
```javascript
ImageEditor.cropImage(uri, cropData).then(url => {
console.log("Cropped image uri", url);
})
```

### cropData
| Property | Required | Description |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
import android.text.TextUtils;

import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.GuardedAsyncTask;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
Expand Down Expand Up @@ -150,25 +150,23 @@ public boolean accept(File dir, String filename) {
}

/**
* Crop an image. If all goes well, the success callback will be called with the file:// URI of
* Crop an image. If all goes well, the promise will be resolved with the file:// URI of
* the new image as the only argument. This is a temporary file - consider using
* CameraRollManager.saveImageWithTag to save it in the gallery.
*
* @param uri the MediaStore URI of the image to crop
* @param uri the URI of the image to crop
* @param options crop parameters specified as {@code {offset: {x, y}, size: {width, height}}}.
* Optionally this also contains {@code {targetSize: {width, height}}}. If this is
* specified, the cropped image will be resized to that size.
* All units are in pixels (not DPs).
* @param success callback to be invoked when the image has been cropped; the only argument that
* is passed to this callback is the file:// URI of the new image
* @param error callback to be invoked when an error occurs (e.g. can't create file etc.)
* @param promise Promise to be resolved when the image has been cropped; the only argument that
* is passed to this is the file:// URI of the new image
*/
@ReactMethod
public void cropImage(
String uri,
ReadableMap options,
final Callback success,
final Callback error) {
Promise promise) {
ReadableMap offset = options.hasKey("offset") ? options.getMap("offset") : null;
ReadableMap size = options.hasKey("size") ? options.getMap("size") : null;
if (offset == null || size == null ||
Expand All @@ -187,8 +185,7 @@ public void cropImage(
(int) offset.getDouble("y"),
(int) size.getDouble("width"),
(int) size.getDouble("height"),
success,
error);
promise);
if (options.hasKey("displaySize")) {
ReadableMap targetSize = options.getMap("displaySize");
cropTask.setTargetSize(
Expand All @@ -207,8 +204,7 @@ private static class CropTask extends GuardedAsyncTask<Void, Void> {
final int mHeight;
int mTargetWidth = 0;
int mTargetHeight = 0;
final Callback mSuccess;
final Callback mError;
final Promise mPromise;

private CropTask(
ReactContext context,
Expand All @@ -217,8 +213,7 @@ private CropTask(
int y,
int width,
int height,
Callback success,
Callback error) {
Promise promise) {
super(context);
if (x < 0 || y < 0 || width <= 0 || height <= 0) {
throw new JSApplicationIllegalArgumentException(String.format(
Expand All @@ -230,8 +225,7 @@ private CropTask(
mY = y;
mWidth = width;
mHeight = height;
mSuccess = success;
mError = error;
mPromise = promise;
}

public void setTargetSize(int width, int height) {
Expand Down Expand Up @@ -284,9 +278,9 @@ protected void doInBackgroundGuarded(Void... params) {
copyExif(mContext, Uri.parse(mUri), tempFile);
}

mSuccess.invoke(Uri.fromFile(tempFile).toString());
mPromise.resolve(Uri.fromFile(tempFile).toString());
} catch (Exception e) {
mError.invoke(e.getMessage());
mPromise.reject(e);
}
}

Expand Down
22 changes: 14 additions & 8 deletions example/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export default class SquareImageCropper extends React.Component<
async _fetchRandomPhoto() {
this.setState({
randomPhoto: {
uri: `http://placeimg.com/${DEFAULT_IMAGE_WIDTH}/${DEFAULT_IMAGE_HEIGHT}/tech`,
uri: `http://placeimg.com/${DEFAULT_IMAGE_WIDTH}/${DEFAULT_IMAGE_HEIGHT}/tech?${new Date().getTime()}`,
height: DEFAULT_IMAGE_HEIGHT,
width: DEFAULT_IMAGE_WIDTH,
},
Expand Down Expand Up @@ -150,13 +150,19 @@ export default class SquareImageCropper extends React.Component<
);
}

_crop() {
ImageEditor.cropImage(
this.state.randomPhoto.uri,
this._transformData,
croppedImageURI => this.setState({croppedImageURI}),
cropError => this.setState({cropError}),
);
async _crop() {
try {
const croppedImageURI = await ImageEditor.cropImage(
this.state.randomPhoto.uri,
this._transformData,
);

if (croppedImageURI) {
this.setState({croppedImageURI});
}
} catch (cropError) {
this.setState({cropError});
}
}

_reset() {
Expand Down
10 changes: 5 additions & 5 deletions ios/RNCImageEditor.m
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ @implementation RNCImageEditor
*/
RCT_EXPORT_METHOD(cropImage:(NSURLRequest *)imageRequest
cropData:(NSDictionary *)cropData
successCallback:(RCTResponseSenderBlock)successCallback
errorCallback:(RCTResponseErrorBlock)errorCallback)
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
{
CGRect rect = {
[RCTConvert CGPoint:cropData[@"offset"]],
Expand All @@ -52,7 +52,7 @@ @implementation RNCImageEditor

[_bridge.imageLoader loadImageWithURLRequest:imageRequest callback:^(NSError *error, UIImage *image) {
if (error) {
errorCallback(error);
reject(@(error.code).stringValue, error.description, error);
return;
}

Expand All @@ -79,11 +79,11 @@ @implementation RNCImageEditor
NSString *uri = [RNCImageUtils writeImage:imageData toPath:path error:&writeError];

if (writeError != nil) {
errorCallback(writeError);
reject(@(writeError.code).stringValue, writeError.description, writeError);
return;
}

successCallback(@[uri]);
resolve(uri);
}];
}

Expand Down
17 changes: 6 additions & 11 deletions lib/ImageEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,17 @@ class ImageEditor {
/**
* Crop the image specified by the URI param. If URI points to a remote
* image, it will be downloaded automatically. If the image cannot be
* loaded/downloaded, the failure callback will be called. On Android, a
* loaded/downloaded, the promise will be rejected. On Android, a
* downloaded image may be cached in external storage, a publicly accessible
* location, if it has more available space than internal storage.
*
* If the cropping process is successful, the resultant cropped image
* will be stored in the ImageStore, and the URI returned in the success
* callback will point to the image in the store. Remember to delete the
* cropped image from the ImageStore when you are done with it.
* will be stored in the Cache Path, and the URI returned in the promise
* will point to the image in the cache path. Remember to delete the
* cropped image from the cache path when you are done with it.
*/
static cropImage(
uri: string,
cropData: ImageCropData,
success: (uri: string) => void,
failure: (error: Object) => void,
) {
RNCImageEditor.cropImage(uri, cropData, success, failure);
static cropImage(uri: string, cropData: ImageCropData): Promise<string> {
return RNCImageEditor.cropImage(uri, cropData);
}
}

Expand Down
12 changes: 5 additions & 7 deletions typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,19 @@ declare class ImageEditor {
/**
* Crop the image specified by the URI param. If URI points to a remote
* image, it will be downloaded automatically. If the image cannot be
* loaded/downloaded, the failure callback will be called. On Android, a
* loaded/downloaded, the promise will be rejected. On Android, a
* downloaded image may be cached in external storage, a publicly accessible
* location, if it has more available space than internal storage.
*
* If the cropping process is successful, the resultant cropped image
* will be stored in the ImageStore, and the URI returned in the success
* callback will point to the image in the store. Remember to delete the
* cropped image from the ImageStore when you are done with it.
* will be stored in the Cache Path, and the URI returned in the promise
* will point to the image in the cache path. Remember to delete the
* cropped image from the cache path when you are done with it.
*/
static cropImage: (
uri: string,
cropData: ImageCropData,
success: (uri: string) => void,
failure: (error: Object) => void,
) => void
) => Promise<string>
}

export default ImageEditor