MobX removal take 2 (#5381)

* mobx removal take 2

* Actually rm mobx

---------

Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
This commit is contained in:
Mary
2024-09-24 23:14:15 +07:00
committed by GitHub
parent dbe1df7ac7
commit 8ea89469ef
23 changed files with 594 additions and 1256 deletions
+16 -13
View File
@@ -13,6 +13,7 @@ import {LinearGradient} from 'expo-linear-gradient'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {ComposerImage} from '#/state/gallery'
import {useModalControls} from '#/state/modals'
import {MAX_ALT_TEXT} from 'lib/constants'
import {useIsKeyboardVisible} from 'lib/hooks/useIsKeyboardVisible'
@@ -21,21 +22,21 @@ import {enforceLen} from 'lib/strings/helpers'
import {gradients, s} from 'lib/styles'
import {useTheme} from 'lib/ThemeContext'
import {isAndroid, isWeb} from 'platform/detection'
import {ImageModel} from 'state/models/media/image'
import {Text} from '../util/text/Text'
import {ScrollView, TextInput} from './util'
export const snapPoints = ['100%']
interface Props {
image: ImageModel
image: ComposerImage
onChange: (next: ComposerImage) => void
}
export function Component({image}: Props) {
export function Component({image, onChange}: Props) {
const pal = usePalette('default')
const theme = useTheme()
const {_} = useLingui()
const [altText, setAltText] = useState(image.altText)
const [altText, setAltText] = useState(image.alt)
const windim = useWindowDimensions()
const {closeModal} = useModalControls()
const inputRef = React.useRef<RNTextInput>(null)
@@ -60,7 +61,8 @@ export function Component({image}: Props) {
const imageStyles = useMemo<ImageStyle>(() => {
const maxWidth = isWeb ? 450 : windim.width
if (image.height > image.width) {
const media = image.transformed ?? image.source
if (media.height > media.width) {
return {
resizeMode: 'contain',
width: '100%',
@@ -70,7 +72,7 @@ export function Component({image}: Props) {
}
return {
width: '100%',
height: (maxWidth / image.width) * image.height,
height: (maxWidth / media.width) * media.height,
borderRadius: 8,
}
}, [image, windim])
@@ -79,15 +81,18 @@ export function Component({image}: Props) {
(v: string) => {
v = enforceLen(v, MAX_ALT_TEXT)
setAltText(v)
image.setAltText(v)
},
[setAltText, image],
[setAltText],
)
const onPressSave = useCallback(() => {
image.setAltText(altText)
onChange({
...image,
alt: altText,
})
closeModal()
}, [closeModal, image, altText])
}, [closeModal, image, altText, onChange])
return (
<ScrollView
@@ -101,9 +106,7 @@ export function Component({image}: Props) {
<Image
testID="selectedPhotoImage"
style={imageStyles}
source={{
uri: image.cropped?.path ?? image.path,
}}
source={{uri: (image.transformed ?? image.source).path}}
contentFit="contain"
accessible={true}
accessibilityIgnoresInvertColors
-380
View File
@@ -1,380 +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 {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 {MAX_ALT_TEXT} from '#/lib/constants'
import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {enforceLen} from '#/lib/strings/helpers'
import {gradients, s} from '#/lib/styles'
import {useTheme} from '#/lib/ThemeContext'
import {getKeys} from '#/lib/type-assertions'
import {useModalControls} from '#/state/modals'
import {GalleryModel} from '#/state/models/media/gallery'
import {ImageModel} from '#/state/models/media/image'
import {atoms as a} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {
AspectRatio11_Stroke2_Corner0_Rounded as A11,
AspectRatio34_Stroke2_Corner0_Rounded as A34,
AspectRatio43_Stroke2_Corner0_Rounded as A43,
} from '#/components/icons/AspectRatio'
import {CircleBanSign_Stroke2_Corner0_Rounded as Ban} from '#/components/icons/CircleBanSign'
import {
FlipHorizontal_Stroke2_Corner0_Rounded as FlipHorizontal,
FlipVertical_Stroke2_Corner0_Rounded as FlipVertical,
} from '#/components/icons/FlipImage'
import {Text} from '../util/text/Text'
import {TextInput} from './util'
export const snapPoints = ['80%']
const RATIOS = {
'4:3': {
icon: A43,
},
'1:1': {
icon: A11,
},
'3:4': {
icon: A34,
},
None: {
icon: Ban,
},
} 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<ImageEditor>(null)
const [scale, setScale] = useState<number>(image.attributes.scale ?? 1)
const [position, setPosition] = useState<Position | undefined>(
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')
// },
// },
{
icon: FlipHorizontal,
label: _(msg`Flip horizontal`),
onPress: onFlipHorizontal,
},
{
icon: FlipVertical,
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])
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 (
<View
testID="editImageModal"
style={[
pal.view,
styles.container,
s.flex1,
{
paddingHorizontal: isMobile ? 16 : undefined,
},
]}>
<Text style={[styles.title, pal.text]}>
<Trans>Edit image</Trans>
</Text>
<View style={[styles.gap18, s.flexRow]}>
<View>
<View
style={[styles.imgContainer, pal.borderDark, imgContainerStyles]}>
<ImageEditor
ref={editorRef}
style={styles.imgEditor}
image={image.cropped.path}
scale={scale}
border={0}
position={position}
onPositionChange={setPosition}
{...dimensions}
/>
</View>
<Slider
value={scale}
onValueChange={(v: number | number[]) =>
setScale(Array.isArray(v) ? v[0] : v)
}
minimumValue={1}
maximumValue={3}
/>
</View>
<View style={[a.gap_sm]}>
{!isMobile ? (
<Text type="sm-bold" style={pal.text}>
<Trans>Ratios</Trans>
</Text>
) : null}
<View style={imgControlStyles}>
{getKeys(RATIOS).map(ratio => {
const {icon} = RATIOS[ratio]
const isSelected = aspectRatio === ratio
return (
<Button
key={ratio}
label={ratio}
size="large"
shape="square"
variant="outline"
color={isSelected ? 'primary' : 'secondary'}
onPress={() => {
onSetRatio(ratio)
}}>
<View style={[a.align_center, a.gap_2xs]}>
<ButtonIcon icon={icon} />
<ButtonText style={[a.text_xs]}>{ratio}</ButtonText>
</View>
</Button>
)
})}
</View>
{!isMobile ? (
<Text type="sm-bold" style={[pal.text, styles.subsection]}>
<Trans>Transformations</Trans>
</Text>
) : null}
<View style={imgControlStyles}>
{adjustments.map(({label, icon, onPress}) => (
<Button
key={label}
label={label}
size="large"
shape="square"
variant="outline"
color="secondary"
onPress={onPress}>
<ButtonIcon icon={icon} />
</Button>
))}
</View>
</View>
</View>
<View style={[styles.gap18, styles.bottomSection, pal.border]}>
<Text type="sm-bold" style={pal.text} nativeID="alt-text">
<Trans>Accessibility</Trans>
</Text>
<TextInput
testID="altTextImageInput"
style={[
styles.textArea,
pal.border,
pal.text,
{
maxHeight: isMobile ? 50 : undefined,
},
]}
keyboardAppearance={theme.colorScheme}
multiline
value={altText}
onChangeText={text => setAltText(enforceLen(text, MAX_ALT_TEXT))}
accessibilityLabel={_(msg`Alt text`)}
accessibilityHint=""
accessibilityLabelledBy="alt-text"
/>
</View>
<View style={styles.btns}>
<Pressable onPress={onPressCancel} accessibilityRole="button">
<Text type="xl" style={pal.link}>
<Trans>Cancel</Trans>
</Text>
</Pressable>
<Pressable onPress={onPressSave} accessibilityRole="button">
<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}>
<Trans context="action">Done</Trans>
</Text>
</LinearGradient>
</Pressable>
</View>
</View>
)
})
const styles = StyleSheet.create({
container: {
gap: 18,
height: '100%',
width: '100%',
},
subsection: {marginTop: 12},
gap18: {gap: 18},
title: {
fontWeight: '600',
fontSize: 24,
},
btns: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
btn: {
borderRadius: 4,
paddingVertical: 8,
paddingHorizontal: 24,
},
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,
},
})
-4
View File
@@ -9,7 +9,6 @@ import {FullWindowOverlay} from '#/components/FullWindowOverlay'
import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop'
import * as AddAppPassword from './AddAppPasswords'
import * as AltImageModal from './AltImage'
import * as EditImageModal from './AltImage'
import * as ChangeEmailModal from './ChangeEmail'
import * as ChangeHandleModal from './ChangeHandle'
import * as ChangePasswordModal from './ChangePassword'
@@ -78,9 +77,6 @@ export function ModalsContainer() {
} else if (activeModal?.name === 'alt-text-image') {
snapPoints = AltImageModal.snapPoints
element = <AltImageModal.Component {...activeModal} />
} else if (activeModal?.name === 'edit-image') {
snapPoints = AltImageModal.snapPoints
element = <EditImageModal.Component {...activeModal} />
} else if (activeModal?.name === 'change-handle') {
snapPoints = ChangeHandleModal.snapPoints
element = <ChangeHandleModal.Component {...activeModal} />
+1 -8
View File
@@ -15,7 +15,6 @@ 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'
@@ -54,11 +53,7 @@ function Modal({modal}: {modal: ModalIface}) {
}
const onPressMask = () => {
if (
modal.name === 'crop-image' ||
modal.name === 'edit-image' ||
modal.name === 'alt-text-image'
) {
if (modal.name === 'crop-image' || modal.name === 'alt-text-image') {
return // dont close on mask presses during crop
}
closeModal()
@@ -95,8 +90,6 @@ function Modal({modal}: {modal: ModalIface}) {
element = <PostLanguagesSettingsModal.Component />
} else if (modal.name === 'alt-text-image') {
element = <AltTextImageModal.Component {...modal} />
} else if (modal.name === 'edit-image') {
element = <EditImageModal.Component {...modal} />
} else if (modal.name === 'verify-email') {
element = <VerifyEmailModal.Component {...modal} />
} else if (modal.name === 'change-email') {