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:
@@ -44,7 +44,6 @@ import {RichText} from '@atproto/api'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
|
||||
import {useAnalytics} from '#/lib/analytics/analytics'
|
||||
import * as apilib from '#/lib/api/index'
|
||||
@@ -68,9 +67,9 @@ import {logger} from '#/logger'
|
||||
import {isAndroid, isIOS, isNative, isWeb} from '#/platform/detection'
|
||||
import {useDialogStateControlContext} from '#/state/dialogs'
|
||||
import {emitPostCreated} from '#/state/events'
|
||||
import {ComposerImage, createInitialImages, pasteImage} from '#/state/gallery'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {useModals} from '#/state/modals'
|
||||
import {GalleryModel} from '#/state/models/media/gallery'
|
||||
import {useRequireAltTextEnabled} from '#/state/preferences'
|
||||
import {
|
||||
toPostLanguages,
|
||||
@@ -122,12 +121,14 @@ import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
import {Text as NewText} from '#/components/Typography'
|
||||
|
||||
const MAX_IMAGES = 4
|
||||
|
||||
type CancelRef = {
|
||||
onPressCancel: () => void
|
||||
}
|
||||
|
||||
type Props = ComposerOpts
|
||||
export const ComposePost = observer(function ComposePost({
|
||||
export const ComposePost = ({
|
||||
replyTo,
|
||||
onPost,
|
||||
quote: initQuote,
|
||||
@@ -139,7 +140,7 @@ export const ComposePost = observer(function ComposePost({
|
||||
cancelRef,
|
||||
}: Props & {
|
||||
cancelRef?: React.RefObject<CancelRef>
|
||||
}) {
|
||||
}) => {
|
||||
const {currentAccount} = useSession()
|
||||
const agent = useAgent()
|
||||
const {data: currentProfile} = useProfileQuery({did: currentAccount!.did})
|
||||
@@ -212,9 +213,8 @@ export const ComposePost = observer(function ComposePost({
|
||||
)
|
||||
const [postgate, setPostgate] = useState(createPostgateRecord({post: ''}))
|
||||
|
||||
const gallery = useMemo(
|
||||
() => new GalleryModel(initImageUris),
|
||||
[initImageUris],
|
||||
const [images, setImages] = useState<ComposerImage[]>(() =>
|
||||
createInitialImages(initImageUris),
|
||||
)
|
||||
const onClose = useCallback(() => {
|
||||
closeComposer()
|
||||
@@ -233,7 +233,7 @@ export const ComposePost = observer(function ComposePost({
|
||||
const onPressCancel = useCallback(() => {
|
||||
if (
|
||||
graphemeLength > 0 ||
|
||||
!gallery.isEmpty ||
|
||||
images.length !== 0 ||
|
||||
extGif ||
|
||||
videoUploadState.status !== 'idle'
|
||||
) {
|
||||
@@ -246,7 +246,7 @@ export const ComposePost = observer(function ComposePost({
|
||||
}, [
|
||||
extGif,
|
||||
graphemeLength,
|
||||
gallery.isEmpty,
|
||||
images.length,
|
||||
closeAllDialogs,
|
||||
discardPromptControl,
|
||||
onClose,
|
||||
@@ -299,22 +299,31 @@ export const ComposePost = observer(function ComposePost({
|
||||
[extLink, setExtLink],
|
||||
)
|
||||
|
||||
const onImageAdd = useCallback(
|
||||
(next: ComposerImage[]) => {
|
||||
setImages(prev => prev.concat(next.slice(0, MAX_IMAGES - prev.length)))
|
||||
},
|
||||
[setImages],
|
||||
)
|
||||
|
||||
const onPhotoPasted = useCallback(
|
||||
async (uri: string) => {
|
||||
track('Composer:PastedPhotos')
|
||||
if (uri.startsWith('data:video/')) {
|
||||
selectVideo({uri, type: 'video', height: 0, width: 0})
|
||||
} else {
|
||||
await gallery.paste(uri)
|
||||
const res = await pasteImage(uri)
|
||||
onImageAdd([res])
|
||||
}
|
||||
},
|
||||
[gallery, track, selectVideo],
|
||||
[track, selectVideo, onImageAdd],
|
||||
)
|
||||
|
||||
const isAltTextRequiredAndMissing = useMemo(() => {
|
||||
if (!requireAltTextEnabled) return false
|
||||
|
||||
if (gallery.needsAltText) return true
|
||||
if (images.some(img => img.alt === '')) return true
|
||||
|
||||
if (extGif) {
|
||||
if (!extLink?.meta?.description) return true
|
||||
|
||||
@@ -322,7 +331,7 @@ export const ComposePost = observer(function ComposePost({
|
||||
if (!parsedAlt.isPreferred) return true
|
||||
}
|
||||
return false
|
||||
}, [gallery.needsAltText, extLink, extGif, requireAltTextEnabled])
|
||||
}, [images, extLink, extGif, requireAltTextEnabled])
|
||||
|
||||
const onPressPublish = React.useCallback(
|
||||
async (finishedUploading?: boolean) => {
|
||||
@@ -347,7 +356,7 @@ export const ComposePost = observer(function ComposePost({
|
||||
|
||||
if (
|
||||
richtext.text.trim().length === 0 &&
|
||||
gallery.isEmpty &&
|
||||
images.length === 0 &&
|
||||
!extLink &&
|
||||
!quote &&
|
||||
videoUploadState.status === 'idle'
|
||||
@@ -368,7 +377,7 @@ export const ComposePost = observer(function ComposePost({
|
||||
await apilib.post(agent, {
|
||||
rawText: richtext.text,
|
||||
replyTo: replyTo?.uri,
|
||||
images: gallery.images,
|
||||
images,
|
||||
quote,
|
||||
extLink,
|
||||
labels,
|
||||
@@ -405,7 +414,7 @@ export const ComposePost = observer(function ComposePost({
|
||||
} catch (e: any) {
|
||||
logger.error(e, {
|
||||
message: `Composer: create post failed`,
|
||||
hasImages: gallery.size > 0,
|
||||
hasImages: images.length > 0,
|
||||
})
|
||||
|
||||
if (extLink) {
|
||||
@@ -427,7 +436,7 @@ export const ComposePost = observer(function ComposePost({
|
||||
} finally {
|
||||
if (postUri) {
|
||||
logEvent('post:create', {
|
||||
imageCount: gallery.size,
|
||||
imageCount: images.length,
|
||||
isReply: replyTo != null,
|
||||
hasLink: extLink != null,
|
||||
hasQuote: quote != null,
|
||||
@@ -436,7 +445,7 @@ export const ComposePost = observer(function ComposePost({
|
||||
})
|
||||
}
|
||||
track('Create Post', {
|
||||
imageCount: gallery.size,
|
||||
imageCount: images.length,
|
||||
})
|
||||
if (replyTo && replyTo.uri) track('Post:Reply')
|
||||
}
|
||||
@@ -472,9 +481,7 @@ export const ComposePost = observer(function ComposePost({
|
||||
agent,
|
||||
captions,
|
||||
extLink,
|
||||
gallery.images,
|
||||
gallery.isEmpty,
|
||||
gallery.size,
|
||||
images,
|
||||
graphemeLength,
|
||||
isAltTextRequiredAndMissing,
|
||||
isProcessing,
|
||||
@@ -516,12 +523,12 @@ export const ComposePost = observer(function ComposePost({
|
||||
: _(msg`What's up?`)
|
||||
|
||||
const canSelectImages =
|
||||
gallery.size < 4 &&
|
||||
images.length < MAX_IMAGES &&
|
||||
!extLink &&
|
||||
videoUploadState.status === 'idle' &&
|
||||
!videoUploadState.video
|
||||
const hasMedia =
|
||||
gallery.size > 0 || Boolean(extLink) || Boolean(videoUploadState.video)
|
||||
images.length > 0 || Boolean(extLink) || Boolean(videoUploadState.video)
|
||||
|
||||
const onEmojiButtonPress = useCallback(() => {
|
||||
openEmojiPicker?.(textInput.current?.getCursorPosition())
|
||||
@@ -716,8 +723,8 @@ export const ComposePost = observer(function ComposePost({
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Gallery gallery={gallery} />
|
||||
{gallery.isEmpty && extLink && (
|
||||
<Gallery images={images} onChange={setImages} />
|
||||
{images.length === 0 && extLink && (
|
||||
<View style={a.relative}>
|
||||
<ExternalEmbed
|
||||
link={extLink}
|
||||
@@ -801,13 +808,17 @@ export const ComposePost = observer(function ComposePost({
|
||||
<VideoUploadToolbar state={videoUploadState} />
|
||||
) : (
|
||||
<ToolbarWrapper style={[a.flex_row, a.align_center, a.gap_xs]}>
|
||||
<SelectPhotoBtn gallery={gallery} disabled={!canSelectImages} />
|
||||
<SelectPhotoBtn
|
||||
size={images.length}
|
||||
disabled={!canSelectImages}
|
||||
onAdd={onImageAdd}
|
||||
/>
|
||||
<SelectVideoBtn
|
||||
onSelectVideo={selectVideo}
|
||||
disabled={!canSelectImages}
|
||||
setError={setError}
|
||||
/>
|
||||
<OpenCameraBtn gallery={gallery} disabled={!canSelectImages} />
|
||||
<OpenCameraBtn disabled={!canSelectImages} onAdd={onImageAdd} />
|
||||
<SelectGifBtn
|
||||
onClose={focusTextInput}
|
||||
onSelectGif={onSelectGif}
|
||||
@@ -842,7 +853,7 @@ export const ComposePost = observer(function ComposePost({
|
||||
/>
|
||||
</KeyboardAvoidingView>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export function useComposerCancelRef() {
|
||||
return useRef<CancelRef>(null)
|
||||
|
||||
@@ -26,7 +26,7 @@ export const ExternalEmbed = ({
|
||||
title: link.meta?.title ?? link.uri,
|
||||
uri: link.uri,
|
||||
description: link.meta?.description ?? '',
|
||||
thumb: link.localThumb?.path,
|
||||
thumb: link.localThumb?.source.path,
|
||||
},
|
||||
[link],
|
||||
)
|
||||
|
||||
@@ -43,7 +43,7 @@ export function GifAltText({
|
||||
title: linkProp.meta?.title ?? linkProp.uri,
|
||||
uri: linkProp.uri,
|
||||
description: linkProp.meta?.description ?? '',
|
||||
thumb: linkProp.localThumb?.path,
|
||||
thumb: linkProp.localThumb?.source.path,
|
||||
},
|
||||
params: parseEmbedPlayerFromUrl(linkProp.uri),
|
||||
}
|
||||
|
||||
@@ -1,29 +1,36 @@
|
||||
import React, {useState} from 'react'
|
||||
import {ImageStyle, Keyboard, LayoutChangeEvent} from 'react-native'
|
||||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||
import React from 'react'
|
||||
import {
|
||||
ImageStyle,
|
||||
Keyboard,
|
||||
LayoutChangeEvent,
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
ViewStyle,
|
||||
} from 'react-native'
|
||||
import {Image} from 'expo-image'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
|
||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||
import {Dimensions} from '#/lib/media/types'
|
||||
import {colors, s} from '#/lib/styles'
|
||||
import {isNative} from '#/platform/detection'
|
||||
import {ComposerImage, cropImage} from '#/state/gallery'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {GalleryModel} from '#/state/models/media/gallery'
|
||||
import {Text} from '#/view/com/util/text/Text'
|
||||
import {useTheme} from '#/alf'
|
||||
|
||||
const IMAGE_GAP = 8
|
||||
|
||||
interface GalleryProps {
|
||||
gallery: GalleryModel
|
||||
images: ComposerImage[]
|
||||
onChange: (next: ComposerImage[]) => void
|
||||
}
|
||||
|
||||
export const Gallery = (props: GalleryProps) => {
|
||||
const [containerInfo, setContainerInfo] = useState<Dimensions | undefined>()
|
||||
export let Gallery = (props: GalleryProps): React.ReactNode => {
|
||||
const [containerInfo, setContainerInfo] = React.useState<Dimensions>()
|
||||
|
||||
const onLayout = (evt: LayoutChangeEvent) => {
|
||||
const {width, height} = evt.nativeEvent.layout
|
||||
@@ -41,177 +48,190 @@ export const Gallery = (props: GalleryProps) => {
|
||||
</View>
|
||||
)
|
||||
}
|
||||
Gallery = React.memo(Gallery)
|
||||
|
||||
interface GalleryInnerProps extends GalleryProps {
|
||||
containerInfo: Dimensions
|
||||
}
|
||||
|
||||
const GalleryInner = observer(function GalleryImpl({
|
||||
gallery,
|
||||
containerInfo,
|
||||
}: GalleryInnerProps) {
|
||||
const {_} = useLingui()
|
||||
const GalleryInner = ({images, containerInfo, onChange}: GalleryInnerProps) => {
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const {openModal} = useModalControls()
|
||||
const t = useTheme()
|
||||
|
||||
let side: number
|
||||
const {altTextControlStyle, imageControlsStyle, imageStyle} =
|
||||
React.useMemo(() => {
|
||||
const side =
|
||||
images.length === 1
|
||||
? 250
|
||||
: (containerInfo.width - IMAGE_GAP * (images.length - 1)) /
|
||||
images.length
|
||||
|
||||
if (gallery.size === 1) {
|
||||
side = 250
|
||||
} else {
|
||||
side = (containerInfo.width - IMAGE_GAP * (gallery.size - 1)) / gallery.size
|
||||
}
|
||||
const isOverflow = isMobile && images.length > 2
|
||||
|
||||
const imageStyle = {
|
||||
height: side,
|
||||
width: side,
|
||||
}
|
||||
|
||||
const isOverflow = isMobile && gallery.size > 2
|
||||
|
||||
const altTextControlStyle = isOverflow
|
||||
? {
|
||||
left: 4,
|
||||
bottom: 4,
|
||||
}
|
||||
: !isMobile && gallery.size < 3
|
||||
? {
|
||||
left: 8,
|
||||
top: 8,
|
||||
}
|
||||
: {
|
||||
left: 4,
|
||||
top: 4,
|
||||
return {
|
||||
altTextControlStyle: isOverflow
|
||||
? {left: 4, bottom: 4}
|
||||
: !isMobile && images.length < 3
|
||||
? {left: 8, top: 8}
|
||||
: {left: 4, top: 4},
|
||||
imageControlsStyle: {
|
||||
display: 'flex' as const,
|
||||
flexDirection: 'row' as const,
|
||||
position: 'absolute' as const,
|
||||
...(isOverflow
|
||||
? {top: 4, right: 4, gap: 4}
|
||||
: !isMobile && images.length < 3
|
||||
? {top: 8, right: 8, gap: 8}
|
||||
: {top: 4, right: 4, gap: 4}),
|
||||
zIndex: 1,
|
||||
},
|
||||
imageStyle: {
|
||||
height: side,
|
||||
width: side,
|
||||
},
|
||||
}
|
||||
}, [images.length, containerInfo, isMobile])
|
||||
|
||||
const imageControlsStyle = {
|
||||
display: 'flex' as const,
|
||||
flexDirection: 'row' as const,
|
||||
position: 'absolute' as const,
|
||||
...(isOverflow
|
||||
? {
|
||||
top: 4,
|
||||
right: 4,
|
||||
gap: 4,
|
||||
}
|
||||
: !isMobile && gallery.size < 3
|
||||
? {
|
||||
top: 8,
|
||||
right: 8,
|
||||
gap: 8,
|
||||
}
|
||||
: {
|
||||
top: 4,
|
||||
right: 4,
|
||||
gap: 4,
|
||||
}),
|
||||
zIndex: 1,
|
||||
}
|
||||
|
||||
return !gallery.isEmpty ? (
|
||||
return images.length !== 0 ? (
|
||||
<>
|
||||
<View testID="selectedPhotosView" style={styles.gallery}>
|
||||
{gallery.images.map(image => (
|
||||
<View key={`selected-image-${image.path}`} style={[imageStyle]}>
|
||||
<TouchableOpacity
|
||||
testID="altTextButton"
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Add alt text`)}
|
||||
accessibilityHint=""
|
||||
onPress={() => {
|
||||
Keyboard.dismiss()
|
||||
openModal({
|
||||
name: 'alt-text-image',
|
||||
image,
|
||||
})
|
||||
{images.map((image, index) => {
|
||||
return (
|
||||
<GalleryItem
|
||||
key={image.source.id}
|
||||
image={image}
|
||||
altTextControlStyle={altTextControlStyle}
|
||||
imageControlsStyle={imageControlsStyle}
|
||||
imageStyle={imageStyle}
|
||||
onChange={next => {
|
||||
onChange(
|
||||
images.map(i => (i.source === image.source ? next : i)),
|
||||
)
|
||||
}}
|
||||
style={[styles.altTextControl, altTextControlStyle]}>
|
||||
{image.altText.length > 0 ? (
|
||||
<FontAwesomeIcon
|
||||
icon="check"
|
||||
size={10}
|
||||
style={{color: t.palette.white}}
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon
|
||||
icon="plus"
|
||||
size={10}
|
||||
style={{color: t.palette.white}}
|
||||
/>
|
||||
)}
|
||||
<Text style={styles.altTextControlLabel} accessible={false}>
|
||||
<Trans>ALT</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={imageControlsStyle}>
|
||||
<TouchableOpacity
|
||||
testID="editPhotoButton"
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Edit image`)}
|
||||
accessibilityHint=""
|
||||
onPress={() => {
|
||||
if (isNative) {
|
||||
gallery.crop(image)
|
||||
} else {
|
||||
openModal({
|
||||
name: 'edit-image',
|
||||
image,
|
||||
gallery,
|
||||
})
|
||||
}
|
||||
}}
|
||||
style={styles.imageControl}>
|
||||
<FontAwesomeIcon
|
||||
icon="pen"
|
||||
size={12}
|
||||
style={{color: colors.white}}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
testID="removePhotoButton"
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Remove image`)}
|
||||
accessibilityHint=""
|
||||
onPress={() => gallery.remove(image)}
|
||||
style={styles.imageControl}>
|
||||
<FontAwesomeIcon
|
||||
icon="xmark"
|
||||
size={16}
|
||||
style={{color: colors.white}}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Add alt text`)}
|
||||
accessibilityHint=""
|
||||
onPress={() => {
|
||||
Keyboard.dismiss()
|
||||
openModal({
|
||||
name: 'alt-text-image',
|
||||
image,
|
||||
})
|
||||
}}
|
||||
style={styles.altTextHiddenRegion}
|
||||
/>
|
||||
onRemove={() => {
|
||||
const next = images.slice()
|
||||
next.splice(index, 1)
|
||||
|
||||
<Image
|
||||
testID="selectedPhotoImage"
|
||||
style={[styles.image, imageStyle] as ImageStyle}
|
||||
source={{
|
||||
uri: image.cropped?.path ?? image.path,
|
||||
onChange(next)
|
||||
}}
|
||||
accessible={true}
|
||||
accessibilityIgnoresInvertColors
|
||||
/>
|
||||
</View>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
</View>
|
||||
<AltTextReminder />
|
||||
</>
|
||||
) : null
|
||||
})
|
||||
}
|
||||
|
||||
type GalleryItemProps = {
|
||||
image: ComposerImage
|
||||
altTextControlStyle?: ViewStyle
|
||||
imageControlsStyle?: ViewStyle
|
||||
imageStyle?: ViewStyle
|
||||
onChange: (next: ComposerImage) => void
|
||||
onRemove: () => void
|
||||
}
|
||||
|
||||
const GalleryItem = ({
|
||||
image,
|
||||
altTextControlStyle,
|
||||
imageControlsStyle,
|
||||
imageStyle,
|
||||
onChange,
|
||||
onRemove,
|
||||
}: GalleryItemProps): React.ReactNode => {
|
||||
const {_} = useLingui()
|
||||
const t = useTheme()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
const onImageEdit = () => {
|
||||
if (isNative) {
|
||||
cropImage(image).then(next => {
|
||||
onChange(next)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const onAltTextEdit = () => {
|
||||
Keyboard.dismiss()
|
||||
openModal({name: 'alt-text-image', image, onChange})
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={imageStyle}>
|
||||
<TouchableOpacity
|
||||
testID="altTextButton"
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Add alt text`)}
|
||||
accessibilityHint=""
|
||||
onPress={onAltTextEdit}
|
||||
style={[styles.altTextControl, altTextControlStyle]}>
|
||||
{image.alt.length !== 0 ? (
|
||||
<FontAwesomeIcon
|
||||
icon="check"
|
||||
size={10}
|
||||
style={{color: t.palette.white}}
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon
|
||||
icon="plus"
|
||||
size={10}
|
||||
style={{color: t.palette.white}}
|
||||
/>
|
||||
)}
|
||||
<Text style={styles.altTextControlLabel} accessible={false}>
|
||||
<Trans>ALT</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={imageControlsStyle}>
|
||||
{isNative && (
|
||||
<TouchableOpacity
|
||||
testID="editPhotoButton"
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Edit image`)}
|
||||
accessibilityHint=""
|
||||
onPress={onImageEdit}
|
||||
style={styles.imageControl}>
|
||||
<FontAwesomeIcon
|
||||
icon="pen"
|
||||
size={12}
|
||||
style={{color: colors.white}}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
<TouchableOpacity
|
||||
testID="removePhotoButton"
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Remove image`)}
|
||||
accessibilityHint=""
|
||||
onPress={onRemove}
|
||||
style={styles.imageControl}>
|
||||
<FontAwesomeIcon
|
||||
icon="xmark"
|
||||
size={16}
|
||||
style={{color: colors.white}}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Add alt text`)}
|
||||
accessibilityHint=""
|
||||
onPress={onAltTextEdit}
|
||||
style={styles.altTextHiddenRegion}
|
||||
/>
|
||||
|
||||
<Image
|
||||
testID="selectedPhotoImage"
|
||||
style={[styles.image, imageStyle] as ImageStyle}
|
||||
source={{
|
||||
uri: (image.transformed ?? image.source).path,
|
||||
}}
|
||||
accessible={true}
|
||||
accessibilityIgnoresInvertColors
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export function AltTextReminder() {
|
||||
const t = useTheme()
|
||||
|
||||
@@ -9,17 +9,17 @@ import {useCameraPermission} from '#/lib/hooks/usePermissions'
|
||||
import {openCamera} from '#/lib/media/picker'
|
||||
import {logger} from '#/logger'
|
||||
import {isMobileWeb, isNative} from '#/platform/detection'
|
||||
import {GalleryModel} from '#/state/models/media/gallery'
|
||||
import {ComposerImage, createComposerImage} from '#/state/gallery'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Button} from '#/components/Button'
|
||||
import {Camera_Stroke2_Corner0_Rounded as Camera} from '#/components/icons/Camera'
|
||||
|
||||
type Props = {
|
||||
gallery: GalleryModel
|
||||
disabled?: boolean
|
||||
onAdd: (next: ComposerImage[]) => void
|
||||
}
|
||||
|
||||
export function OpenCameraBtn({gallery, disabled}: Props) {
|
||||
export function OpenCameraBtn({disabled, onAdd}: Props) {
|
||||
const {track} = useAnalytics()
|
||||
const {_} = useLingui()
|
||||
const {requestCameraAccessIfNeeded} = useCameraPermission()
|
||||
@@ -48,13 +48,16 @@ export function OpenCameraBtn({gallery, disabled}: Props) {
|
||||
if (mediaPermissionRes) {
|
||||
await MediaLibrary.createAssetAsync(img.path)
|
||||
}
|
||||
gallery.add(img)
|
||||
|
||||
const res = await createComposerImage(img)
|
||||
|
||||
onAdd([res])
|
||||
} catch (err: any) {
|
||||
// ignore
|
||||
logger.warn('Error using camera', {error: err})
|
||||
}
|
||||
}, [
|
||||
gallery,
|
||||
onAdd,
|
||||
track,
|
||||
requestCameraAccessIfNeeded,
|
||||
mediaPermissionRes,
|
||||
|
||||
@@ -5,18 +5,20 @@ import {useLingui} from '@lingui/react'
|
||||
|
||||
import {useAnalytics} from '#/lib/analytics/analytics'
|
||||
import {usePhotoLibraryPermission} from '#/lib/hooks/usePermissions'
|
||||
import {openPicker} from '#/lib/media/picker'
|
||||
import {isNative} from '#/platform/detection'
|
||||
import {GalleryModel} from '#/state/models/media/gallery'
|
||||
import {ComposerImage, createComposerImage} from '#/state/gallery'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Button} from '#/components/Button'
|
||||
import {Image_Stroke2_Corner0_Rounded as Image} from '#/components/icons/Image'
|
||||
|
||||
type Props = {
|
||||
gallery: GalleryModel
|
||||
size: number
|
||||
disabled?: boolean
|
||||
onAdd: (next: ComposerImage[]) => void
|
||||
}
|
||||
|
||||
export function SelectPhotoBtn({gallery, disabled}: Props) {
|
||||
export function SelectPhotoBtn({size, disabled, onAdd}: Props) {
|
||||
const {track} = useAnalytics()
|
||||
const {_} = useLingui()
|
||||
const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission()
|
||||
@@ -29,8 +31,17 @@ export function SelectPhotoBtn({gallery, disabled}: Props) {
|
||||
return
|
||||
}
|
||||
|
||||
gallery.pick()
|
||||
}, [track, requestPhotoAccessIfNeeded, gallery])
|
||||
const images = await openPicker({
|
||||
selectionLimit: 4 - size,
|
||||
allowsMultipleSelection: true,
|
||||
})
|
||||
|
||||
const results = await Promise.all(
|
||||
images.map(img => createComposerImage(img)),
|
||||
)
|
||||
|
||||
onAdd(results)
|
||||
}, [track, requestPhotoAccessIfNeeded, size, onAdd])
|
||||
|
||||
return (
|
||||
<Button
|
||||
|
||||
@@ -3,6 +3,7 @@ import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {logger} from '#/logger'
|
||||
import {createComposerImage} from '#/state/gallery'
|
||||
import {useFetchDid} from '#/state/queries/handle'
|
||||
import {useGetPost} from '#/state/queries/post'
|
||||
import {useAgent} from '#/state/session'
|
||||
@@ -26,7 +27,6 @@ import {
|
||||
isBskyStartUrl,
|
||||
isShortLink,
|
||||
} from 'lib/strings/url-helpers'
|
||||
import {ImageModel} from 'state/models/media/image'
|
||||
import {ComposerOpts} from 'state/shell/composer'
|
||||
|
||||
export function useExternalLinkFetch({
|
||||
@@ -161,14 +161,15 @@ export function useExternalLinkFetch({
|
||||
timeout: 15e3,
|
||||
})
|
||||
.catch(() => undefined)
|
||||
.then(localThumb => {
|
||||
.then(thumb => (thumb ? createComposerImage(thumb) : undefined))
|
||||
.then(thumb => {
|
||||
if (aborted) {
|
||||
return
|
||||
}
|
||||
setExtLink({
|
||||
...extLink,
|
||||
isLoading: false, // done
|
||||
localThumb: localThumb ? new ImageModel(localThumb) : undefined,
|
||||
localThumb: thumb,
|
||||
})
|
||||
})
|
||||
return cleanup
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
@@ -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} />
|
||||
|
||||
@@ -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') {
|
||||
|
||||
Reference in New Issue
Block a user