diff --git a/src/lib/media/picker.tsx b/src/lib/media/picker.tsx index bf531c98..37e01e67 100644 --- a/src/lib/media/picker.tsx +++ b/src/lib/media/picker.tsx @@ -1,8 +1,9 @@ import { + Image as RNImage, openCamera as openCameraFn, openCropper as openCropperFn, - Image as RNImage, } from 'react-native-image-crop-picker' + import {CameraOpts, CropperOptions} from './types' export {openPicker} from './picker.shared' diff --git a/src/lib/media/picker.web.tsx b/src/lib/media/picker.web.tsx index 995a0c95..fde6a64a 100644 --- a/src/lib/media/picker.web.tsx +++ b/src/lib/media/picker.web.tsx @@ -1,7 +1,8 @@ /// -import {CameraOpts, CropperOptions} from './types' import {Image as RNImage} from 'react-native-image-crop-picker' + +import {CameraOpts, CropperOptions} from './types' export {openPicker} from './picker.shared' import {unstable__openModal} from '#/state/modals' @@ -12,15 +13,17 @@ export async function openCamera(_opts: CameraOpts): Promise { export async function openCropper(opts: CropperOptions): Promise { // TODO handle more opts - return new Promise((resolve, reject) => { + return new Promise(resolve => { 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) - } else { - reject(new Error('Canceled')) } }, }) diff --git a/src/state/modals/index.tsx b/src/state/modals/index.tsx index 0f61a971..cf82bcd0 100644 --- a/src/state/modals/index.tsx +++ b/src/state/modals/index.tsx @@ -47,6 +47,7 @@ export interface EditImageModal { export interface CropImageModal { name: 'crop-image' uri: string + dimensions?: {width: number; height: number} onSelect: (img?: RNImage) => void } diff --git a/src/view/com/modals/crop-image/CropImage.web.tsx b/src/view/com/modals/crop-image/CropImage.web.tsx index 79ff5a02..10cae2f1 100644 --- a/src/view/com/modals/crop-image/CropImage.web.tsx +++ b/src/view/com/modals/crop-image/CropImage.web.tsx @@ -14,11 +14,13 @@ import {Dimensions} from 'lib/media/types' import {getDataUriSize} from 'lib/media/util' import {gradients, s} from 'lib/styles' import {Text} from 'view/com/util/text/Text' +import {calculateDimensions} from './cropImageUtil' enum AspectRatio { Square = 'square', Wide = 'wide', Tall = 'tall', + Custom = 'custom', } const DIMS: Record = { @@ -31,17 +33,24 @@ export const snapPoints = ['0%'] export function Component({ uri, + dimensions, onSelect, }: { uri: string + dimensions?: Dimensions onSelect: (img?: RNImage) => void }) { const {closeModal} = useModalControls() const pal = usePalette('default') const {_} = useLingui() - const [as, setAs] = React.useState(AspectRatio.Square) + const defaultAspectStyle = dimensions + ? AspectRatio.Custom + : AspectRatio.Square + const [as, setAs] = React.useState(defaultAspectStyle) const [scale, setScale] = React.useState(1) const editorRef = React.useRef(null) + const imageEditorWidth = dimensions ? dimensions.width : DIMS[as].width + const imageEditorHeight = dimensions ? dimensions.height : DIMS[as].height const doSetAs = (v: AspectRatio) => () => setAs(v) @@ -57,8 +66,8 @@ export function Component({ path: dataUri, mime: 'image/jpeg', size: getDataUriSize(dataUri), - width: DIMS[as].width, - height: DIMS[as].height, + width: imageEditorWidth, + height: imageEditorHeight, }) } else { onSelect(undefined) @@ -73,7 +82,18 @@ export function Component({ cropperStyle = styles.cropperWide } else if (as === AspectRatio.Tall) { cropperStyle = styles.cropperTall + } else if (as === AspectRatio.Custom) { + const cropperDimensions = calculateDimensions( + 550, + imageEditorHeight, + imageEditorWidth, + ) + cropperStyle = { + width: cropperDimensions.width, + height: cropperDimensions.height, + } } + return ( @@ -81,8 +101,8 @@ export function Component({ ref={editorRef} style={styles.imageEditor} image={uri} - width={DIMS[as].width} - height={DIMS[as].height} + width={imageEditorWidth} + height={imageEditorHeight} scale={scale} border={0} /> @@ -97,36 +117,40 @@ export function Component({ maximumValue={3} containerStyle={styles.slider} /> - - - - - - - - - + {as === AspectRatio.Custom ? null : ( + <> + + + + + + + + + + + )} { + const aspectRatio = originalWidth / originalHeight + const newHeight = maxWidth / aspectRatio + const newWidth = maxWidth + return { + width: newWidth, + height: newHeight, + } +} diff --git a/src/view/com/util/UserBanner.tsx b/src/view/com/util/UserBanner.tsx index 4d73b853..f08044ec 100644 --- a/src/view/com/util/UserBanner.tsx +++ b/src/view/com/util/UserBanner.tsx @@ -1,29 +1,29 @@ import React from 'react' import {StyleSheet, TouchableOpacity, View} from 'react-native' -import {ModerationUI} from '@atproto/api' +import {Image as RNImage} from 'react-native-image-crop-picker' import {Image} from 'expo-image' -import {useLingui} from '@lingui/react' +import {ModerationUI} from '@atproto/api' import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {usePalette} from 'lib/hooks/usePalette' +import { + useCameraPermission, + usePhotoLibraryPermission, +} from 'lib/hooks/usePermissions' import {colors} from 'lib/styles' import {useTheme} from 'lib/ThemeContext' -import {useTheme as useAlfTheme, tokens} from '#/alf' -import {openCamera, openCropper, openPicker} from '../../../lib/media/picker' -import { - usePhotoLibraryPermission, - useCameraPermission, -} from 'lib/hooks/usePermissions' -import {usePalette} from 'lib/hooks/usePalette' import {isAndroid, isNative} from 'platform/detection' -import {Image as RNImage} from 'react-native-image-crop-picker' import {EventStopper} from 'view/com/util/EventStopper' -import * as Menu from '#/components/Menu' +import {tokens, useTheme as useAlfTheme} from '#/alf' import { Camera_Filled_Stroke2_Corner0_Rounded as CameraFilled, Camera_Stroke2_Corner0_Rounded as Camera, } from '#/components/icons/Camera' import {StreamingLive_Stroke2_Corner0_Rounded as Library} from '#/components/icons/StreamingLive' import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash' +import * as Menu from '#/components/Menu' +import {openCamera, openCropper, openPicker} from '../../../lib/media/picker' export function UserBanner({ type,