diff --git a/src/view/com/composer/photos/Gallery.tsx b/src/view/com/composer/photos/Gallery.tsx
index 886dc193e3..250b549258 100644
--- a/src/view/com/composer/photos/Gallery.tsx
+++ b/src/view/com/composer/photos/Gallery.tsx
@@ -1,5 +1,5 @@
import React from 'react'
-import {ImageStyle, Keyboard, LayoutChangeEvent} from 'react-native'
+import {ImageStyle, LayoutChangeEvent, ViewStyle} from 'react-native'
import {StyleSheet, TouchableOpacity, View} from 'react-native'
import {Image} from 'expo-image'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
@@ -7,13 +7,14 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {ComposerImage, cropImage} from '#/state/gallery'
-import {useModalControls} from '#/state/modals'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {Dimensions} from 'lib/media/types'
import {colors, s} from 'lib/styles'
import {isNative} from 'platform/detection'
import {Text} from 'view/com/util/text/Text'
import {useTheme} from '#/alf'
+import * as Dialog from '#/components/Dialog'
+import {ImageAltTextDialog} from './ImageAltTextDialog'
const IMAGE_GAP = 8
@@ -48,165 +49,65 @@ interface GalleryInnerProps extends GalleryProps {
}
const GalleryInner = ({images, containerInfo, onChange}: GalleryInnerProps) => {
- const {_} = useLingui()
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 (images.length === 1) {
- side = 250
- } else {
- side =
- (containerInfo.width - IMAGE_GAP * (images.length - 1)) / images.length
- }
+ const isOverflow = isMobile && images.length > 2
- const imageStyle = {
- height: side,
- width: side,
- }
-
- const isOverflow = isMobile && images.length > 2
-
- const altTextControlStyle = isOverflow
- ? {left: 4, bottom: 4}
- : !isMobile && images.length < 3
- ? {left: 8, top: 8}
- : {left: 4, top: 4}
-
- const 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,
- }
+ 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])
return images.length !== 0 ? (
<>
{images.map((image, index) => {
- const onRemove = () => {
- const next = images.slice()
- next.splice(index, 1)
-
- onChange(next)
- }
-
- const onAltText = () => {
- Keyboard.dismiss()
- openModal({
- name: 'alt-text-image',
- image,
- onChange(next) {
- onChange(
- images.map(i => (i.source === image.source ? next : i)),
- )
- },
- })
- }
-
- const onEdit = () => {
- if (isNative) {
- cropImage(image).then(next => {
- if (next === image) {
- return
- }
-
- onChange(
- images.map(i => (i.source === image.source ? next : i)),
- )
- })
- } else {
- // openModal({
- // name: 'edit-image',
- // image,
- // gallery,
- // })
- }
- }
-
return (
-
-
- {image.alt.length !== 0 ? (
-
- ) : (
-
- )}
-
- ALT
-
-
-
- {isNative && (
-
-
-
- )}
-
-
-
-
-
+ {
+ onChange(
+ images.map(i => (i.source === image.source ? next : i)),
+ )
+ }}
+ onRemove={() => {
+ const next = images.slice()
+ next.splice(index, 1)
-
-
+ onChange(next)
+ }}
+ />
)
})}
@@ -215,6 +116,125 @@ const GalleryInner = ({images, containerInfo, onChange}: GalleryInnerProps) => {
) : 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 altTextControl = Dialog.useDialogControl()
+
+ const onEdit = () => {
+ if (isNative) {
+ cropImage(image).then(next => {
+ onChange(next)
+ })
+ } else {
+ // openModal({
+ // name: 'edit-image',
+ // image,
+ // gallery,
+ // })
+ }
+ }
+
+ return (
+
+
+ {image.alt.length !== 0 ? (
+
+ ) : (
+
+ )}
+
+ ALT
+
+
+
+ {isNative && (
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
export function AltTextReminder() {
const t = useTheme()
return (
diff --git a/src/view/com/composer/photos/ImageAltTextDialog.tsx b/src/view/com/composer/photos/ImageAltTextDialog.tsx
new file mode 100644
index 0000000000..b029d8ceb6
--- /dev/null
+++ b/src/view/com/composer/photos/ImageAltTextDialog.tsx
@@ -0,0 +1,124 @@
+import React from 'react'
+import {ImageStyle, useWindowDimensions, View} from 'react-native'
+import {Image} from 'expo-image'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+
+import {isWeb} from '#/platform/detection'
+import {ComposerImage} from '#/state/gallery'
+import {atoms as a, useTheme} from '#/alf'
+import {Button, ButtonText} from '#/components/Button'
+import * as Dialog from '#/components/Dialog'
+import * as TextField from '#/components/forms/TextField'
+import {Text} from '#/components/Typography'
+
+type Props = {
+ control: Dialog.DialogOuterProps['control']
+ image: ComposerImage
+ onChange: (next: ComposerImage) => void
+}
+
+export const ImageAltTextDialog = (props: Props): React.ReactNode => {
+ return (
+
+
+
+
+
+ )
+}
+
+const ImageAltTextInner = ({
+ control,
+ image,
+ onChange,
+}: Props): React.ReactNode => {
+ const {_} = useLingui()
+ const t = useTheme()
+
+ const windim = useWindowDimensions()
+
+ const [altText, setAltText] = React.useState(image.alt)
+ const trimmed = React.useMemo(() => altText.trim(), [altText])
+
+ const onPressSubmit = React.useCallback(() => {
+ control.close()
+ onChange({...image, alt: trimmed})
+ }, [control, image, trimmed, onChange])
+
+ const imageStyle = React.useMemo(() => {
+ const maxWidth = isWeb ? 450 : windim.width
+ const source = image.transformed ?? image.source
+
+ if (source.height > source.width) {
+ return {
+ resizeMode: 'contain',
+ width: '100%',
+ aspectRatio: 1,
+ borderRadius: 8,
+ }
+ }
+ return {
+ width: '100%',
+ height: (maxWidth / source.width) * source.height,
+ borderRadius: 8,
+ }
+ }, [image, windim])
+
+ return (
+
+
+
+
+
+
+
+ Descriptive alt text
+
+
+ setAltText(text)}
+ value={altText}
+ multiline
+ numberOfLines={3}
+ autoFocus
+ />
+
+
+
+
+
+ {/* below the text input to force tab order */}
+
+
+ Add ALT text
+
+
+
+
+
+
+
+
+ )
+}