Improvements to the alt text behaviors in the composer (#910)
* Add an image preview in the alt modal * Composer: add info about alt text and a green checkmark when done * Shrink the alt visual indicator a bit so it doesnt obscure the image * Fix typo * Fix: avoid requiring multiple tabs to save alt text * update react-native-screens * Improve the alt text help tip * Remove redundant hints --------- Co-authored-by: Ansh Nanda <anshnanda10@gmail.com>
This commit is contained in:
parent
25b3e14926
commit
bfaa6d73f3
6 changed files with 274 additions and 185 deletions
|
@ -1,16 +1,16 @@
|
|||
import React, {useCallback} from 'react'
|
||||
import React from 'react'
|
||||
import {ImageStyle, Keyboard} from 'react-native'
|
||||
import {GalleryModel} from 'state/models/media/gallery'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {colors} from 'lib/styles'
|
||||
import {s, colors} from 'lib/styles'
|
||||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||
import {ImageModel} from 'state/models/media/image'
|
||||
import {Image} from 'expo-image'
|
||||
import {Text} from 'view/com/util/text/Text'
|
||||
import {isDesktopWeb} from 'platform/detection'
|
||||
import {openAltTextModal} from 'lib/media/alt-text'
|
||||
import {useStores} from 'state/index'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
|
||||
interface Props {
|
||||
gallery: GalleryModel
|
||||
|
@ -18,67 +18,39 @@ interface Props {
|
|||
|
||||
export const Gallery = observer(function ({gallery}: Props) {
|
||||
const store = useStores()
|
||||
const getImageStyle = useCallback(() => {
|
||||
let side: number
|
||||
const pal = usePalette('default')
|
||||
|
||||
if (gallery.size === 1) {
|
||||
side = 250
|
||||
} else {
|
||||
side = (isDesktopWeb ? 560 : 350) / gallery.size
|
||||
}
|
||||
let side: number
|
||||
|
||||
return {
|
||||
height: side,
|
||||
width: side,
|
||||
}
|
||||
}, [gallery])
|
||||
if (gallery.size === 1) {
|
||||
side = 250
|
||||
} else {
|
||||
side = (isDesktopWeb ? 560 : 350) / gallery.size
|
||||
}
|
||||
|
||||
const imageStyle = getImageStyle()
|
||||
const handleAddImageAltText = useCallback(
|
||||
(image: ImageModel) => {
|
||||
Keyboard.dismiss()
|
||||
openAltTextModal(store, image)
|
||||
},
|
||||
[store],
|
||||
)
|
||||
const handleRemovePhoto = useCallback(
|
||||
(image: ImageModel) => {
|
||||
gallery.remove(image)
|
||||
},
|
||||
[gallery],
|
||||
)
|
||||
|
||||
const handleEditPhoto = useCallback(
|
||||
(image: ImageModel) => {
|
||||
gallery.edit(image)
|
||||
},
|
||||
[gallery],
|
||||
)
|
||||
const imageStyle = {
|
||||
height: side,
|
||||
width: side,
|
||||
}
|
||||
|
||||
const isOverflow = !isDesktopWeb && gallery.size > 2
|
||||
|
||||
const imageControlLabelStyle = {
|
||||
borderRadius: 5,
|
||||
paddingHorizontal: 10,
|
||||
position: 'absolute' as const,
|
||||
zIndex: 1,
|
||||
...(isOverflow
|
||||
? {
|
||||
left: 4,
|
||||
bottom: 4,
|
||||
}
|
||||
: isDesktopWeb && gallery.size < 3
|
||||
? {
|
||||
left: 8,
|
||||
top: 8,
|
||||
}
|
||||
: {
|
||||
left: 4,
|
||||
top: 4,
|
||||
}),
|
||||
}
|
||||
const altTextControlStyle = isOverflow
|
||||
? {
|
||||
left: 4,
|
||||
bottom: 4,
|
||||
}
|
||||
: isDesktopWeb && gallery.size < 3
|
||||
? {
|
||||
left: 8,
|
||||
top: 8,
|
||||
}
|
||||
: {
|
||||
left: 4,
|
||||
top: 4,
|
||||
}
|
||||
|
||||
const imageControlsSubgroupStyle = {
|
||||
const imageControlsStyle = {
|
||||
display: 'flex' as const,
|
||||
flexDirection: 'row' as const,
|
||||
position: 'absolute' as const,
|
||||
|
@ -103,63 +75,90 @@ export const Gallery = observer(function ({gallery}: Props) {
|
|||
}
|
||||
|
||||
return !gallery.isEmpty ? (
|
||||
<View testID="selectedPhotosView" style={styles.gallery}>
|
||||
{gallery.images.map(image => (
|
||||
<View key={`selected-image-${image.path}`} style={[imageStyle]}>
|
||||
<TouchableOpacity
|
||||
testID="altTextButton"
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Add alt text"
|
||||
accessibilityHint=""
|
||||
onPress={() => {
|
||||
handleAddImageAltText(image)
|
||||
}}
|
||||
style={imageControlLabelStyle}>
|
||||
<Text style={styles.imageControlTextContent}>ALT</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={imageControlsSubgroupStyle}>
|
||||
<>
|
||||
<View testID="selectedPhotosView" style={styles.gallery}>
|
||||
{gallery.images.map(image => (
|
||||
<View key={`selected-image-${image.path}`} style={[imageStyle]}>
|
||||
<TouchableOpacity
|
||||
testID="editPhotoButton"
|
||||
testID="altTextButton"
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Edit image"
|
||||
accessibilityLabel="Add alt text"
|
||||
accessibilityHint=""
|
||||
onPress={() => {
|
||||
handleEditPhoto(image)
|
||||
Keyboard.dismiss()
|
||||
openAltTextModal(store, image)
|
||||
}}
|
||||
style={styles.imageControl}>
|
||||
<FontAwesomeIcon
|
||||
icon="pen"
|
||||
size={12}
|
||||
style={{color: colors.white}}
|
||||
/>
|
||||
style={[styles.altTextControl, altTextControlStyle]}>
|
||||
<Text style={styles.altTextControlLabel}>ALT</Text>
|
||||
{image.altText.length > 0 ? (
|
||||
<FontAwesomeIcon
|
||||
icon="check"
|
||||
size={10}
|
||||
style={{color: colors.green3}}
|
||||
/>
|
||||
) : undefined}
|
||||
</TouchableOpacity>
|
||||
<View style={imageControlsStyle}>
|
||||
<TouchableOpacity
|
||||
testID="editPhotoButton"
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Edit image"
|
||||
accessibilityHint=""
|
||||
onPress={() => gallery.edit(image)}
|
||||
style={styles.imageControl}>
|
||||
<FontAwesomeIcon
|
||||
icon="pen"
|
||||
size={12}
|
||||
style={{color: colors.white}}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
testID="removePhotoButton"
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Remove image"
|
||||
accessibilityHint=""
|
||||
onPress={() => gallery.remove(image)}
|
||||
style={styles.imageControl}>
|
||||
<FontAwesomeIcon
|
||||
icon="xmark"
|
||||
size={16}
|
||||
style={{color: colors.white}}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
testID="removePhotoButton"
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Remove image"
|
||||
accessibilityLabel="Add alt text"
|
||||
accessibilityHint=""
|
||||
onPress={() => handleRemovePhoto(image)}
|
||||
style={styles.imageControl}>
|
||||
<FontAwesomeIcon
|
||||
icon="xmark"
|
||||
size={16}
|
||||
style={{color: colors.white}}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
onPress={() => {
|
||||
Keyboard.dismiss()
|
||||
openAltTextModal(store, image)
|
||||
}}
|
||||
style={styles.altTextHiddenRegion}
|
||||
/>
|
||||
|
||||
<Image
|
||||
testID="selectedPhotoImage"
|
||||
style={[styles.image, imageStyle] as ImageStyle}
|
||||
source={{
|
||||
uri: image.cropped?.path ?? image.path,
|
||||
}}
|
||||
accessible={true}
|
||||
accessibilityIgnoresInvertColors
|
||||
/>
|
||||
<Image
|
||||
testID="selectedPhotoImage"
|
||||
style={[styles.image, imageStyle] as ImageStyle}
|
||||
source={{
|
||||
uri: image.cropped?.path ?? image.path,
|
||||
}}
|
||||
accessible={true}
|
||||
accessibilityIgnoresInvertColors
|
||||
/>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
<View style={[styles.reminder]}>
|
||||
<View style={[styles.infoIcon, pal.viewLight]}>
|
||||
<FontAwesomeIcon icon="info" size={12} color={pal.colors.text} />
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
<Text type="sm" style={[pal.textLight, s.flex1]}>
|
||||
Alt text describes images for blind and low-vision users, and helps
|
||||
give context to everyone.
|
||||
</Text>
|
||||
</View>
|
||||
</>
|
||||
) : null
|
||||
})
|
||||
|
||||
|
@ -179,19 +178,46 @@ const styles = StyleSheet.create({
|
|||
height: 24,
|
||||
borderRadius: 12,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.75)',
|
||||
borderWidth: 0.5,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
imageControlTextContent: {
|
||||
altTextControl: {
|
||||
position: 'absolute',
|
||||
zIndex: 1,
|
||||
borderRadius: 6,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.75)',
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 3,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
altTextControlLabel: {
|
||||
color: 'white',
|
||||
fontSize: 12,
|
||||
fontWeight: 'bold',
|
||||
letterSpacing: 1,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.75)',
|
||||
borderWidth: 0.5,
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 3,
|
||||
},
|
||||
altTextHiddenRegion: {
|
||||
position: 'absolute',
|
||||
left: 4,
|
||||
right: 4,
|
||||
bottom: 4,
|
||||
top: 30,
|
||||
zIndex: 1,
|
||||
},
|
||||
|
||||
reminder: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
borderRadius: 8,
|
||||
paddingVertical: 14,
|
||||
},
|
||||
infoIcon: {
|
||||
width: 22,
|
||||
height: 22,
|
||||
borderRadius: 12,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue