Fixes to web avatar and banner cropper

This commit is contained in:
Paul Frazee
2024-05-08 22:26:24 -07:00
parent d57b344cd5
commit 8a12b606ee
7 changed files with 46 additions and 35 deletions
+2
View File
@@ -21,6 +21,8 @@ export async function openCropper(opts: CropperOptions): Promise<RNImage> {
opts.width && opts.height opts.width && opts.height
? {width: opts.width, height: opts.height} ? {width: opts.width, height: opts.height}
: undefined, : undefined,
aspect: opts.webAspectRatio,
circular: opts.webCircularCrop,
onSelect: (img?: RNImage) => { onSelect: (img?: RNImage) => {
if (img) { if (img) {
resolve(img) resolve(img)
+4 -1
View File
@@ -18,4 +18,7 @@ export interface CameraOpts {
cropperCircleOverlay?: boolean cropperCircleOverlay?: boolean
} }
export type CropperOptions = Parameters<typeof openCropper>[0] export type CropperOptions = Parameters<typeof openCropper>[0] & {
webAspectRatio?: number
webCircularCrop?: boolean
}
+2
View File
@@ -40,6 +40,8 @@ export interface CropImageModal {
name: 'crop-image' name: 'crop-image'
uri: string uri: string
dimensions?: {width: number; height: number} dimensions?: {width: number; height: number}
aspect?: number
circular?: boolean
onSelect: (img?: RNImage) => void onSelect: (img?: RNImage) => void
} }
@@ -29,7 +29,6 @@ const EditImageInner = ({control, image, onChange}: EditImageDialogProps) => {
const {initialCrop} = React.useMemo(() => { const {initialCrop} = React.useMemo(() => {
const initialArea = manips?.crop const initialArea = manips?.crop
let crop: PercentCrop | undefined let crop: PercentCrop | undefined
if (initialArea) { if (initialArea) {
+35 -33
View File
@@ -7,6 +7,7 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import ReactCrop, {PercentCrop} from 'react-image-crop' import ReactCrop, {PercentCrop} from 'react-image-crop'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {useModalControls} from '#/state/modals' import {useModalControls} from '#/state/modals'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
import {getDataUriSize} from 'lib/media/util' import {getDataUriSize} from 'lib/media/util'
@@ -17,60 +18,60 @@ export const snapPoints = ['0%']
export function Component({ export function Component({
uri, uri,
dimensions, aspect,
circular,
onSelect, onSelect,
}: { }: {
uri: string uri: string
dimensions?: {width: number; height: number} aspect?: number
circular?: boolean
onSelect: (img?: RNImage) => void onSelect: (img?: RNImage) => void
}) { }) {
const pal = usePalette('default') const pal = usePalette('default')
const {_} = useLingui() const {_} = useLingui()
const {closeModal} = useModalControls() const {closeModal} = useModalControls()
const {isMobile} = useWebMediaQueries()
const imageRef = React.useRef<HTMLImageElement>(null) const imageRef = React.useRef<HTMLImageElement>(null)
const [crop, setCrop] = React.useState<PercentCrop>() const [crop, setCrop] = React.useState<PercentCrop>()
const isEmpty = !crop || (crop.width || crop.height) === 0 const isEmpty = !crop || (crop.width || crop.height) === 0
const aspect = dimensions ? dimensions.width / dimensions.height : undefined
const onPressCancel = () => { const onPressCancel = () => {
onSelect(undefined) onSelect(undefined)
closeModal() closeModal()
} }
const onPressDone = async () => { const onPressDone = async () => {
if (!isEmpty) { const img = imageRef.current!
const img = imageRef.current!
const result = await manipulateAsync( const result = await manipulateAsync(
uri, uri,
[ isEmpty
{ ? []
crop: { : [
originX: (crop.x * img.width) / 100, {
originY: (crop.y * img.height) / 100, crop: {
width: (crop.width * img.width) / 100, originX: (crop.x * img.naturalWidth) / 100,
height: (crop.height * img.height) / 100, originY: (crop.y * img.naturalHeight) / 100,
width: (crop.width * img.naturalWidth) / 100,
height: (crop.height * img.naturalHeight) / 100,
},
}, },
}, ],
], {
{ base64: true,
base64: true, format: SaveFormat.JPEG,
format: SaveFormat.JPEG, },
}, )
)
onSelect({ onSelect({
path: result.uri, path: result.uri,
mime: 'image/jpeg', mime: 'image/jpeg',
size: result.base64 !== undefined ? getDataUriSize(result.base64) : 0, size: result.base64 !== undefined ? getDataUriSize(result.base64) : 0,
width: result.width, width: result.width,
height: result.height, height: result.height,
}) })
} else {
onSelect(undefined)
}
closeModal() closeModal()
} }
@@ -81,11 +82,12 @@ export function Component({
<ReactCrop <ReactCrop
aspect={aspect} aspect={aspect}
crop={crop} crop={crop}
onChange={(_, next) => setCrop(next)}> onChange={(_, next) => setCrop(next)}
circularCrop={circular}>
<img ref={imageRef} src={uri} style={{maxHeight: '75vh'}} /> <img ref={imageRef} src={uri} style={{maxHeight: '75vh'}} />
</ReactCrop> </ReactCrop>
</View> </View>
<View style={styles.btns}> <View style={[styles.btns, isMobile && {paddingHorizontal: 16}]}>
<TouchableOpacity <TouchableOpacity
onPress={onPressCancel} onPress={onPressCancel}
accessibilityRole="button" accessibilityRole="button"
+2
View File
@@ -291,6 +291,8 @@ let EditableUserAvatar = ({
height: item.height, height: item.height,
width: item.width, width: item.width,
path: item.path, path: item.path,
webAspectRatio: 1,
webCircularCrop: true,
}) })
onSelectNewAvatar(croppedImage) onSelectNewAvatar(croppedImage)
+1
View File
@@ -72,6 +72,7 @@ export function UserBanner({
path: items[0].path, path: items[0].path,
width: 3000, width: 3000,
height: 1000, height: 1000,
webAspectRatio: 3,
}), }),
) )
} catch (e: any) { } catch (e: any) {