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
|
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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user