Skip to content

Commit e43a4ec

Browse files
OtavioStasiakdiegolmelloRohit3523
authored
feat(a11y): alt text (#7163)
* feat: alt text * action: organized translations * fix: move alt text accessibility to button level and fix gallery height * chore: format code and fix lint issues * ix: stable gallery keys and forward alt text accessibility to gallery items * fix: i18n fallbacks, alt text a11y label, stale altText on remove, and 8.4.0 boundary test * action: organized translations * fix: i18n * action: organized translations * fix: preserve caption on send, correct version gate for share extension, and translated a11y labels in gallery * fix: alt text label * feat: queue composer attachments inline * feat: open attachment alt text in action sheet * fix: remove image gallery * action: organized translations * feat: edit message alt text * chore: format code and fix lint issues * feat: backward compatibilities * fix: edit * remove image gallery * fix: conflicts * code improvements * fix: composer * code improvements * fix: tests * fix(a11y): improve image attachment accessibility labels and order * action: organized translations * fix: unit tests * fix: focus * fix: a11y labels * chore: format code and fix lint issues * test * fix: merge conflicts * feat: improve a11y experience * fix: unlabelled on android * fix: action sheet input keyboard * fix: announce GIFs as interactive in ImageViewer for screen readers * fix: ignore whitespace-only alt text in message a11y label * refactor: extract sendAttachments helper from composer and share view * action: organized translations * feat: code improvements * code improvements * refactor: extract useImageDescriptionLabel hook and inline useAltTextSupported in Image * fix: i18n * action: organized translations * refactor: stabilize FlatList renderers and harden altText checks * fix(a11y): omit empty segments in message accessibilityLabel * refactor: extract normalizeAttachment and preserve handler order in useChooseMedia * refactor: extract normalizeAttachment and preserve handler order in useChooseMedia * rollback prettier changes * feat: attachment action sheet stories and test * feat: attachment actionsheet stories and tests * feat: announce images without description to screen readers * action: organized translations * fix: render Attachment file name in Thread Message preview when body is empty (#7323) * fix: preference value changes causing reset to other option (#7313) * fix: do not encrypt messages when workspace E2E is disabled (#7324) * fix: snapshot test * fix: snapshot test * feat: unify thumbs * efactor: rename handlePickedAttachments to handleSelectedAttachments * fix: pass altText and isAnimated to ImageViewer in ShareView Preview * refactor: make AltTextLabel altText optional with early return * fix: stop leaking attachment.altText into caption rendering * fix: if no alt text its rendering an empty absolute view * test: cover useMessageAccessibilityLabel and keep suffix on translated * refactor: prefer title_link/message_link over index for Reply attachment key * refactor: unify ShareView Thumbs and ComposerAttachments under shared AttachmentThumbs * feat: unify Thumbs * remove memo of useImageDescriptionLabel * fix: test and lint * fix: test * fix: i18n missing translation * fix: fallback accessibility label when alt text is empty * fix: remove unused code * rollback prettier changes * refactor: colocate useImageDescriptionLabel with message hooks * refactor: simplify message accessibility label composition * feat: add missing keys * feat: use thumb as children instead of add it again * refactor: tighten types and stable keys in message/share views * fix: useTheme on AltTextInput * action: organized translations * chore: code organization * chore: type improvement * chore: code organization * fix: image improvement * code improvements * fix: avoid undefined a11y label * fix: altText trim and improvements * fix: tests * fix: use memo composer attachments * chore: remove editAltText (not available) * feat: standardize shareView composer --------- Co-authored-by: OtavioStasiak <OtavioStasiak@users.noreply.github.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> Co-authored-by: Rohit Bansal <40559587+Rohit3523@users.noreply.github.com>
1 parent 66571ed commit e43a4ec

72 files changed

Lines changed: 3909 additions & 766 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/containers/AltTextLabel.tsx

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import React from 'react';
2+
import { Pressable, StyleSheet, Text, View } from 'react-native';
3+
4+
import { useActionSheet } from './ActionSheet';
5+
import I18n from '../i18n';
6+
import sharedStyles from '../views/Styles';
7+
import { useTheme } from '../theme';
8+
9+
const styles = StyleSheet.create({
10+
container: {
11+
position: 'absolute',
12+
bottom: 12,
13+
right: 12,
14+
alignSelf: 'flex-start',
15+
paddingHorizontal: 4,
16+
borderRadius: 4,
17+
height: 20,
18+
justifyContent: 'center'
19+
},
20+
label: {
21+
fontSize: 14,
22+
lineHeight: 20,
23+
...sharedStyles.textBold
24+
},
25+
altTextContent: {
26+
...sharedStyles.containerScrollView
27+
},
28+
altTextTitle: {
29+
fontSize: 16,
30+
lineHeight: 24,
31+
marginBottom: 12,
32+
...sharedStyles.textSemibold
33+
},
34+
altTextBody: {
35+
fontSize: 16,
36+
lineHeight: 24,
37+
...sharedStyles.textRegular
38+
}
39+
});
40+
41+
const AltTextActionSheetContent = ({ altText }: { altText: string }) => {
42+
'use memo';
43+
44+
const { colors } = useTheme();
45+
46+
return (
47+
<View style={styles.altTextContent}>
48+
<Text style={[styles.altTextTitle, { color: colors.fontTitlesLabels }]}>{I18n.t('Alt_text')}</Text>
49+
<Text style={[styles.altTextBody, { color: colors.fontDefault }]}>{altText}</Text>
50+
</View>
51+
);
52+
};
53+
54+
type TAltTextLabelProps = {
55+
altText?: string;
56+
testID?: string;
57+
};
58+
59+
const AltTextLabel = ({ altText, testID }: TAltTextLabelProps) => {
60+
'use memo';
61+
62+
const { colors } = useTheme();
63+
const { showActionSheet } = useActionSheet();
64+
65+
if (!altText) {
66+
return null;
67+
}
68+
69+
const handleOpenAltText = () => {
70+
showActionSheet({
71+
children: <AltTextActionSheetContent altText={altText} />
72+
});
73+
};
74+
75+
return (
76+
<Pressable
77+
accessible
78+
testID={testID}
79+
onPress={handleOpenAltText}
80+
accessibilityRole='button'
81+
accessibilityLabel={I18n.t('Alt_text')}
82+
style={[styles.container, { backgroundColor: colors.surfaceNeutral }]}>
83+
<Text style={[styles.label, { color: colors.fontTitlesLabels }]}>{I18n.t('Alt')}</Text>
84+
</Pressable>
85+
);
86+
};
87+
88+
AltTextLabel.displayName = 'AltTextLabel';
89+
90+
export default AltTextLabel;

app/containers/ImageViewer/ImageViewer.tsx

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Touch from '../Touch';
88
import { useUserPreferences } from '../../lib/methods/userPreferences';
99
import { AUTOPLAY_GIFS_PREFERENCES_KEY } from '../../lib/constants/keys';
1010
import { useTheme } from '../../theme';
11+
import I18n from '../../i18n';
1112

1213
interface ImageViewerProps {
1314
style?: StyleProp<ImageStyle>;
@@ -18,6 +19,8 @@ interface ImageViewerProps {
1819
width: number;
1920
height: number;
2021
onLoadEnd?: () => void;
22+
altText?: string;
23+
isAnimated?: boolean;
2124
}
2225

2326
const styles = StyleSheet.create({
@@ -34,7 +37,7 @@ const styles = StyleSheet.create({
3437
}
3538
});
3639

37-
export const ImageViewer = ({ uri = '', width, height, ...props }: ImageViewerProps): React.ReactElement => {
40+
export const ImageViewer = ({ uri = '', width, height, altText, isAnimated, ...props }: ImageViewerProps): React.ReactElement => {
3841
const [autoplayGifs] = useUserPreferences<boolean>(AUTOPLAY_GIFS_PREFERENCES_KEY, true);
3942
const [isPlaying, setIsPlaying] = useState<boolean>(!!autoplayGifs);
4043
const expoImageRef = useRef<Image>(null);
@@ -131,13 +134,42 @@ export const ImageViewer = ({ uri = '', width, height, ...props }: ImageViewerPr
131134

132135
const { colors } = useTheme();
133136

137+
const accessibilityLabel = altText?.trim() || I18n.t('A11y_image_no_description');
138+
134139
return (
135-
<View style={[styles.container, { width, height, backgroundColor: colors.surfaceNeutral }]}>
140+
<View importantForAccessibility='no' style={[styles.container, { width, height, backgroundColor: colors.surfaceNeutral }]}>
136141
<GestureDetector gesture={gesture}>
137-
<Animated.View onLayout={onLayout} style={[styles.flex, style]}>
138-
<Touch onPress={handleGifPlayback} style={styles.flex} rectButtonStyle={styles.flex}>
139-
<Image style={styles.image} contentFit='contain' source={{ uri }} ref={expoImageRef} {...props} />
140-
</Touch>
142+
<Animated.View accessible={false} onLayout={onLayout} style={[styles.flex, style]}>
143+
{isAnimated ? (
144+
<Touch
145+
accessible
146+
accessibilityLabel={accessibilityLabel}
147+
accessibilityRole='button'
148+
accessibilityHint={I18n.t('A11y_image_viewer_gif_hint')}
149+
onPress={handleGifPlayback}
150+
style={styles.flex}
151+
rectButtonStyle={styles.flex}>
152+
<Image
153+
accessible={false}
154+
style={styles.image}
155+
contentFit='contain'
156+
source={{ uri }}
157+
ref={expoImageRef}
158+
{...props}
159+
/>
160+
</Touch>
161+
) : (
162+
<Image
163+
accessible
164+
accessibilityLabel={accessibilityLabel}
165+
accessibilityRole='image'
166+
style={styles.image}
167+
contentFit='contain'
168+
source={{ uri }}
169+
ref={expoImageRef}
170+
{...props}
171+
/>
172+
)}
141173
</Animated.View>
142174
</GestureDetector>
143175
</View>

0 commit comments

Comments
 (0)