Fixes to web avatar and banner cropper
This commit is contained in:
@@ -21,6 +21,8 @@ export async function openCropper(opts: CropperOptions): Promise<RNImage> {
|
||||
opts.width && opts.height
|
||||
? {width: opts.width, height: opts.height}
|
||||
: undefined,
|
||||
aspect: opts.webAspectRatio,
|
||||
circular: opts.webCircularCrop,
|
||||
onSelect: (img?: RNImage) => {
|
||||
if (img) {
|
||||
resolve(img)
|
||||
|
||||
@@ -18,4 +18,7 @@ export interface CameraOpts {
|
||||
cropperCircleOverlay?: boolean
|
||||
}
|
||||
|
||||
export type CropperOptions = Parameters<typeof openCropper>[0]
|
||||
export type CropperOptions = Parameters<typeof openCropper>[0] & {
|
||||
webAspectRatio?: number
|
||||
webCircularCrop?: boolean
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ export interface CropImageModal {
|
||||
name: 'crop-image'
|
||||
uri: string
|
||||
dimensions?: {width: number; height: number}
|
||||
aspect?: number
|
||||
circular?: boolean
|
||||
onSelect: (img?: RNImage) => void
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ const EditImageInner = ({control, image, onChange}: EditImageDialogProps) => {
|
||||
|
||||
const {initialCrop} = React.useMemo(() => {
|
||||
const initialArea = manips?.crop
|
||||
|
||||
let crop: PercentCrop | undefined
|
||||
|
||||
if (initialArea) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import ReactCrop, {PercentCrop} from 'react-image-crop'
|
||||
|
||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {getDataUriSize} from 'lib/media/util'
|
||||
@@ -17,60 +18,60 @@ export const snapPoints = ['0%']
|
||||
|
||||
export function Component({
|
||||
uri,
|
||||
dimensions,
|
||||
aspect,
|
||||
circular,
|
||||
onSelect,
|
||||
}: {
|
||||
uri: string
|
||||
dimensions?: {width: number; height: number}
|
||||
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 aspect = dimensions ? dimensions.width / dimensions.height : undefined
|
||||
|
||||
const onPressCancel = () => {
|
||||
onSelect(undefined)
|
||||
closeModal()
|
||||
}
|
||||
const onPressDone = async () => {
|
||||
if (!isEmpty) {
|
||||
const img = imageRef.current!
|
||||
const img = imageRef.current!
|
||||
|
||||
const result = await manipulateAsync(
|
||||
uri,
|
||||
[
|
||||
{
|
||||
crop: {
|
||||
originX: (crop.x * img.width) / 100,
|
||||
originY: (crop.y * img.height) / 100,
|
||||
width: (crop.width * img.width) / 100,
|
||||
height: (crop.height * img.height) / 100,
|
||||
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,
|
||||
},
|
||||
)
|
||||
],
|
||||
{
|
||||
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)
|
||||
}
|
||||
onSelect({
|
||||
path: result.uri,
|
||||
mime: 'image/jpeg',
|
||||
size: result.base64 !== undefined ? getDataUriSize(result.base64) : 0,
|
||||
width: result.width,
|
||||
height: result.height,
|
||||
})
|
||||
|
||||
closeModal()
|
||||
}
|
||||
@@ -81,11 +82,12 @@ export function Component({
|
||||
<ReactCrop
|
||||
aspect={aspect}
|
||||
crop={crop}
|
||||
onChange={(_, next) => setCrop(next)}>
|
||||
onChange={(_, next) => setCrop(next)}
|
||||
circularCrop={circular}>
|
||||
<img ref={imageRef} src={uri} style={{maxHeight: '75vh'}} />
|
||||
</ReactCrop>
|
||||
</View>
|
||||
<View style={styles.btns}>
|
||||
<View style={[styles.btns, isMobile && {paddingHorizontal: 16}]}>
|
||||
<TouchableOpacity
|
||||
onPress={onPressCancel}
|
||||
accessibilityRole="button"
|
||||
|
||||
@@ -291,6 +291,8 @@ let EditableUserAvatar = ({
|
||||
height: item.height,
|
||||
width: item.width,
|
||||
path: item.path,
|
||||
webAspectRatio: 1,
|
||||
webCircularCrop: true,
|
||||
})
|
||||
|
||||
onSelectNewAvatar(croppedImage)
|
||||
|
||||
@@ -72,6 +72,7 @@ export function UserBanner({
|
||||
path: items[0].path,
|
||||
width: 3000,
|
||||
height: 1000,
|
||||
webAspectRatio: 3,
|
||||
}),
|
||||
)
|
||||
} catch (e: any) {
|
||||
|
||||
Reference in New Issue
Block a user