Rework alt-text input to use bottom sheet (#2355)
* Rework alt-text input to use bottom sheet * Add translationszio/stable
parent
0842df3675
commit
d95972c9ff
|
@ -1,14 +1,12 @@
|
||||||
import React, {useMemo, useCallback, useState} from 'react'
|
import React, {useMemo, useCallback, useState} from 'react'
|
||||||
import {
|
import {
|
||||||
ImageStyle,
|
ImageStyle,
|
||||||
KeyboardAvoidingView,
|
|
||||||
ScrollView,
|
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
TextInput,
|
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
useWindowDimensions,
|
useWindowDimensions,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
|
import {ScrollView, TextInput} from './util'
|
||||||
import {Image} from 'expo-image'
|
import {Image} from 'expo-image'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {gradients, s} from 'lib/styles'
|
import {gradients, s} from 'lib/styles'
|
||||||
|
@ -17,13 +15,13 @@ import {MAX_ALT_TEXT} from 'lib/constants'
|
||||||
import {useTheme} from 'lib/ThemeContext'
|
import {useTheme} from 'lib/ThemeContext'
|
||||||
import {Text} from '../util/text/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import LinearGradient from 'react-native-linear-gradient'
|
import LinearGradient from 'react-native-linear-gradient'
|
||||||
import {isAndroid, isWeb} from 'platform/detection'
|
import {isWeb} from 'platform/detection'
|
||||||
import {ImageModel} from 'state/models/media/image'
|
import {ImageModel} from 'state/models/media/image'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {Trans, msg} from '@lingui/macro'
|
import {Trans, msg} from '@lingui/macro'
|
||||||
import {useModalControls} from '#/state/modals'
|
import {useModalControls} from '#/state/modals'
|
||||||
|
|
||||||
export const snapPoints = ['fullscreen']
|
export const snapPoints = ['100%']
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
image: ImageModel
|
image: ImageModel
|
||||||
|
@ -54,102 +52,86 @@ export function Component({image}: Props) {
|
||||||
}
|
}
|
||||||
}, [image, windim])
|
}, [image, windim])
|
||||||
|
|
||||||
|
const onUpdate = useCallback(
|
||||||
|
(v: string) => {
|
||||||
|
v = enforceLen(v, MAX_ALT_TEXT)
|
||||||
|
setAltText(v)
|
||||||
|
image.setAltText(v)
|
||||||
|
},
|
||||||
|
[setAltText, image],
|
||||||
|
)
|
||||||
|
|
||||||
const onPressSave = useCallback(() => {
|
const onPressSave = useCallback(() => {
|
||||||
image.setAltText(altText)
|
image.setAltText(altText)
|
||||||
closeModal()
|
closeModal()
|
||||||
}, [closeModal, image, altText])
|
}, [closeModal, image, altText])
|
||||||
|
|
||||||
const onPressCancel = () => {
|
|
||||||
closeModal()
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyboardAvoidingView
|
<ScrollView
|
||||||
behavior={isAndroid ? 'height' : 'padding'}
|
testID="altTextImageModal"
|
||||||
style={[pal.view, styles.container]}>
|
style={[pal.view, styles.scrollContainer]}
|
||||||
<ScrollView
|
keyboardShouldPersistTaps="always"
|
||||||
testID="altTextImageModal"
|
nativeID="imageAltText">
|
||||||
style={styles.scrollContainer}
|
<View style={styles.scrollInner}>
|
||||||
keyboardShouldPersistTaps="always"
|
<View style={[pal.viewLight, styles.imageContainer]}>
|
||||||
nativeID="imageAltText">
|
<Image
|
||||||
<View style={styles.scrollInner}>
|
testID="selectedPhotoImage"
|
||||||
<View style={[pal.viewLight, styles.imageContainer]}>
|
style={imageStyles}
|
||||||
<Image
|
source={{
|
||||||
testID="selectedPhotoImage"
|
uri: image.cropped?.path ?? image.path,
|
||||||
style={imageStyles}
|
}}
|
||||||
source={{
|
contentFit="contain"
|
||||||
uri: image.cropped?.path ?? image.path,
|
accessible={true}
|
||||||
}}
|
accessibilityIgnoresInvertColors
|
||||||
contentFit="contain"
|
|
||||||
accessible={true}
|
|
||||||
accessibilityIgnoresInvertColors
|
|
||||||
/>
|
|
||||||
</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={_(msg`Image alt text`)}
|
|
||||||
accessibilityHint=""
|
|
||||||
accessibilityLabelledBy="imageAltText"
|
|
||||||
autoFocus
|
|
||||||
/>
|
/>
|
||||||
<View style={styles.buttonControls}>
|
|
||||||
<TouchableOpacity
|
|
||||||
testID="altTextImageSaveBtn"
|
|
||||||
onPress={onPressSave}
|
|
||||||
accessibilityLabel={_(msg`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]}>
|
|
||||||
<Trans>Save</Trans>
|
|
||||||
</Text>
|
|
||||||
</LinearGradient>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity
|
|
||||||
testID="altTextImageCancelBtn"
|
|
||||||
onPress={onPressCancel}
|
|
||||||
accessibilityRole="button"
|
|
||||||
accessibilityLabel={_(msg`Cancel add image alt text`)}
|
|
||||||
accessibilityHint=""
|
|
||||||
onAccessibilityEscape={onPressCancel}>
|
|
||||||
<View style={[styles.button]}>
|
|
||||||
<Text type="button-lg" style={[pal.textLight]}>
|
|
||||||
<Trans>Cancel</Trans>
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
<TextInput
|
||||||
</KeyboardAvoidingView>
|
testID="altTextImageInput"
|
||||||
|
style={[styles.textArea, pal.border, pal.text]}
|
||||||
|
keyboardAppearance={theme.colorScheme}
|
||||||
|
multiline
|
||||||
|
placeholder={_(msg`Add alt text`)}
|
||||||
|
placeholderTextColor={pal.colors.textLight}
|
||||||
|
value={altText}
|
||||||
|
onChangeText={onUpdate}
|
||||||
|
accessibilityLabel={_(msg`Image alt text`)}
|
||||||
|
accessibilityHint=""
|
||||||
|
accessibilityLabelledBy="imageAltText"
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
<View style={styles.buttonControls}>
|
||||||
|
<TouchableOpacity
|
||||||
|
testID="altTextImageSaveBtn"
|
||||||
|
onPress={onPressSave}
|
||||||
|
accessibilityLabel={_(msg`Save alt text`)}
|
||||||
|
accessibilityHint=""
|
||||||
|
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]}>
|
||||||
|
<Trans>Done</Trans>
|
||||||
|
</Text>
|
||||||
|
</LinearGradient>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
|
||||||
flex: 1,
|
|
||||||
height: '100%',
|
|
||||||
width: '100%',
|
|
||||||
paddingVertical: isWeb ? 0 : 18,
|
|
||||||
},
|
|
||||||
scrollContainer: {
|
scrollContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
height: '100%',
|
height: '100%',
|
||||||
paddingHorizontal: isWeb ? 0 : 12,
|
paddingHorizontal: isWeb ? 0 : 12,
|
||||||
|
paddingVertical: isWeb ? 0 : 24,
|
||||||
},
|
},
|
||||||
scrollInner: {
|
scrollInner: {
|
||||||
gap: 12,
|
gap: 12,
|
||||||
|
paddingTop: isWeb ? 0 : 12,
|
||||||
},
|
},
|
||||||
imageContainer: {
|
imageContainer: {
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
|
@ -173,5 +155,6 @@ const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
buttonControls: {
|
buttonControls: {
|
||||||
gap: 8,
|
gap: 8,
|
||||||
|
paddingBottom: isWeb ? 0 : 50,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue