Skip to content

Commit

Permalink
do not allow to press DONE button quickly multiple times
Browse files Browse the repository at this point in the history
  • Loading branch information
ggunti committed Jul 30, 2021
1 parent 55b12b9 commit 5a9c38d
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 50 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Step 2 is not needed for react-native >= 0.60 because of autolinking. Instead ru
-------------
| Prop | Type | Description |
| :------------ |:---------------:| :---------------|
| onDone | `function` | A function which accepts 1 argument `croppedImageUri`. Called when user press the 'DONE' button |
| onDone | `function` | A function which accepts 1 argument `croppedImageUri`. Called after user press the 'DONE' button and cropped image uri is obtained |
| onError | `function` | A function which accepts 1 argument `err` of type `Error`. Called when rotation or cropping fails |
| onCancel | `function` | A function without arguments. Called when user press the 'CANCEL' button |
| imageUri | `string` | The uri of the image you want to crop or rotate |
Expand Down Expand Up @@ -121,7 +121,7 @@ class AmazingCropperPage extends Component {
-------------

Write your custom footer component.</br>
Don't forget to call the **props.onDone**, **props.onRotate** and **props.onCancel** methods inside it (the Cropper will pass them automatically to your footer component). Example of custom footer component:
Don't forget to call the **props.onDone**, **props.onRotate** and **props.onCancel** methods inside it (the Cropper will pass them automatically to your footer component). Also you can use the **props.isLoading** property for example to show a loading indicator immediately after the 'DONE' button is pressed (and until **props.onDone** is resolved with the cropped image uri). Example of custom footer component:

```javascript
import React from 'react';
Expand All @@ -137,7 +137,8 @@ const CustomCropperFooter = (props) => (
<TouchableOpacity onPress={props.onRotate} style={styles.touchable}>
<MaterialCommunityIcon name='format-rotate-90' style={styles.rotateIcon} />
</TouchableOpacity>
<TouchableOpacity onPress={props.onDone} style={styles.touchable}>
{/* do not allow user to press 'DONE' button quickly multiple times, so disable it while props.isLoading is true */}
<TouchableOpacity onPress={props.onDone} disabled={props.isLoading} style={styles.touchable}>
<Text style={styles.text}>DONE</Text>
</TouchableOpacity>
</View>
Expand All @@ -148,7 +149,8 @@ export default CustomCropperFooter;
CustomCropperFooter.propTypes = {
onDone: PropTypes.func,
onRotate: PropTypes.func,
onCancel: PropTypes.func
onCancel: PropTypes.func,
isLoading: PropTypes.bool
}

const styles = StyleSheet.create({
Expand Down Expand Up @@ -213,7 +215,7 @@ class AmazingCropperPage extends Component {
return (
<AmazingCropper
// Use your custom footer component
// Do NOT pass onDone, onRotate and onCancel to the footer component, the Cropper will do it for you
// Do NOT pass onDone, onRotate, onCancel and isLoading to the footer component, the Cropper will do it for you
footerComponent={<CustomCropperFooter />}
onDone={this.onDone}
onError={this.onError}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-amazing-cropper",
"version": "0.1.6",
"version": "0.1.7",
"description": "Custom react native cropper with rotation",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
81 changes: 40 additions & 41 deletions src/Cropper/Cropper.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ interface State {
BOTTOM_VALUE: number;
RIGHT_VALUE: number;
rotation: number;
isLoading: boolean;
}

class CropperPage extends Component<CropperPageProps, State> {
Expand Down Expand Up @@ -181,6 +182,7 @@ class CropperPage extends Component<CropperPageProps, State> {
BOTTOM_VALUE,
RIGHT_VALUE,
rotation: props.initialRotation,
isLoading: false,
};
}

Expand Down Expand Up @@ -664,47 +666,43 @@ class CropperPage extends Component<CropperPageProps, State> {
return null;
}

//this.setState({ isSaving: true });
const IMAGE_W = this.props.COMPONENT_WIDTH - this.state.RIGHT_LIMIT - this.state.LEFT_LIMIT;
const IMAGE_H = this.props.COMPONENT_HEIGHT - this.state.BOTTOM_LIMIT - this.state.TOP_LIMIT;
let x = this.state.leftPosition.x._value - this.state.LEFT_LIMIT + this.props.BORDER_WIDTH;
let y = this.state.topPosition.y._value - this.state.TOP_LIMIT + this.props.BORDER_WIDTH;
let width = this.state.rightPosition.x._value - this.state.leftPosition.x._value - this.props.BORDER_WIDTH;
let height = this.state.bottomPosition.y._value - this.state.topPosition.y._value - this.props.BORDER_WIDTH;
let imageWidth = this.props.imageWidth > 0 ? this.props.imageWidth : 1280; // 340
let imageHeight = this.props.imageHeight > 0 ? this.props.imageHeight : 747; // 500
if (this.state.rotation % 180 === 90) {
const pivot = imageWidth;
imageWidth = imageHeight;
imageHeight = pivot;
}
width = (width * imageWidth) / IMAGE_W;
height = (height * imageHeight) / IMAGE_H;
x = (x * imageWidth) / IMAGE_W;
y = (y * imageHeight) / IMAGE_H;
const cropData = {
offset: { x, y },
size: { width, height },
resizeMode: 'stretch',
} as ImageCropData;
RNImageRotate.rotateImage(
this.props.imageUri,
this.state.rotation,
(rotatedUri: string) => {
//
ImageEditor.cropImage(rotatedUri, cropData)
.then(croppedUri => {
this.props.onDone(croppedUri);
})
.catch((err: Error) => {
this.props.onError(err);
});
//
},
(err: Error) => {
this.props.onError(err);
},
);
this.setState({ isLoading: true }, () => {
const IMAGE_W = this.props.COMPONENT_WIDTH - this.state.RIGHT_LIMIT - this.state.LEFT_LIMIT;
const IMAGE_H = this.props.COMPONENT_HEIGHT - this.state.BOTTOM_LIMIT - this.state.TOP_LIMIT;
let x = this.state.leftPosition.x._value - this.state.LEFT_LIMIT + this.props.BORDER_WIDTH;
let y = this.state.topPosition.y._value - this.state.TOP_LIMIT + this.props.BORDER_WIDTH;
let width = this.state.rightPosition.x._value - this.state.leftPosition.x._value - this.props.BORDER_WIDTH;
let height = this.state.bottomPosition.y._value - this.state.topPosition.y._value - this.props.BORDER_WIDTH;
let imageWidth = this.props.imageWidth > 0 ? this.props.imageWidth : 1280; // 340
let imageHeight = this.props.imageHeight > 0 ? this.props.imageHeight : 747; // 500
if (this.state.rotation % 180 === 90) {
const pivot = imageWidth;
imageWidth = imageHeight;
imageHeight = pivot;
}
width = (width * imageWidth) / IMAGE_W;
height = (height * imageHeight) / IMAGE_H;
x = (x * imageWidth) / IMAGE_W;
y = (y * imageHeight) / IMAGE_H;
const cropData = {
offset: { x, y },
size: { width, height },
resizeMode: 'stretch',
} as ImageCropData;
RNImageRotate.rotateImage(
this.props.imageUri,
this.state.rotation,
(rotatedUri: string) =>
ImageEditor.cropImage(rotatedUri, cropData)
.then(croppedUri => this.props.onDone(croppedUri))
.catch((err: Error) => this.props.onError(err))
.finally(() => this.setState({ isLoading: false })),
(err: Error) => {
this.props.onError(err);
this.setState({ isLoading: false });
},
);
});
};

render() {
Expand All @@ -729,6 +727,7 @@ class CropperPage extends Component<CropperPageProps, State> {
onDone={this.onDone}
onRotate={this.onRotate}
onCancel={this.onCancel}
isLoading={this.state.isLoading}
topOuterPanResponder={this.state.topOuterPanResponder}
leftOuterPanResponder={this.state.leftOuterPanResponder}
bottomOuterPanResponder={this.state.bottomOuterPanResponder}
Expand Down
2 changes: 2 additions & 0 deletions src/Cropper/Cropper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface CropperProps {
onDone: () => void;
onRotate: () => void;
onCancel: () => void;
isLoading: boolean;
topOuterPanResponder: PanResponderInstance;
leftOuterPanResponder: PanResponderInstance;
bottomOuterPanResponder: PanResponderInstance;
Expand Down Expand Up @@ -58,6 +59,7 @@ const Cropper: React.FC<CropperProps> = props => {
onDone: props.onDone,
onRotate: props.onRotate,
onCancel: props.onCancel,
isLoading: props.isLoading,
})}
</View>
{/*
Expand Down
7 changes: 4 additions & 3 deletions src/common/DefaultFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,21 @@ export type DefaultFooterProps = {
onDone?: () => any;
onRotate?: () => any;
onCancel?: () => any;
isLoading?: boolean;
doneText: string;
rotateText: string;
cancelText: string;
};

const DefaultFooter: React.FC<DefaultFooterProps> = props => (
<View style={styles.buttonsContainer}>
<TouchableOpacity onPress={props.onCancel} style={styles.touchable}>
<TouchableOpacity disabled={props.isLoading} onPress={props.onCancel} style={styles.touchable}>
<Text style={styles.text}>{props.cancelText}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={props.onRotate} style={styles.touchable}>
<TouchableOpacity disabled={props.isLoading} onPress={props.onRotate} style={styles.touchable}>
<Text style={styles.text}>{props.rotateText}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={props.onDone} style={styles.touchable}>
<TouchableOpacity disabled={props.isLoading} onPress={props.onDone} style={styles.touchable}>
<Text style={styles.text}>{props.doneText}</Text>
</TouchableOpacity>
</View>
Expand Down

0 comments on commit 5a9c38d

Please sign in to comment.