diff --git a/src/state/models/media/gallery.ts b/src/state/models/media/gallery.ts
index e53e861e2..1b22fadbd 100644
--- a/src/state/models/media/gallery.ts
+++ b/src/state/models/media/gallery.ts
@@ -23,6 +23,10 @@ export class GalleryModel {
return this.images.length
}
+ get needsAltText() {
+ return this.images.some(image => image.altText.trim() === '')
+ }
+
async add(image_: Omit<RNImage, 'size'>) {
if (this.size >= 4) {
return
diff --git a/src/state/models/ui/preferences.ts b/src/state/models/ui/preferences.ts
index 28c7c5666..858225a6f 100644
--- a/src/state/models/ui/preferences.ts
+++ b/src/state/models/ui/preferences.ts
@@ -53,6 +53,7 @@ export class PreferencesModel {
homeFeedRepliesThreshold: number = 2
homeFeedRepostsEnabled: boolean = true
homeFeedQuotePostsEnabled: boolean = true
+ requireAltTextEnabled: boolean = false
// used to linearize async modifications to state
lock = new AwaitLock()
@@ -72,6 +73,7 @@ export class PreferencesModel {
homeFeedRepliesThreshold: this.homeFeedRepliesThreshold,
homeFeedRepostsEnabled: this.homeFeedRepostsEnabled,
homeFeedQuotePostsEnabled: this.homeFeedQuotePostsEnabled,
+ requireAltTextEnabled: this.requireAltTextEnabled,
}
}
@@ -152,6 +154,13 @@ export class PreferencesModel {
) {
this.homeFeedQuotePostsEnabled = v.homeFeedQuotePostsEnabled
}
+ // check if requiring alt text is enabled in preferences, then hydrate
+ if (
+ hasProp(v, 'requireAltTextEnabled') &&
+ typeof v.requireAltTextEnabled === 'boolean'
+ ) {
+ this.requireAltTextEnabled = v.requireAltTextEnabled
+ }
}
}
@@ -467,4 +476,8 @@ export class PreferencesModel {
toggleHomeFeedQuotePostsEnabled() {
this.homeFeedQuotePostsEnabled = !this.homeFeedQuotePostsEnabled
}
+
+ toggleRequireAltTextEnabled() {
+ this.requireAltTextEnabled = !this.requireAltTextEnabled
+ }
}
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
index caece3476..c6a9ecd4a 100644
--- a/src/view/com/composer/Composer.tsx
+++ b/src/view/com/composer/Composer.tsx
@@ -156,6 +156,9 @@ export const ComposePost = observer(function ComposePost({
if (isProcessing || rt.graphemeLength > MAX_GRAPHEME_LENGTH) {
return
}
+ if (store.preferences.requireAltTextEnabled && gallery.needsAltText) {
+ return
+ }
setError('')
@@ -220,8 +223,14 @@ export const ComposePost = observer(function ComposePost({
)
const canPost = useMemo(
- () => graphemeLength <= MAX_GRAPHEME_LENGTH,
- [graphemeLength],
+ () =>
+ graphemeLength <= MAX_GRAPHEME_LENGTH &&
+ (!store.preferences.requireAltTextEnabled || !gallery.needsAltText),
+ [
+ graphemeLength,
+ store.preferences.requireAltTextEnabled,
+ gallery.needsAltText,
+ ],
)
const selectTextInputPlaceholder = replyTo ? 'Write your reply' : `What's up?`
@@ -282,6 +291,20 @@ export const ComposePost = observer(function ComposePost({
<Text style={pal.text}>{processingState}</Text>
</View>
) : undefined}
+ {store.preferences.requireAltTextEnabled && gallery.needsAltText && (
+ <View style={[styles.reminderLine, pal.viewLight]}>
+ <View style={styles.errorIcon}>
+ <FontAwesomeIcon
+ icon="exclamation"
+ style={{color: colors.red4}}
+ size={10}
+ />
+ </View>
+ <Text style={[pal.text, s.flex1]}>
+ One or more images is missing alt text.
+ </Text>
+ </View>
+ )}
{error !== '' && (
<View style={styles.errorLine}>
<View style={styles.errorIcon}>
@@ -415,6 +438,15 @@ const styles = StyleSheet.create({
paddingVertical: 6,
marginVertical: 6,
},
+ reminderLine: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ borderRadius: 6,
+ marginHorizontal: 15,
+ paddingHorizontal: 8,
+ paddingVertical: 6,
+ marginBottom: 6,
+ },
errorIcon: {
borderWidth: 1,
borderColor: colors.red4,
diff --git a/src/view/com/util/forms/ToggleButton.tsx b/src/view/com/util/forms/ToggleButton.tsx
index 47620d0a6..02be80b31 100644
--- a/src/view/com/util/forms/ToggleButton.tsx
+++ b/src/view/com/util/forms/ToggleButton.tsx
@@ -5,18 +5,21 @@ import {Button, ButtonType} from './Button'
import {useTheme} from 'lib/ThemeContext'
import {choose} from 'lib/functions'
import {colors} from 'lib/styles'
+import {TypographyVariant} from 'lib/ThemeContext'
export function ToggleButton({
type = 'default-light',
label,
isSelected,
style,
+ labelType,
onPress,
}: {
type?: ButtonType
label: string
isSelected: boolean
style?: StyleProp<ViewStyle>
+ labelType?: TypographyVariant
onPress?: () => void
}) {
const theme = useTheme()
@@ -143,7 +146,7 @@ export function ToggleButton({
/>
</View>
{label === '' ? null : (
- <Text type="button" style={[labelStyle, styles.label]}>
+ <Text type={labelType || 'button'} style={[labelStyle, styles.label]}>
{label}
</Text>
)}
diff --git a/src/view/screens/Settings.tsx b/src/view/screens/Settings.tsx
index e1fb549bc..0057841b2 100644
--- a/src/view/screens/Settings.tsx
+++ b/src/view/screens/Settings.tsx
@@ -330,6 +330,22 @@ export const SettingsScreen = withAuthRequired(
</TouchableOpacity>
<View style={styles.spacer20} />
+
+ <Text type="xl-bold" style={[pal.text, styles.heading]}>
+ Accessibility
+ </Text>
+ <View style={[pal.view, styles.toggleCard]}>
+ <ToggleButton
+ type="default-light"
+ label="Require alt text on images"
+ labelType="lg"
+ isSelected={store.preferences.requireAltTextEnabled}
+ onPress={store.preferences.toggleRequireAltTextEnabled}
+ />
+ </View>
+
+ <View style={styles.spacer20} />
+
<Text type="xl-bold" style={[pal.text, styles.heading]}>
Appearance
</Text>
@@ -633,6 +649,11 @@ const styles = StyleSheet.create({
paddingHorizontal: 18,
marginBottom: 1,
},
+ toggleCard: {
+ paddingVertical: 8,
+ paddingHorizontal: 6,
+ marginBottom: 1,
+ },
avi: {
marginRight: 12,
},
|