From bfaa6d73f37f251259c521befa9e9ee8ea877560 Mon Sep 17 00:00:00 2001
From: Paul Frazee <pfrazee@gmail.com>
Date: Tue, 27 Jun 2023 09:52:49 -0500
Subject: 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>
---
 src/view/com/modals/AltImage.tsx | 167 ++++++++++++++++++++++++++-------------
 1 file changed, 110 insertions(+), 57 deletions(-)

(limited to 'src/view/com/modals/AltImage.tsx')

diff --git a/src/view/com/modals/AltImage.tsx b/src/view/com/modals/AltImage.tsx
index 07270d557..e1145a0fe 100644
--- a/src/view/com/modals/AltImage.tsx
+++ b/src/view/com/modals/AltImage.tsx
@@ -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>
+          <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>
-        </TouchableOpacity>
-      </View>
-    </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,
+  },
+  scrollContainer: {
+    flex: 1,
+    height: '100%',
+    paddingHorizontal: isDesktopWeb ? 0 : 12,
+  },
+  scrollInner: {
+    gap: 12,
   },
-  title: {
-    textAlign: 'center',
-    fontWeight: 'bold',
-    fontSize: 24,
+  imageContainer: {
+    borderRadius: 8,
   },
   textArea: {
     borderWidth: 1,
-- 
cgit 1.4.1

