Merge branch 'fix-banner-cropper' of https://github.com/piotrpalek/social-app into piotrpalek-fix-banner-cropper
commit
e1940983a3
|
@ -1,8 +1,9 @@
|
||||||
import {
|
import {
|
||||||
|
Image as RNImage,
|
||||||
openCamera as openCameraFn,
|
openCamera as openCameraFn,
|
||||||
openCropper as openCropperFn,
|
openCropper as openCropperFn,
|
||||||
Image as RNImage,
|
|
||||||
} from 'react-native-image-crop-picker'
|
} from 'react-native-image-crop-picker'
|
||||||
|
|
||||||
import {CameraOpts, CropperOptions} from './types'
|
import {CameraOpts, CropperOptions} from './types'
|
||||||
export {openPicker} from './picker.shared'
|
export {openPicker} from './picker.shared'
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
/// <reference lib="dom" />
|
/// <reference lib="dom" />
|
||||||
|
|
||||||
import {CameraOpts, CropperOptions} from './types'
|
|
||||||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||||
|
|
||||||
|
import {CameraOpts, CropperOptions} from './types'
|
||||||
export {openPicker} from './picker.shared'
|
export {openPicker} from './picker.shared'
|
||||||
import {unstable__openModal} from '#/state/modals'
|
import {unstable__openModal} from '#/state/modals'
|
||||||
|
|
||||||
|
@ -12,15 +13,17 @@ export async function openCamera(_opts: CameraOpts): Promise<RNImage> {
|
||||||
|
|
||||||
export async function openCropper(opts: CropperOptions): Promise<RNImage> {
|
export async function openCropper(opts: CropperOptions): Promise<RNImage> {
|
||||||
// TODO handle more opts
|
// TODO handle more opts
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise(resolve => {
|
||||||
unstable__openModal({
|
unstable__openModal({
|
||||||
name: 'crop-image',
|
name: 'crop-image',
|
||||||
uri: opts.path,
|
uri: opts.path,
|
||||||
|
dimensions:
|
||||||
|
opts.height && opts.width
|
||||||
|
? {width: opts.width, height: opts.height}
|
||||||
|
: undefined,
|
||||||
onSelect: (img?: RNImage) => {
|
onSelect: (img?: RNImage) => {
|
||||||
if (img) {
|
if (img) {
|
||||||
resolve(img)
|
resolve(img)
|
||||||
} else {
|
|
||||||
reject(new Error('Canceled'))
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -47,6 +47,7 @@ export interface EditImageModal {
|
||||||
export interface CropImageModal {
|
export interface CropImageModal {
|
||||||
name: 'crop-image'
|
name: 'crop-image'
|
||||||
uri: string
|
uri: string
|
||||||
|
dimensions?: {width: number; height: number}
|
||||||
onSelect: (img?: RNImage) => void
|
onSelect: (img?: RNImage) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,11 +14,13 @@ import {Dimensions} from 'lib/media/types'
|
||||||
import {getDataUriSize} from 'lib/media/util'
|
import {getDataUriSize} from 'lib/media/util'
|
||||||
import {gradients, s} from 'lib/styles'
|
import {gradients, s} from 'lib/styles'
|
||||||
import {Text} from 'view/com/util/text/Text'
|
import {Text} from 'view/com/util/text/Text'
|
||||||
|
import {calculateDimensions} from './cropImageUtil'
|
||||||
|
|
||||||
enum AspectRatio {
|
enum AspectRatio {
|
||||||
Square = 'square',
|
Square = 'square',
|
||||||
Wide = 'wide',
|
Wide = 'wide',
|
||||||
Tall = 'tall',
|
Tall = 'tall',
|
||||||
|
Custom = 'custom',
|
||||||
}
|
}
|
||||||
|
|
||||||
const DIMS: Record<string, Dimensions> = {
|
const DIMS: Record<string, Dimensions> = {
|
||||||
|
@ -31,17 +33,24 @@ export const snapPoints = ['0%']
|
||||||
|
|
||||||
export function Component({
|
export function Component({
|
||||||
uri,
|
uri,
|
||||||
|
dimensions,
|
||||||
onSelect,
|
onSelect,
|
||||||
}: {
|
}: {
|
||||||
uri: string
|
uri: string
|
||||||
|
dimensions?: Dimensions
|
||||||
onSelect: (img?: RNImage) => void
|
onSelect: (img?: RNImage) => void
|
||||||
}) {
|
}) {
|
||||||
const {closeModal} = useModalControls()
|
const {closeModal} = useModalControls()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const [as, setAs] = React.useState<AspectRatio>(AspectRatio.Square)
|
const defaultAspectStyle = dimensions
|
||||||
|
? AspectRatio.Custom
|
||||||
|
: AspectRatio.Square
|
||||||
|
const [as, setAs] = React.useState<AspectRatio>(defaultAspectStyle)
|
||||||
const [scale, setScale] = React.useState<number>(1)
|
const [scale, setScale] = React.useState<number>(1)
|
||||||
const editorRef = React.useRef<ImageEditor>(null)
|
const editorRef = React.useRef<ImageEditor>(null)
|
||||||
|
const imageEditorWidth = dimensions ? dimensions.width : DIMS[as].width
|
||||||
|
const imageEditorHeight = dimensions ? dimensions.height : DIMS[as].height
|
||||||
|
|
||||||
const doSetAs = (v: AspectRatio) => () => setAs(v)
|
const doSetAs = (v: AspectRatio) => () => setAs(v)
|
||||||
|
|
||||||
|
@ -57,8 +66,8 @@ export function Component({
|
||||||
path: dataUri,
|
path: dataUri,
|
||||||
mime: 'image/jpeg',
|
mime: 'image/jpeg',
|
||||||
size: getDataUriSize(dataUri),
|
size: getDataUriSize(dataUri),
|
||||||
width: DIMS[as].width,
|
width: imageEditorWidth,
|
||||||
height: DIMS[as].height,
|
height: imageEditorHeight,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
onSelect(undefined)
|
onSelect(undefined)
|
||||||
|
@ -73,7 +82,18 @@ export function Component({
|
||||||
cropperStyle = styles.cropperWide
|
cropperStyle = styles.cropperWide
|
||||||
} else if (as === AspectRatio.Tall) {
|
} else if (as === AspectRatio.Tall) {
|
||||||
cropperStyle = styles.cropperTall
|
cropperStyle = styles.cropperTall
|
||||||
|
} else if (as === AspectRatio.Custom) {
|
||||||
|
const cropperDimensions = calculateDimensions(
|
||||||
|
550,
|
||||||
|
imageEditorHeight,
|
||||||
|
imageEditorWidth,
|
||||||
|
)
|
||||||
|
cropperStyle = {
|
||||||
|
width: cropperDimensions.width,
|
||||||
|
height: cropperDimensions.height,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<View style={[styles.cropper, pal.borderDark, cropperStyle]}>
|
<View style={[styles.cropper, pal.borderDark, cropperStyle]}>
|
||||||
|
@ -81,8 +101,8 @@ export function Component({
|
||||||
ref={editorRef}
|
ref={editorRef}
|
||||||
style={styles.imageEditor}
|
style={styles.imageEditor}
|
||||||
image={uri}
|
image={uri}
|
||||||
width={DIMS[as].width}
|
width={imageEditorWidth}
|
||||||
height={DIMS[as].height}
|
height={imageEditorHeight}
|
||||||
scale={scale}
|
scale={scale}
|
||||||
border={0}
|
border={0}
|
||||||
/>
|
/>
|
||||||
|
@ -97,6 +117,8 @@ export function Component({
|
||||||
maximumValue={3}
|
maximumValue={3}
|
||||||
containerStyle={styles.slider}
|
containerStyle={styles.slider}
|
||||||
/>
|
/>
|
||||||
|
{as === AspectRatio.Custom ? null : (
|
||||||
|
<>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={doSetAs(AspectRatio.Wide)}
|
onPress={doSetAs(AspectRatio.Wide)}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
|
@ -127,6 +149,8 @@ export function Component({
|
||||||
style={as === AspectRatio.Square ? s.blue3 : pal.text}
|
style={as === AspectRatio.Square ? s.blue3 : pal.text}
|
||||||
/>
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.btns}>
|
<View style={styles.btns}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
export const calculateDimensions = (
|
||||||
|
maxWidth: number,
|
||||||
|
originalHeight: number,
|
||||||
|
originalWidth: number,
|
||||||
|
) => {
|
||||||
|
const aspectRatio = originalWidth / originalHeight
|
||||||
|
const newHeight = maxWidth / aspectRatio
|
||||||
|
const newWidth = maxWidth
|
||||||
|
return {
|
||||||
|
width: newWidth,
|
||||||
|
height: newHeight,
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,29 +1,29 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
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 {Image} from 'expo-image'
|
||||||
import {useLingui} from '@lingui/react'
|
import {ModerationUI} from '@atproto/api'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
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 {colors} from 'lib/styles'
|
||||||
import {useTheme} from 'lib/ThemeContext'
|
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 {isAndroid, isNative} from 'platform/detection'
|
||||||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
|
||||||
import {EventStopper} from 'view/com/util/EventStopper'
|
import {EventStopper} from 'view/com/util/EventStopper'
|
||||||
import * as Menu from '#/components/Menu'
|
import {tokens, useTheme as useAlfTheme} from '#/alf'
|
||||||
import {
|
import {
|
||||||
Camera_Filled_Stroke2_Corner0_Rounded as CameraFilled,
|
Camera_Filled_Stroke2_Corner0_Rounded as CameraFilled,
|
||||||
Camera_Stroke2_Corner0_Rounded as Camera,
|
Camera_Stroke2_Corner0_Rounded as Camera,
|
||||||
} from '#/components/icons/Camera'
|
} from '#/components/icons/Camera'
|
||||||
import {StreamingLive_Stroke2_Corner0_Rounded as Library} from '#/components/icons/StreamingLive'
|
import {StreamingLive_Stroke2_Corner0_Rounded as Library} from '#/components/icons/StreamingLive'
|
||||||
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
|
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({
|
export function UserBanner({
|
||||||
type,
|
type,
|
||||||
|
|
Loading…
Reference in New Issue