From a8f6058da3bad636fc290a2cee5dd475922f5252 Mon Sep 17 00:00:00 2001 From: Mary Date: Wed, 8 May 2024 09:22:36 +0700 Subject: [PATCH] refactor: remove gallery mobx model and old modals --- src/state/modals/index.tsx | 17 - src/state/models/media/gallery.ts | 110 ----- src/state/models/media/image.e2e.ts | 146 ------- src/state/models/media/image.ts | 299 ------------- src/view/com/composer/photos/Gallery.tsx | 6 - src/view/com/modals/EditImage.tsx | 402 ------------------ src/view/com/modals/Modal.web.tsx | 10 - .../com/modals/crop-image/CropImage.web.tsx | 228 ---------- .../com/modals/crop-image/cropImageUtil.ts | 13 - 9 files changed, 1231 deletions(-) delete mode 100644 src/state/models/media/gallery.ts delete mode 100644 src/state/models/media/image.e2e.ts delete mode 100644 src/state/models/media/image.ts delete mode 100644 src/view/com/modals/EditImage.tsx delete mode 100644 src/view/com/modals/crop-image/CropImage.web.tsx delete mode 100644 src/view/com/modals/crop-image/cropImageUtil.ts diff --git a/src/state/modals/index.tsx b/src/state/modals/index.tsx index 5f4ff4f555..8298d282bc 100644 --- a/src/state/modals/index.tsx +++ b/src/state/modals/index.tsx @@ -1,9 +1,7 @@ import React from 'react' -import {Image as RNImage} from 'react-native-image-crop-picker' import {AppBskyActorDefs, AppBskyGraphDefs} from '@atproto/api' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' -import {ComposerImage} from '../gallery' import {ThreadgateSetting} from '../queries/threadgate' export interface EditProfileModal { @@ -37,19 +35,6 @@ export interface ListAddRemoveUsersModal { ) => void } -export interface EditImageModal { - name: 'edit-image' - image: ComposerImage - onChange: (next: ComposerImage) => void -} - -export interface CropImageModal { - name: 'crop-image' - uri: string - dimensions?: {width: number; height: number} - onSelect: (img?: RNImage) => void -} - export interface DeleteAccountModal { name: 'delete-account' } @@ -145,8 +130,6 @@ export type Modal = | ListAddRemoveUsersModal // Posts - | CropImageModal - | EditImageModal | RepostModal | SelfLabelModal | ThreadgateModal diff --git a/src/state/models/media/gallery.ts b/src/state/models/media/gallery.ts deleted file mode 100644 index 9c8c13010d..0000000000 --- a/src/state/models/media/gallery.ts +++ /dev/null @@ -1,110 +0,0 @@ -import {makeAutoObservable, runInAction} from 'mobx' -import {ImageModel} from './image' -import {Image as RNImage} from 'react-native-image-crop-picker' -import {openPicker} from 'lib/media/picker' -import {getImageDim} from 'lib/media/manip' - -interface InitialImageUri { - uri: string - width: number - height: number -} - -export class GalleryModel { - images: ImageModel[] = [] - - constructor(uris?: {uri: string; width: number; height: number}[]) { - makeAutoObservable(this) - - if (uris) { - this.addFromUris(uris) - } - } - - get isEmpty() { - return this.size === 0 - } - - get size() { - return this.images.length - } - - get needsAltText() { - return this.images.some(image => image.altText.trim() === '') - } - - *add(image_: Omit) { - if (this.size >= 4) { - return - } - - // Temporarily enforce uniqueness but can eventually also use index - if (!this.images.some(i => i.path === image_.path)) { - const image = new ImageModel(image_) - - // Initial resize - image.manipulate({}) - this.images.push(image) - } - } - - async paste(uri: string) { - if (this.size >= 4) { - return - } - - const {width, height} = await getImageDim(uri) - - const image = { - path: uri, - height, - width, - mime: 'image/jpeg', - } - - runInAction(() => { - this.add(image) - }) - } - - setAltText(image: ImageModel, altText: string) { - image.setAltText(altText) - } - - crop(image: ImageModel) { - image.crop() - } - - remove(image: ImageModel) { - const index = this.images.findIndex(image_ => image_.path === image.path) - this.images.splice(index, 1) - } - - async previous(image: ImageModel) { - image.previous() - } - - async pick() { - const images = await openPicker({ - selectionLimit: 4 - this.size, - allowsMultipleSelection: true, - }) - - return await Promise.all( - images.map(image => { - this.add(image) - }), - ) - } - - async addFromUris(uris: InitialImageUri[]) { - for (const uriObj of uris) { - this.add({ - mime: 'image/jpeg', - height: uriObj.height, - width: uriObj.width, - path: uriObj.uri, - }) - } - } -} diff --git a/src/state/models/media/image.e2e.ts b/src/state/models/media/image.e2e.ts deleted file mode 100644 index ccabd50475..0000000000 --- a/src/state/models/media/image.e2e.ts +++ /dev/null @@ -1,146 +0,0 @@ -import {Image as RNImage} from 'react-native-image-crop-picker' -import {makeAutoObservable} from 'mobx' -import {POST_IMG_MAX} from 'lib/constants' -import {ActionCrop} from 'expo-image-manipulator' -import {Position} from 'react-avatar-editor' -import {Dimensions} from 'lib/media/types' - -export interface ImageManipulationAttributes { - aspectRatio?: '4:3' | '1:1' | '3:4' | 'None' - rotate?: number - scale?: number - position?: Position - flipHorizontal?: boolean - flipVertical?: boolean -} - -export class ImageModel implements Omit { - path: string - mime = 'image/jpeg' - width: number - height: number - altText = '' - cropped?: RNImage = undefined - compressed?: RNImage = undefined - - // Web manipulation - prev?: RNImage - attributes: ImageManipulationAttributes = { - aspectRatio: 'None', - scale: 1, - flipHorizontal: false, - flipVertical: false, - rotate: 0, - } - prevAttributes: ImageManipulationAttributes = {} - - constructor(image: Omit) { - makeAutoObservable(this) - - this.path = image.path - this.width = image.width - this.height = image.height - } - - setRatio(aspectRatio: ImageManipulationAttributes['aspectRatio']) { - this.attributes.aspectRatio = aspectRatio - } - - setRotate(degrees: number) { - this.attributes.rotate = degrees - this.manipulate({}) - } - - flipVertical() { - this.attributes.flipVertical = !this.attributes.flipVertical - this.manipulate({}) - } - - flipHorizontal() { - this.attributes.flipHorizontal = !this.attributes.flipHorizontal - this.manipulate({}) - } - - get ratioMultipliers() { - return { - '4:3': 4 / 3, - '1:1': 1, - '3:4': 3 / 4, - None: this.width / this.height, - } - } - - getUploadDimensions( - dimensions: Dimensions, - maxDimensions: Dimensions = POST_IMG_MAX, - as: ImageManipulationAttributes['aspectRatio'] = 'None', - ) { - const {width, height} = dimensions - const {width: maxWidth, height: maxHeight} = maxDimensions - - return width < maxWidth && height < maxHeight - ? { - width, - height, - } - : this.getResizedDimensions(as, POST_IMG_MAX.width) - } - - getResizedDimensions( - as: ImageManipulationAttributes['aspectRatio'] = 'None', - maxSide: number, - ) { - const ratioMultiplier = this.ratioMultipliers[as] - - if (ratioMultiplier === 1) { - return { - height: maxSide, - width: maxSide, - } - } - - if (ratioMultiplier < 1) { - return { - width: maxSide * ratioMultiplier, - height: maxSide, - } - } - - return { - width: maxSide, - height: maxSide / ratioMultiplier, - } - } - - setAltText(altText: string) { - this.altText = altText.trim() - } - - // Only compress prior to upload - async compress() { - // do nothing - } - - // Mobile - async crop() { - // do nothing - } - - // Web manipulation - async manipulate( - _attributes: { - crop?: ActionCrop['crop'] - } & ImageManipulationAttributes, - ) { - // do nothing - } - - resetCropped() { - this.manipulate({}) - } - - previous() { - this.cropped = this.prev - this.attributes = this.prevAttributes - } -} diff --git a/src/state/models/media/image.ts b/src/state/models/media/image.ts deleted file mode 100644 index 5c547c148f..0000000000 --- a/src/state/models/media/image.ts +++ /dev/null @@ -1,299 +0,0 @@ -import {Image as RNImage} from 'react-native-image-crop-picker' -import {makeAutoObservable, runInAction} from 'mobx' -import {POST_IMG_MAX} from 'lib/constants' -import * as ImageManipulator from 'expo-image-manipulator' -import {getDataUriSize} from 'lib/media/util' -import {openCropper} from 'lib/media/picker' -import {ActionCrop, FlipType, SaveFormat} from 'expo-image-manipulator' -import {Position} from 'react-avatar-editor' -import {Dimensions} from 'lib/media/types' -import {isIOS} from 'platform/detection' -import {logger} from '#/logger' - -export interface ImageManipulationAttributes { - aspectRatio?: '4:3' | '1:1' | '3:4' | 'None' - rotate?: number - scale?: number - position?: Position - flipHorizontal?: boolean - flipVertical?: boolean -} - -const MAX_IMAGE_SIZE_IN_BYTES = 976560 - -export class ImageModel implements Omit { - path: string - mime = 'image/jpeg' - width: number - height: number - altText = '' - cropped?: RNImage = undefined - compressed?: RNImage = undefined - - // Web manipulation - prev?: RNImage - attributes: ImageManipulationAttributes = { - aspectRatio: 'None', - scale: 1, - flipHorizontal: false, - flipVertical: false, - rotate: 0, - } - prevAttributes: ImageManipulationAttributes = {} - - constructor(image: Omit) { - makeAutoObservable(this) - - this.path = image.path - this.width = image.width - this.height = image.height - } - - setRatio(aspectRatio: ImageManipulationAttributes['aspectRatio']) { - this.attributes.aspectRatio = aspectRatio - } - - setRotate(degrees: number) { - this.attributes.rotate = degrees - this.manipulate({}) - } - - flipVertical() { - this.attributes.flipVertical = !this.attributes.flipVertical - this.manipulate({}) - } - - flipHorizontal() { - this.attributes.flipHorizontal = !this.attributes.flipHorizontal - this.manipulate({}) - } - - get ratioMultipliers() { - return { - '4:3': 4 / 3, - '1:1': 1, - '3:4': 3 / 4, - None: this.width / this.height, - } - } - - getUploadDimensions( - dimensions: Dimensions, - maxDimensions: Dimensions = POST_IMG_MAX, - as: ImageManipulationAttributes['aspectRatio'] = 'None', - ) { - const {width, height} = dimensions - const {width: maxWidth, height: maxHeight} = maxDimensions - - return width < maxWidth && height < maxHeight - ? { - width, - height, - } - : this.getResizedDimensions(as, POST_IMG_MAX.width) - } - - getResizedDimensions( - as: ImageManipulationAttributes['aspectRatio'] = 'None', - maxSide: number, - ) { - const ratioMultiplier = this.ratioMultipliers[as] - - if (ratioMultiplier === 1) { - return { - height: maxSide, - width: maxSide, - } - } - - if (ratioMultiplier < 1) { - return { - width: maxSide * ratioMultiplier, - height: maxSide, - } - } - - return { - width: maxSide, - height: maxSide / ratioMultiplier, - } - } - - setAltText(altText: string) { - this.altText = altText.trim() - } - - // Only compress prior to upload - async compress() { - for (let i = 10; i > 0; i--) { - // Float precision - const factor = Math.round(i) / 10 - const compressed = await ImageManipulator.manipulateAsync( - this.cropped?.path ?? this.path, - undefined, - { - compress: factor, - base64: true, - format: SaveFormat.JPEG, - }, - ) - - if (compressed.base64 !== undefined) { - const size = getDataUriSize(compressed.base64) - - if (size < MAX_IMAGE_SIZE_IN_BYTES) { - runInAction(() => { - this.compressed = { - mime: 'image/jpeg', - path: compressed.uri, - size, - ...compressed, - } - }) - return - } - } - } - - // Compression fails when removing redundant information is not possible. - // This can be tested with images that have high variance in noise. - throw new Error('Failed to compress image') - } - - // Mobile - async crop() { - try { - // NOTE - // on ios, react-native-image-crop-picker gives really bad quality - // without specifying width and height. on android, however, the - // crop stretches incorrectly if you do specify it. these are - // both separate bugs in the library. we deal with that by - // providing width & height for ios only - // -prf - const {width, height} = this.getUploadDimensions({ - width: this.width, - height: this.height, - }) - - const cropped = await openCropper({ - mediaType: 'photo', - path: this.path, - freeStyleCropEnabled: true, - ...(isIOS ? {width, height} : {}), - }) - - runInAction(() => { - this.cropped = cropped - }) - } catch (err) { - logger.error('Failed to crop photo', {message: err}) - } - } - - // Web manipulation - async manipulate( - attributes: { - crop?: ActionCrop['crop'] - } & ImageManipulationAttributes, - ) { - let uploadWidth: number | undefined - let uploadHeight: number | undefined - - const {aspectRatio, crop, position, scale} = attributes - const modifiers = [] - - if (this.attributes.flipHorizontal) { - modifiers.push({flip: FlipType.Horizontal}) - } - - if (this.attributes.flipVertical) { - modifiers.push({flip: FlipType.Vertical}) - } - - if (this.attributes.rotate !== undefined) { - modifiers.push({rotate: this.attributes.rotate}) - } - - if (crop !== undefined) { - const croppedHeight = crop.height * this.height - const croppedWidth = crop.width * this.width - modifiers.push({ - crop: { - originX: crop.originX * this.width, - originY: crop.originY * this.height, - height: croppedHeight, - width: croppedWidth, - }, - }) - - const uploadDimensions = this.getUploadDimensions( - {width: croppedWidth, height: croppedHeight}, - POST_IMG_MAX, - aspectRatio, - ) - - uploadWidth = uploadDimensions.width - uploadHeight = uploadDimensions.height - } else { - const uploadDimensions = this.getUploadDimensions( - {width: this.width, height: this.height}, - POST_IMG_MAX, - aspectRatio, - ) - - uploadWidth = uploadDimensions.width - uploadHeight = uploadDimensions.height - } - - if (scale !== undefined) { - this.attributes.scale = scale - } - - if (position !== undefined) { - this.attributes.position = position - } - - if (aspectRatio !== undefined) { - this.attributes.aspectRatio = aspectRatio - } - - const ratioMultiplier = - this.ratioMultipliers[this.attributes.aspectRatio ?? '1:1'] - - const result = await ImageManipulator.manipulateAsync( - this.path, - [ - ...modifiers, - { - resize: - ratioMultiplier > 1 ? {width: uploadWidth} : {height: uploadHeight}, - }, - ], - { - base64: true, - format: SaveFormat.JPEG, - }, - ) - - runInAction(() => { - this.cropped = { - mime: 'image/jpeg', - path: result.uri, - size: - result.base64 !== undefined - ? getDataUriSize(result.base64) - : MAX_IMAGE_SIZE_IN_BYTES + 999, // shouldn't hit this unless manipulation fails - ...result, - } - }) - } - - resetCropped() { - this.manipulate({}) - } - - previous() { - this.cropped = this.prev - this.attributes = this.prevAttributes - } -} diff --git a/src/view/com/composer/photos/Gallery.tsx b/src/view/com/composer/photos/Gallery.tsx index 3840e77ee1..a2606ae602 100644 --- a/src/view/com/composer/photos/Gallery.tsx +++ b/src/view/com/composer/photos/Gallery.tsx @@ -143,12 +143,6 @@ const GalleryItem = ({ cropImage(image).then(next => { onChange(next) }) - } else { - // openModal({ - // name: 'edit-image', - // image, - // gallery, - // }) } } diff --git a/src/view/com/modals/EditImage.tsx b/src/view/com/modals/EditImage.tsx deleted file mode 100644 index b39dcd9364..0000000000 --- a/src/view/com/modals/EditImage.tsx +++ /dev/null @@ -1,402 +0,0 @@ -import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react' -import {Pressable, StyleSheet, View} from 'react-native' -import {useWindowDimensions} from 'react-native' -import {LinearGradient} from 'expo-linear-gradient' -import {MaterialIcons} from '@expo/vector-icons' -import {msg, Trans} from '@lingui/macro' -import {useLingui} from '@lingui/react' -import {Slider} from '@miblanchard/react-native-slider' -import {observer} from 'mobx-react-lite' -import ImageEditor, {Position} from 'react-avatar-editor' - -import {useModalControls} from '#/state/modals' -import {MAX_ALT_TEXT} from 'lib/constants' -import {usePalette} from 'lib/hooks/usePalette' -import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' -import {RectTallIcon, RectWideIcon, SquareIcon} from 'lib/icons' -import {enforceLen} from 'lib/strings/helpers' -import {gradients, s} from 'lib/styles' -import {useTheme} from 'lib/ThemeContext' -import {getKeys} from 'lib/type-assertions' -import {GalleryModel} from 'state/models/media/gallery' -import {ImageModel} from 'state/models/media/image' -import {Text} from '../util/text/Text' -import {TextInput} from './util' - -export const snapPoints = ['80%'] - -const RATIOS = { - '4:3': { - Icon: RectWideIcon, - }, - '1:1': { - Icon: SquareIcon, - }, - '3:4': { - Icon: RectTallIcon, - }, - None: { - label: 'None', - Icon: MaterialIcons, - name: 'do-not-disturb-alt', - }, -} as const - -type AspectRatio = keyof typeof RATIOS - -interface Props { - image: ImageModel - gallery: GalleryModel -} - -export const Component = observer(function EditImageImpl({ - image, - gallery, -}: Props) { - const pal = usePalette('default') - const theme = useTheme() - const {_} = useLingui() - const windowDimensions = useWindowDimensions() - const {isMobile} = useWebMediaQueries() - const {closeModal} = useModalControls() - - const { - aspectRatio, - // rotate = 0 - } = image.attributes - - const editorRef = useRef(null) - const [scale, setScale] = useState(image.attributes.scale ?? 1) - const [position, setPosition] = useState( - image.attributes.position, - ) - const [altText, setAltText] = useState(image?.altText ?? '') - - const onFlipHorizontal = useCallback(() => { - image.flipHorizontal() - }, [image]) - - const onFlipVertical = useCallback(() => { - image.flipVertical() - }, [image]) - - // const onSetRotate = useCallback( - // (direction: 'left' | 'right') => { - // const rotation = (rotate + 90 * (direction === 'left' ? -1 : 1)) % 360 - // image.setRotate(rotation) - // }, - // [rotate, image], - // ) - - const onSetRatio = useCallback( - (ratio: AspectRatio) => { - image.setRatio(ratio) - }, - [image], - ) - - const adjustments = useMemo( - () => [ - // { - // name: 'rotate-left' as const, - // label: 'Rotate left', - // onPress: () => { - // onSetRotate('left') - // }, - // }, - // { - // name: 'rotate-right' as const, - // label: 'Rotate right', - // onPress: () => { - // onSetRotate('right') - // }, - // }, - { - name: 'flip' as const, - label: _(msg`Flip horizontal`), - onPress: onFlipHorizontal, - }, - { - name: 'flip' as const, - label: _(msg`Flip vertically`), - onPress: onFlipVertical, - }, - ], - [onFlipHorizontal, onFlipVertical, _], - ) - - useEffect(() => { - image.prev = image.cropped - image.prevAttributes = image.attributes - image.resetCropped() - }, [image]) - - const onCloseModal = useCallback(() => { - closeModal() - }, [closeModal]) - - const onPressCancel = useCallback(async () => { - await gallery.previous(image) - onCloseModal() - }, [onCloseModal, gallery, image]) - - const onPressSave = useCallback(async () => { - image.setAltText(altText) - - const crop = editorRef.current?.getCroppingRect() - - await image.manipulate({ - ...(crop !== undefined - ? { - crop: { - originX: crop.x, - originY: crop.y, - width: crop.width, - height: crop.height, - }, - ...(scale !== 1 ? {scale} : {}), - ...(position !== undefined ? {position} : {}), - } - : {}), - }) - - image.prev = image.cropped - image.prevAttributes = image.attributes - onCloseModal() - }, [altText, image, position, scale, onCloseModal]) - - const getLabelIconSize = useCallback((as: AspectRatio) => { - switch (as) { - case 'None': - return 22 - case '1:1': - return 32 - default: - return 26 - } - }, []) - - if (image.cropped === undefined) { - return null - } - - const computedWidth = - windowDimensions.width > 500 ? 410 : windowDimensions.width - 80 - const sideLength = isMobile ? computedWidth : 300 - - const dimensions = image.getResizedDimensions(aspectRatio, sideLength) - const imgContainerStyles = {width: sideLength, height: sideLength} - - const imgControlStyles = { - alignItems: 'center' as const, - flexDirection: isMobile ? ('column' as const) : ('row' as const), - gap: isMobile ? 0 : 5, - } - - return ( - - - Edit image - - - - - - - - setScale(Array.isArray(v) ? v[0] : v) - } - minimumValue={1} - maximumValue={3} - /> - - - {!isMobile ? ( - - Ratios - - ) : null} - - {getKeys(RATIOS).map(ratio => { - const {Icon, ...props} = RATIOS[ratio] - const labelIconSize = getLabelIconSize(ratio) - const isSelected = aspectRatio === ratio - - return ( - { - onSetRatio(ratio) - }} - accessibilityLabel={ratio} - accessibilityHint=""> - - - - {ratio} - - - ) - })} - - {!isMobile ? ( - - Transformations - - ) : null} - - {adjustments.map(({label, name, onPress}) => ( - - - - ))} - - - - - - Accessibility - - setAltText(enforceLen(text, MAX_ALT_TEXT))} - accessibilityLabel={_(msg`Alt text`)} - accessibilityHint="" - accessibilityLabelledBy="alt-text" - /> - - - - - Cancel - - - - - - Done - - - - - - ) -}) - -const styles = StyleSheet.create({ - container: { - gap: 18, - height: '100%', - width: '100%', - }, - subsection: {marginTop: 12}, - gap18: {gap: 18}, - title: { - fontWeight: 'bold', - fontSize: 24, - }, - btns: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - }, - btn: { - borderRadius: 4, - paddingVertical: 8, - paddingHorizontal: 24, - }, - imgControl: { - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - height: 40, - }, - imgEditor: { - maxWidth: '100%', - }, - imgContainer: { - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - borderWidth: 1, - borderStyle: 'solid', - marginBottom: 4, - }, - flipVertical: { - transform: [{rotate: '90deg'}], - }, - flipBtn: { - paddingHorizontal: 4, - paddingVertical: 8, - }, - textArea: { - borderWidth: 1, - borderRadius: 6, - paddingTop: 10, - paddingHorizontal: 12, - fontSize: 16, - height: 100, - textAlignVertical: 'top', - }, - bottomSection: { - borderTopWidth: 1, - paddingTop: 18, - }, -}) diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx index 4d1b8f4483..16da4205f6 100644 --- a/src/view/com/modals/Modal.web.tsx +++ b/src/view/com/modals/Modal.web.tsx @@ -12,9 +12,7 @@ import * as ChangeEmailModal from './ChangeEmail' import * as ChangeHandleModal from './ChangeHandle' import * as ChangePasswordModal from './ChangePassword' import * as CreateOrEditListModal from './CreateOrEditList' -import * as CropImageModal from './crop-image/CropImage.web' import * as DeleteAccountModal from './DeleteAccount' -import * as EditImageModal from './EditImage' import * as EditProfileModal from './EditProfile' import * as InviteCodesModal from './InviteCodes' import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguagesSettings' @@ -55,9 +53,6 @@ function Modal({modal}: {modal: ModalIface}) { } const onPressMask = () => { - if (modal.name === 'crop-image' || modal.name === 'edit-image') { - return // dont close on mask presses during crop - } closeModal() } const onInnerPress = () => { @@ -74,8 +69,6 @@ function Modal({modal}: {modal: ModalIface}) { element = } else if (modal.name === 'list-add-remove-users') { element = - } else if (modal.name === 'crop-image') { - element = } else if (modal.name === 'delete-account') { element = } else if (modal.name === 'repost') { @@ -94,9 +87,6 @@ function Modal({modal}: {modal: ModalIface}) { element = } else if (modal.name === 'post-languages-settings') { element = - } else if (modal.name === 'edit-image') { - // @ts-expect-error: todo - element = } else if (modal.name === 'verify-email') { element = } else if (modal.name === 'change-email') { diff --git a/src/view/com/modals/crop-image/CropImage.web.tsx b/src/view/com/modals/crop-image/CropImage.web.tsx deleted file mode 100644 index 10cae2f174..0000000000 --- a/src/view/com/modals/crop-image/CropImage.web.tsx +++ /dev/null @@ -1,228 +0,0 @@ -import React from 'react' -import {StyleSheet, TouchableOpacity, View} from 'react-native' -import {Image as RNImage} from 'react-native-image-crop-picker' -import {LinearGradient} from 'expo-linear-gradient' -import {msg, Trans} from '@lingui/macro' -import {useLingui} from '@lingui/react' -import {Slider} from '@miblanchard/react-native-slider' -import ImageEditor from 'react-avatar-editor' - -import {useModalControls} from '#/state/modals' -import {usePalette} from 'lib/hooks/usePalette' -import {RectTallIcon, RectWideIcon, SquareIcon} from 'lib/icons' -import {Dimensions} from 'lib/media/types' -import {getDataUriSize} from 'lib/media/util' -import {gradients, s} from 'lib/styles' -import {Text} from 'view/com/util/text/Text' -import {calculateDimensions} from './cropImageUtil' - -enum AspectRatio { - Square = 'square', - Wide = 'wide', - Tall = 'tall', - Custom = 'custom', -} - -const DIMS: Record = { - [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, - dimensions, - onSelect, -}: { - uri: string - dimensions?: Dimensions - onSelect: (img?: RNImage) => void -}) { - const {closeModal} = useModalControls() - const pal = usePalette('default') - const {_} = useLingui() - const defaultAspectStyle = dimensions - ? AspectRatio.Custom - : AspectRatio.Square - const [as, setAs] = React.useState(defaultAspectStyle) - const [scale, setScale] = React.useState(1) - const editorRef = React.useRef(null) - const imageEditorWidth = dimensions ? dimensions.width : DIMS[as].width - const imageEditorHeight = dimensions ? dimensions.height : DIMS[as].height - - const doSetAs = (v: AspectRatio) => () => setAs(v) - - const onPressCancel = () => { - onSelect(undefined) - closeModal() - } - const onPressDone = () => { - const canvas = editorRef.current?.getImageScaledToCanvas() - if (canvas) { - const dataUri = canvas.toDataURL('image/jpeg') - onSelect({ - path: dataUri, - mime: 'image/jpeg', - size: getDataUriSize(dataUri), - width: imageEditorWidth, - height: imageEditorHeight, - }) - } else { - onSelect(undefined) - } - 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 - } else if (as === AspectRatio.Custom) { - const cropperDimensions = calculateDimensions( - 550, - imageEditorHeight, - imageEditorWidth, - ) - cropperStyle = { - width: cropperDimensions.width, - height: cropperDimensions.height, - } - } - - return ( - - - - - - - setScale(Array.isArray(v) ? v[0] : v) - } - minimumValue={1} - maximumValue={3} - containerStyle={styles.slider} - /> - {as === AspectRatio.Custom ? null : ( - <> - - - - - - - - - - - )} - - - - - Cancel - - - - - - - Done - - - - - - ) -} - -const styles = StyleSheet.create({ - cropper: { - marginLeft: 'auto', - marginRight: 'auto', - borderWidth: 1, - borderRadius: 4, - overflow: 'hidden', - }, - 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, - }, -}) diff --git a/src/view/com/modals/crop-image/cropImageUtil.ts b/src/view/com/modals/crop-image/cropImageUtil.ts deleted file mode 100644 index 303d15ba5b..0000000000 --- a/src/view/com/modals/crop-image/cropImageUtil.ts +++ /dev/null @@ -1,13 +0,0 @@ -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, - } -}