diff options
Diffstat (limited to 'src/view/com/modals')
-rw-r--r-- | src/view/com/modals/EditProfile.tsx | 10 | ||||
-rw-r--r-- | src/view/com/modals/Modal.web.tsx | 7 | ||||
-rw-r--r-- | src/view/com/modals/crop-image/CropImage.tsx | 11 | ||||
-rw-r--r-- | src/view/com/modals/crop-image/CropImage.web.tsx | 164 |
4 files changed, 187 insertions, 5 deletions
diff --git a/src/view/com/modals/EditProfile.tsx b/src/view/com/modals/EditProfile.tsx index 1c139e9bd..12b72a399 100644 --- a/src/view/com/modals/EditProfile.tsx +++ b/src/view/com/modals/EditProfile.tsx @@ -8,7 +8,7 @@ import { } from 'react-native' import LinearGradient from 'react-native-linear-gradient' import {ScrollView, TextInput} from './util' -import {Image as PickedImage} from '../util/images/ImageCropPicker' +import {PickedMedia} from '../util/images/image-crop-picker/ImageCropPicker' import {Text} from '../util/text/Text' import {ErrorMessage} from '../util/error/ErrorMessage' import {useStores} from '../../../state' @@ -48,12 +48,12 @@ export function Component({ const [userAvatar, setUserAvatar] = useState<string | undefined>( profileView.avatar, ) - const [newUserBanner, setNewUserBanner] = useState<PickedImage | undefined>() - const [newUserAvatar, setNewUserAvatar] = useState<PickedImage | undefined>() + const [newUserBanner, setNewUserBanner] = useState<PickedMedia | undefined>() + const [newUserAvatar, setNewUserAvatar] = useState<PickedMedia | undefined>() const onPressCancel = () => { store.shell.closeModal() } - const onSelectNewAvatar = async (img: PickedImage) => { + const onSelectNewAvatar = async (img: PickedMedia) => { try { const finalImg = await compressIfNeeded(img, 300000) setNewUserAvatar(finalImg) @@ -62,7 +62,7 @@ export function Component({ setError(e.message || e.toString()) } } - const onSelectNewBanner = async (img: PickedImage) => { + const onSelectNewBanner = async (img: PickedMedia) => { try { const finalImg = await compressIfNeeded(img, 500000) setNewUserBanner(finalImg) diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx index 25493312d..44ea95f07 100644 --- a/src/view/com/modals/Modal.web.tsx +++ b/src/view/com/modals/Modal.web.tsx @@ -11,6 +11,7 @@ import * as EditProfileModal from './EditProfile' import * as ServerInputModal from './ServerInput' import * as ReportPostModal from './ReportPost' import * as ReportAccountModal from './ReportAccount' +import * as CropImageModal from './crop-image/CropImage.web' export const Modal = observer(function Modal() { const store = useStores() @@ -50,6 +51,12 @@ export const Modal = observer(function Modal() { element = <ReportPostModal.Component /> } else if (store.shell.activeModal?.name === 'report-account') { element = <ReportAccountModal.Component /> + } else if (store.shell.activeModal?.name === 'crop-image') { + element = ( + <CropImageModal.Component + {...(store.shell.activeModal as models.CropImageModal)} + /> + ) } else { return null } diff --git a/src/view/com/modals/crop-image/CropImage.tsx b/src/view/com/modals/crop-image/CropImage.tsx new file mode 100644 index 000000000..9ac3f277f --- /dev/null +++ b/src/view/com/modals/crop-image/CropImage.tsx @@ -0,0 +1,11 @@ +/** + * NOTE + * This modal is used only in the web build + * Native uses a third-party library + */ + +export const snapPoints = ['0%'] + +export function Component() { + return null +} diff --git a/src/view/com/modals/crop-image/CropImage.web.tsx b/src/view/com/modals/crop-image/CropImage.web.tsx new file mode 100644 index 000000000..1f234c4a6 --- /dev/null +++ b/src/view/com/modals/crop-image/CropImage.web.tsx @@ -0,0 +1,164 @@ +import React from 'react' +import {StyleSheet, TouchableOpacity, View} from 'react-native' +import ImageEditor from 'react-avatar-editor' +import {Slider} from '@miblanchard/react-native-slider' +import LinearGradient from 'react-native-linear-gradient' +import {Text} from '../../util/text/Text' +import {PickedMedia} from '../../util/images/image-crop-picker/types' +import {s, gradients} from '../../../lib/styles' +import {useStores} from '../../../../state' +import {usePalette} from '../../../lib/hooks/usePalette' +import {SquareIcon, RectWideIcon, RectTallIcon} from '../../../lib/icons' + +enum AspectRatio { + Square = 'square', + Wide = 'wide', + Tall = 'tall', +} +interface Dim { + width: number + height: number +} +const DIMS: Record<string, Dim> = { + [AspectRatio.Square]: {width: 1000, height: 1000}, + [AspectRatio.Wide]: {width: 1000, height: 750}, + [AspectRatio.Tall]: {width: 750, height: 1000}, +} + +export const snapPoints = ['0%'] + +export function Component({ + uri, + onSelect, +}: { + uri: string + onSelect: (img?: PickedMedia) => void +}) { + const store = useStores() + const pal = usePalette('default') + const [as, setAs] = React.useState<AspectRatio>(AspectRatio.Square) + const [scale, setScale] = React.useState<number>(1) + + const doSetAs = (v: AspectRatio) => () => setAs(v) + + const onPressCancel = () => { + onSelect(undefined) + store.shell.closeModal() + } + const onPressDone = () => { + console.log('TODO') + onSelect(undefined) // TODO + store.shell.closeModal() + } + + let cropperStyle + if (as === AspectRatio.Square) { + cropperStyle = styles.cropperSquare + } else if (as === AspectRatio.Wide) { + cropperStyle = styles.cropperWide + } else if (as === AspectRatio.Tall) { + cropperStyle = styles.cropperTall + } + return ( + <View> + <View style={[styles.cropper, cropperStyle]}> + <ImageEditor + style={styles.imageEditor} + image={uri} + width={DIMS[as].width} + height={DIMS[as].height} + scale={scale} + /> + </View> + <View style={styles.ctrls}> + <Slider + value={scale} + onValueChange={(v: number | number[]) => + setScale(Array.isArray(v) ? v[0] : v) + } + minimumValue={1} + maximumValue={3} + containerStyle={styles.slider} + /> + <TouchableOpacity onPress={doSetAs(AspectRatio.Wide)}> + <RectWideIcon + size={24} + style={as === AspectRatio.Wide ? s.blue3 : undefined} + /> + </TouchableOpacity> + <TouchableOpacity onPress={doSetAs(AspectRatio.Tall)}> + <RectTallIcon + size={24} + style={as === AspectRatio.Tall ? s.blue3 : undefined} + /> + </TouchableOpacity> + <TouchableOpacity onPress={doSetAs(AspectRatio.Square)}> + <SquareIcon + size={24} + style={as === AspectRatio.Square ? s.blue3 : undefined} + /> + </TouchableOpacity> + </View> + <View style={styles.btns}> + <TouchableOpacity onPress={onPressCancel}> + <Text type="xl" style={pal.link}> + Cancel + </Text> + </TouchableOpacity> + <View style={s.flex1} /> + <TouchableOpacity onPress={onPressDone}> + <LinearGradient + colors={[gradients.blueLight.start, gradients.blueLight.end]} + start={{x: 0, y: 0}} + end={{x: 1, y: 1}} + style={[styles.btn]}> + <Text type="xl-medium" style={s.white}> + Done + </Text> + </LinearGradient> + </TouchableOpacity> + </View> + </View> + ) +} + +const styles = StyleSheet.create({ + cropper: { + marginLeft: 'auto', + marginRight: 'auto', + }, + cropperSquare: { + width: 400, + height: 400, + }, + cropperWide: { + width: 400, + height: 300, + }, + cropperTall: { + width: 300, + height: 400, + }, + imageEditor: { + maxWidth: '100%', + }, + ctrls: { + flexDirection: 'row', + alignItems: 'center', + marginTop: 10, + }, + slider: { + flex: 1, + marginRight: 10, + }, + btns: { + flexDirection: 'row', + alignItems: 'center', + marginTop: 10, + }, + btn: { + borderRadius: 4, + paddingVertical: 8, + paddingHorizontal: 24, + }, +}) |