-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* new image editor * Rm react-avatar-editor --------- Co-authored-by: Dan Abramov <[email protected]>
- Loading branch information
Showing
14 changed files
with
318 additions
and
293 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import React from 'react' | ||
|
||
import {ComposerImage} from '#/state/gallery' | ||
import * as Dialog from '#/components/Dialog' | ||
|
||
export type EditImageDialogProps = { | ||
control: Dialog.DialogOuterProps['control'] | ||
image: ComposerImage | ||
onChange: (next: ComposerImage) => void | ||
} | ||
|
||
export const EditImageDialog = ({}: EditImageDialogProps): React.ReactNode => { | ||
return null | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import 'react-image-crop/dist/ReactCrop.css' | ||
|
||
import React from 'react' | ||
import {View} from 'react-native' | ||
import {msg, Trans} from '@lingui/macro' | ||
import {useLingui} from '@lingui/react' | ||
import ReactCrop, {PercentCrop} from 'react-image-crop' | ||
|
||
import { | ||
ImageSource, | ||
ImageTransformation, | ||
manipulateImage, | ||
} from '#/state/gallery' | ||
import {atoms as a} from '#/alf' | ||
import {Button, ButtonText} from '#/components/Button' | ||
import * as Dialog from '#/components/Dialog' | ||
import {Text} from '#/components/Typography' | ||
import {EditImageDialogProps} from './EditImageDialog' | ||
|
||
export const EditImageDialog = (props: EditImageDialogProps) => { | ||
return ( | ||
<Dialog.Outer control={props.control}> | ||
<EditImageInner key={props.image.source.id} {...props} /> | ||
</Dialog.Outer> | ||
) | ||
} | ||
|
||
const EditImageInner = ({control, image, onChange}: EditImageDialogProps) => { | ||
const {_} = useLingui() | ||
|
||
const source = image.source | ||
|
||
const initialCrop = getInitialCrop(source, image.manips) | ||
const [crop, setCrop] = React.useState(initialCrop) | ||
|
||
const isEmpty = !crop || (crop.width || crop.height) === 0 | ||
const isNew = initialCrop ? true : !isEmpty | ||
|
||
const onPressSubmit = React.useCallback(async () => { | ||
const result = await manipulateImage(image, { | ||
crop: | ||
crop && (crop.width || crop.height) !== 0 | ||
? { | ||
originX: (crop.x * source.width) / 100, | ||
originY: (crop.y * source.height) / 100, | ||
width: (crop.width * source.width) / 100, | ||
height: (crop.height * source.height) / 100, | ||
} | ||
: undefined, | ||
}) | ||
|
||
onChange(result) | ||
control.close() | ||
}, [crop, image, source, control, onChange]) | ||
|
||
return ( | ||
<Dialog.Inner label={_(msg`Edit image`)}> | ||
<Dialog.Close /> | ||
|
||
<Text style={[a.text_2xl, a.font_bold, a.leading_tight, a.pb_sm]}> | ||
<Trans>Edit image</Trans> | ||
</Text> | ||
|
||
<View style={[a.align_center]}> | ||
<ReactCrop | ||
crop={crop} | ||
onChange={(_pixelCrop, percentCrop) => setCrop(percentCrop)} | ||
className="ReactCrop--no-animate"> | ||
<img src={source.path} style={{maxHeight: `50vh`}} /> | ||
</ReactCrop> | ||
</View> | ||
|
||
<View style={[a.mt_md, a.gap_md]}> | ||
<Button | ||
disabled={!isNew} | ||
label={_(msg`Save`)} | ||
size="large" | ||
color="primary" | ||
variant="solid" | ||
onPress={onPressSubmit}> | ||
<ButtonText> | ||
<Trans>Save</Trans> | ||
</ButtonText> | ||
</Button> | ||
</View> | ||
</Dialog.Inner> | ||
) | ||
} | ||
|
||
const getInitialCrop = ( | ||
source: ImageSource, | ||
manips: ImageTransformation | undefined, | ||
): PercentCrop | undefined => { | ||
const initialArea = manips?.crop | ||
|
||
if (initialArea) { | ||
return { | ||
unit: '%', | ||
x: (initialArea.originX / source.width) * 100, | ||
y: (initialArea.originY / source.height) * 100, | ||
width: (initialArea.width / source.width) * 100, | ||
height: (initialArea.height / source.height) * 100, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import React from 'react' | ||
import {StyleSheet, TouchableOpacity, View} from 'react-native' | ||
import {Image as RNImage} from 'react-native-image-crop-picker' | ||
import {manipulateAsync, SaveFormat} from 'expo-image-manipulator' | ||
import {LinearGradient} from 'expo-linear-gradient' | ||
import {msg, Trans} from '@lingui/macro' | ||
import {useLingui} from '@lingui/react' | ||
import ReactCrop, {PercentCrop} from 'react-image-crop' | ||
|
||
import {usePalette} from '#/lib/hooks/usePalette' | ||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' | ||
import {getDataUriSize} from '#/lib/media/util' | ||
import {gradients, s} from '#/lib/styles' | ||
import {useModalControls} from '#/state/modals' | ||
import {Text} from '#/view/com/util/text/Text' | ||
|
||
export const snapPoints = ['0%'] | ||
|
||
export function Component({ | ||
uri, | ||
aspect, | ||
circular, | ||
onSelect, | ||
}: { | ||
uri: string | ||
aspect?: number | ||
circular?: boolean | ||
onSelect: (img?: RNImage) => void | ||
}) { | ||
const pal = usePalette('default') | ||
const {_} = useLingui() | ||
|
||
const {closeModal} = useModalControls() | ||
const {isMobile} = useWebMediaQueries() | ||
|
||
const imageRef = React.useRef<HTMLImageElement>(null) | ||
const [crop, setCrop] = React.useState<PercentCrop>() | ||
|
||
const isEmpty = !crop || (crop.width || crop.height) === 0 | ||
|
||
const onPressCancel = () => { | ||
onSelect(undefined) | ||
closeModal() | ||
} | ||
const onPressDone = async () => { | ||
const img = imageRef.current! | ||
|
||
const result = await manipulateAsync( | ||
uri, | ||
isEmpty | ||
? [] | ||
: [ | ||
{ | ||
crop: { | ||
originX: (crop.x * img.naturalWidth) / 100, | ||
originY: (crop.y * img.naturalHeight) / 100, | ||
width: (crop.width * img.naturalWidth) / 100, | ||
height: (crop.height * img.naturalHeight) / 100, | ||
}, | ||
}, | ||
], | ||
{ | ||
base64: true, | ||
format: SaveFormat.JPEG, | ||
}, | ||
) | ||
|
||
onSelect({ | ||
path: result.uri, | ||
mime: 'image/jpeg', | ||
size: result.base64 !== undefined ? getDataUriSize(result.base64) : 0, | ||
width: result.width, | ||
height: result.height, | ||
}) | ||
|
||
closeModal() | ||
} | ||
|
||
return ( | ||
<View> | ||
<View style={[styles.cropper, pal.borderDark]}> | ||
<ReactCrop | ||
aspect={aspect} | ||
crop={crop} | ||
onChange={(_pixelCrop, percentCrop) => setCrop(percentCrop)} | ||
circularCrop={circular}> | ||
<img ref={imageRef} src={uri} style={{maxHeight: '75vh'}} /> | ||
</ReactCrop> | ||
</View> | ||
<View style={[styles.btns, isMobile && {paddingHorizontal: 16}]}> | ||
<TouchableOpacity | ||
onPress={onPressCancel} | ||
accessibilityRole="button" | ||
accessibilityLabel={_(msg`Cancel image crop`)} | ||
accessibilityHint={_(msg`Exits image cropping process`)}> | ||
<Text type="xl" style={pal.link}> | ||
<Trans>Cancel</Trans> | ||
</Text> | ||
</TouchableOpacity> | ||
<View style={s.flex1} /> | ||
<TouchableOpacity | ||
onPress={onPressDone} | ||
accessibilityRole="button" | ||
accessibilityLabel={_(msg`Save image crop`)} | ||
accessibilityHint={_(msg`Saves image crop settings`)}> | ||
<LinearGradient | ||
colors={[gradients.blueLight.start, gradients.blueLight.end]} | ||
start={{x: 0, y: 0}} | ||
end={{x: 1, y: 1}} | ||
style={[styles.btn]}> | ||
<Text type="xl-medium" style={s.white}> | ||
<Trans>Done</Trans> | ||
</Text> | ||
</LinearGradient> | ||
</TouchableOpacity> | ||
</View> | ||
</View> | ||
) | ||
} | ||
|
||
const styles = StyleSheet.create({ | ||
cropper: { | ||
marginLeft: 'auto', | ||
marginRight: 'auto', | ||
borderWidth: 1, | ||
borderRadius: 4, | ||
overflow: 'hidden', | ||
alignItems: 'center', | ||
}, | ||
ctrls: { | ||
flexDirection: 'row', | ||
alignItems: 'center', | ||
marginTop: 10, | ||
}, | ||
btns: { | ||
flexDirection: 'row', | ||
alignItems: 'center', | ||
marginTop: 10, | ||
}, | ||
btn: { | ||
borderRadius: 4, | ||
paddingVertical: 8, | ||
paddingHorizontal: 24, | ||
}, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.