diff --git a/src/lib/media/picker.web.tsx b/src/lib/media/picker.web.tsx index 8782e14570..7e2562e297 100644 --- a/src/lib/media/picker.web.tsx +++ b/src/lib/media/picker.web.tsx @@ -17,10 +17,6 @@ export async function openCropper(opts: CropperOptions): Promise { unstable__openModal({ name: 'crop-image', uri: opts.path, - dimensions: - opts.height && opts.width - ? {width: opts.width, height: opts.height} - : undefined, onSelect: (img?: RNImage) => { if (img) { resolve(img) diff --git a/src/state/modals/index.tsx b/src/state/modals/index.tsx index 8298d282bc..f7eb762462 100644 --- a/src/state/modals/index.tsx +++ b/src/state/modals/index.tsx @@ -1,4 +1,5 @@ import React from 'react' +import {Image as RNImage} from 'react-native-image-crop-picker' import {AppBskyActorDefs, AppBskyGraphDefs} from '@atproto/api' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' @@ -35,6 +36,12 @@ export interface ListAddRemoveUsersModal { ) => void } +export interface CropImageModal { + name: 'crop-image' + uri: string + onSelect: (img?: RNImage) => void +} + export interface DeleteAccountModal { name: 'delete-account' } @@ -130,6 +137,7 @@ export type Modal = | ListAddRemoveUsersModal // Posts + | CropImageModal | RepostModal | SelfLabelModal | ThreadgateModal diff --git a/src/view/com/modals/CropImage.web.tsx b/src/view/com/modals/CropImage.web.tsx new file mode 100644 index 0000000000..9ccdaf043d --- /dev/null +++ b/src/view/com/modals/CropImage.web.tsx @@ -0,0 +1,134 @@ +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, {PixelCrop} from 'react-image-crop' + +import {useModalControls} from '#/state/modals' +import {usePalette} from 'lib/hooks/usePalette' +import {getDataUriSize} from 'lib/media/util' +import {gradients, s} from 'lib/styles' +import {Text} from 'view/com/util/text/Text' + +export const snapPoints = ['0%'] + +export function Component({ + uri, + onSelect, +}: { + uri: string + onSelect: (img?: RNImage) => void +}) { + const pal = usePalette('default') + const {_} = useLingui() + + const {closeModal} = useModalControls() + + const [crop, setCrop] = React.useState() + + const isEmpty = !crop || (crop.width || crop.height) === 0 + + const onPressCancel = () => { + onSelect(undefined) + closeModal() + } + const onPressDone = async () => { + if (!isEmpty) { + const result = await manipulateAsync( + uri, + [ + { + crop: { + originX: crop.x, + originY: crop.y, + width: crop.width, + height: crop.height, + }, + }, + ], + { + 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, + }) + } else { + onSelect(undefined) + } + + closeModal() + } + + return ( + + + + + + + + + + Cancel + + + + + + + Done + + + + + + ) +} + +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, + }, +}) diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx index 16da4205f6..16d5ecb39a 100644 --- a/src/view/com/modals/Modal.web.tsx +++ b/src/view/com/modals/Modal.web.tsx @@ -12,6 +12,7 @@ import * as ChangeEmailModal from './ChangeEmail' import * as ChangeHandleModal from './ChangeHandle' import * as ChangePasswordModal from './ChangePassword' import * as CreateOrEditListModal from './CreateOrEditList' +import * as CropImage from './CropImage.web' import * as DeleteAccountModal from './DeleteAccount' import * as EditProfileModal from './EditProfile' import * as InviteCodesModal from './InviteCodes' @@ -69,6 +70,8 @@ function Modal({modal}: {modal: ModalIface}) { element = } else if (modal.name === 'list-add-remove-users') { element = + } else if (modal.name === 'crop-image') { + element = } else if (modal.name === 'delete-account') { element = } else if (modal.name === 'repost') {