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,5 +1,15 @@
|
|||
import React, {useCallback, useState} from 'react'
|
||||
import {StyleSheet, TextInput, TouchableOpacity, View} from 'react-native'
|
||||
import React, {useMemo, useCallback, useState} from 'react'
|
||||
import {
|
||||
ImageStyle,
|
||||
KeyboardAvoidingView,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
useWindowDimensions,
|
||||
} from 'react-native'
|
||||
import {Image} from 'expo-image'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {gradients, s} from 'lib/styles'
|
||||
import {enforceLen} from 'lib/strings/helpers'
|
||||
|
@ -8,7 +18,7 @@ import {useTheme} from 'lib/ThemeContext'
|
|||
import {Text} from '../util/text/Text'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
import {useStores} from 'state/index'
|
||||
import {isDesktopWeb} from 'platform/detection'
|
||||
import {isDesktopWeb, isAndroid} from 'platform/detection'
|
||||
import {ImageModel} from 'state/models/media/image'
|
||||
|
||||
export const snapPoints = ['fullscreen']
|
||||
|
@ -22,6 +32,24 @@ export function Component({image}: Props) {
|
|||
const store = useStores()
|
||||
const theme = useTheme()
|
||||
const [altText, setAltText] = useState(image.altText)
|
||||
const windim = useWindowDimensions()
|
||||
|
||||
const imageStyles = useMemo<ImageStyle>(() => {
|
||||
const maxWidth = isDesktopWeb ? 450 : windim.width
|
||||
if (image.height > image.width) {
|
||||
return {
|
||||
resizeMode: 'contain',
|
||||
width: '100%',
|
||||
aspectRatio: 1,
|
||||
borderRadius: 8,
|
||||
}
|
||||
}
|
||||
return {
|
||||
width: '100%',
|
||||
height: (maxWidth / image.width) * image.height,
|
||||
borderRadius: 8,
|
||||
}
|
||||
}, [image, windim])
|
||||
|
||||
const onPressSave = useCallback(() => {
|
||||
image.setAltText(altText)
|
||||
|
@ -33,69 +61,94 @@ export function Component({image}: Props) {
|
|||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
testID="altTextImageModal"
|
||||
style={[pal.view, styles.container, s.flex1]}
|
||||
nativeID="imageAltText">
|
||||
<Text style={[styles.title, pal.text]}>Add alt text</Text>
|
||||
<TextInput
|
||||
testID="altTextImageInput"
|
||||
style={[styles.textArea, pal.border, pal.text]}
|
||||
keyboardAppearance={theme.colorScheme}
|
||||
multiline
|
||||
value={altText}
|
||||
onChangeText={text => setAltText(enforceLen(text, MAX_ALT_TEXT))}
|
||||
accessibilityLabel="Image alt text"
|
||||
accessibilityHint="Sets image alt text for screenreaders"
|
||||
accessibilityLabelledBy="imageAltText"
|
||||
/>
|
||||
<View style={styles.buttonControls}>
|
||||
<TouchableOpacity
|
||||
testID="altTextImageSaveBtn"
|
||||
onPress={onPressSave}
|
||||
accessibilityLabel="Save alt text"
|
||||
accessibilityHint={`Saves alt text, which reads: ${altText}`}
|
||||
accessibilityRole="button">
|
||||
<LinearGradient
|
||||
colors={[gradients.blueLight.start, gradients.blueLight.end]}
|
||||
start={{x: 0, y: 0}}
|
||||
end={{x: 1, y: 1}}
|
||||
style={[styles.button]}>
|
||||
<Text type="button-lg" style={[s.white, s.bold]}>
|
||||
Save
|
||||
</Text>
|
||||
</LinearGradient>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
testID="altTextImageCancelBtn"
|
||||
onPress={onPressCancel}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Cancel add image alt text"
|
||||
accessibilityHint="Exits adding alt text to image"
|
||||
onAccessibilityEscape={onPressCancel}>
|
||||
<View style={[styles.button]}>
|
||||
<Text type="button-lg" style={[pal.textLight]}>
|
||||
Cancel
|
||||
</Text>
|
||||
<KeyboardAvoidingView
|
||||
behavior={isAndroid ? 'height' : 'padding'}
|
||||
style={[pal.view, styles.container]}>
|
||||
<ScrollView
|
||||
testID="altTextImageModal"
|
||||
style={styles.scrollContainer}
|
||||
keyboardShouldPersistTaps="always"
|
||||
nativeID="imageAltText">
|
||||
<View style={styles.scrollInner}>
|
||||
<View style={[pal.viewLight, styles.imageContainer]}>
|
||||
<Image
|
||||
testID="selectedPhotoImage"
|
||||
style={imageStyles}
|
||||
source={{
|
||||
uri: image.cropped?.path ?? image.path,
|
||||
}}
|
||||
accessible={true}
|
||||
accessibilityIgnoresInvertColors
|
||||
/>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
<TextInput
|
||||
testID="altTextImageInput"
|
||||
style={[styles.textArea, pal.border, pal.text]}
|
||||
keyboardAppearance={theme.colorScheme}
|
||||
multiline
|
||||
placeholder="Add alt text"
|
||||
placeholderTextColor={pal.colors.textLight}
|
||||
value={altText}
|
||||
onChangeText={text => setAltText(enforceLen(text, MAX_ALT_TEXT))}
|
||||
accessibilityLabel="Image alt text"
|
||||
accessibilityHint=""
|
||||
accessibilityLabelledBy="imageAltText"
|
||||
autoFocus
|
||||
/>
|
||||
<View style={styles.buttonControls}>
|
||||
<TouchableOpacity
|
||||
testID="altTextImageSaveBtn"
|
||||
onPress={onPressSave}
|
||||
accessibilityLabel="Save alt text"
|
||||
accessibilityHint={`Saves alt text, which reads: ${altText}`}
|
||||
accessibilityRole="button">
|
||||
<LinearGradient
|
||||
colors={[gradients.blueLight.start, gradients.blueLight.end]}
|
||||
start={{x: 0, y: 0}}
|
||||
end={{x: 1, y: 1}}
|
||||
style={[styles.button]}>
|
||||
<Text type="button-lg" style={[s.white, s.bold]}>
|
||||
Save
|
||||
</Text>
|
||||
</LinearGradient>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
testID="altTextImageCancelBtn"
|
||||
onPress={onPressCancel}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Cancel add image alt text"
|
||||
accessibilityHint=""
|
||||
onAccessibilityEscape={onPressCancel}>
|
||||
<View style={[styles.button]}>
|
||||
<Text type="button-lg" style={[pal.textLight]}>
|
||||
Cancel
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</KeyboardAvoidingView>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
gap: 18,
|
||||
paddingVertical: isDesktopWeb ? 0 : 18,
|
||||
paddingHorizontal: isDesktopWeb ? 0 : 12,
|
||||
flex: 1,
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
paddingVertical: isDesktopWeb ? 0 : 18,
|
||||
},
|
||||
title: {
|
||||
textAlign: 'center',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 24,
|
||||
scrollContainer: {
|
||||
flex: 1,
|
||||
height: '100%',
|
||||
paddingHorizontal: isDesktopWeb ? 0 : 12,
|
||||
},
|
||||
scrollInner: {
|
||||
gap: 12,
|
||||
},
|
||||
imageContainer: {
|
||||
borderRadius: 8,
|
||||
},
|
||||
textArea: {
|
||||
borderWidth: 1,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue