From 2509290fdd2b20c76c302d4962216f5d2d2b5a73 Mon Sep 17 00:00:00 2001
From: Ollie Hsieh <renahlee@outlook.com>
Date: Mon, 17 Apr 2023 15:41:44 -0700
Subject: Split image cropping into secondary step (#473)

* Split image cropping into secondary step

* Use ImageModel and GalleryModel

* Add fix for pasting image URLs

* Move models to state folder

* Fix things that broke after rebase

* Latest -- has image display bug

* Remove contentFit

* Fix iOS display in gallery

* Tuneup the api signatures and implement compress/resize on web

* Fix await

* Lint fix and remove unused function

* Fix android image pathing

* Fix external embed x button on android

* Remove min-height from composer (no longer useful and was mispositioning the composer on android)

* Fix e2e picker

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
---
 src/view/com/composer/text-input/TextInput.tsx | 111 +++++++++++++------------
 1 file changed, 56 insertions(+), 55 deletions(-)

(limited to 'src/view/com/composer/text-input/TextInput.tsx')

diff --git a/src/view/com/composer/text-input/TextInput.tsx b/src/view/com/composer/text-input/TextInput.tsx
index bd536e1c3..9c111bd38 100644
--- a/src/view/com/composer/text-input/TextInput.tsx
+++ b/src/view/com/composer/text-input/TextInput.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, {forwardRef, useCallback, useEffect, useRef, useMemo} from 'react'
 import {
   NativeSyntheticEvent,
   StyleSheet,
@@ -14,18 +14,13 @@ import isEqual from 'lodash.isequal'
 import {UserAutocompleteModel} from 'state/models/discovery/user-autocomplete'
 import {Autocomplete} from './mobile/Autocomplete'
 import {Text} from 'view/com/util/text/Text'
-import {useStores} from 'state/index'
 import {cleanError} from 'lib/strings/errors'
-import {getImageDim} from 'lib/media/manip'
-import {cropAndCompressFlow} from 'lib/media/picker'
 import {getMentionAt, insertMentionAt} from 'lib/strings/mention-manip'
-import {
-  POST_IMG_MAX_WIDTH,
-  POST_IMG_MAX_HEIGHT,
-  POST_IMG_MAX_SIZE,
-} from 'lib/constants'
 import {usePalette} from 'lib/hooks/usePalette'
 import {useTheme} from 'lib/ThemeContext'
+import {isUriImage} from 'lib/media/util'
+import {downloadAndResize} from 'lib/media/manip'
+import {POST_IMG_MAX} from 'lib/constants'
 
 export interface TextInputRef {
   focus: () => void
@@ -48,7 +43,7 @@ interface Selection {
   end: number
 }
 
-export const TextInput = React.forwardRef(
+export const TextInput = forwardRef(
   (
     {
       richtext,
@@ -63,9 +58,8 @@ export const TextInput = React.forwardRef(
     ref,
   ) => {
     const pal = usePalette('default')
-    const store = useStores()
-    const textInput = React.useRef<PasteInputRef>(null)
-    const textInputSelection = React.useRef<Selection>({start: 0, end: 0})
+    const textInput = useRef<PasteInputRef>(null)
+    const textInputSelection = useRef<Selection>({start: 0, end: 0})
     const theme = useTheme()
 
     React.useImperativeHandle(ref, () => ({
@@ -73,7 +67,7 @@ export const TextInput = React.forwardRef(
       blur: () => textInput.current?.blur(),
     }))
 
-    React.useEffect(() => {
+    useEffect(() => {
       // HACK
       // wait a moment before focusing the input to resolve some layout bugs with the keyboard-avoiding-view
       // -prf
@@ -90,8 +84,8 @@ export const TextInput = React.forwardRef(
       }
     }, [])
 
-    const onChangeText = React.useCallback(
-      (newText: string) => {
+    const onChangeText = useCallback(
+      async (newText: string) => {
         const newRt = new RichText({text: newText})
         newRt.detectFacetsWithoutResolution()
         setRichText(newRt)
@@ -108,50 +102,62 @@ export const TextInput = React.forwardRef(
         }
 
         const set: Set<string> = new Set()
+
         if (newRt.facets) {
           for (const facet of newRt.facets) {
             for (const feature of facet.features) {
               if (AppBskyRichtextFacet.isLink(feature)) {
-                set.add(feature.uri)
+                if (isUriImage(feature.uri)) {
+                  const res = await downloadAndResize({
+                    uri: feature.uri,
+                    width: POST_IMG_MAX.width,
+                    height: POST_IMG_MAX.height,
+                    mode: 'contain',
+                    maxSize: POST_IMG_MAX.size,
+                    timeout: 15e3,
+                  })
+
+                  if (res !== undefined) {
+                    onPhotoPasted(res.path)
+                  }
+                } else {
+                  set.add(feature.uri)
+                }
               }
             }
           }
         }
+
         if (!isEqual(set, suggestedLinks)) {
           onSuggestedLinksChanged(set)
         }
       },
-      [setRichText, autocompleteView, suggestedLinks, onSuggestedLinksChanged],
+      [
+        setRichText,
+        autocompleteView,
+        suggestedLinks,
+        onSuggestedLinksChanged,
+        onPhotoPasted,
+      ],
     )
 
-    const onPaste = React.useCallback(
+    const onPaste = useCallback(
       async (err: string | undefined, files: PastedFile[]) => {
         if (err) {
           return onError(cleanError(err))
         }
+
         const uris = files.map(f => f.uri)
-        const imgUri = uris.find(uri => /\.(jpe?g|png)$/.test(uri))
-        if (imgUri) {
-          let imgDim
-          try {
-            imgDim = await getImageDim(imgUri)
-          } catch (e) {
-            imgDim = {width: POST_IMG_MAX_WIDTH, height: POST_IMG_MAX_HEIGHT}
-          }
-          const finalImgPath = await cropAndCompressFlow(
-            store,
-            imgUri,
-            imgDim,
-            {width: POST_IMG_MAX_WIDTH, height: POST_IMG_MAX_HEIGHT},
-            POST_IMG_MAX_SIZE,
-          )
-          onPhotoPasted(finalImgPath)
+        const uri = uris.find(isUriImage)
+
+        if (uri) {
+          onPhotoPasted(uri)
         }
       },
-      [store, onError, onPhotoPasted],
+      [onError, onPhotoPasted],
     )
 
-    const onSelectionChange = React.useCallback(
+    const onSelectionChange = useCallback(
       (evt: NativeSyntheticEvent<TextInputSelectionChangeEventData>) => {
         // NOTE we track the input selection using a ref to avoid excessive renders -prf
         textInputSelection.current = evt.nativeEvent.selection
@@ -159,7 +165,7 @@ export const TextInput = React.forwardRef(
       [textInputSelection],
     )
 
-    const onSelectAutocompleteItem = React.useCallback(
+    const onSelectAutocompleteItem = useCallback(
       (item: string) => {
         onChangeText(
           insertMentionAt(
@@ -173,23 +179,19 @@ export const TextInput = React.forwardRef(
       [onChangeText, richtext, autocompleteView],
     )
 
-    const textDecorated = React.useMemo(() => {
+    const textDecorated = useMemo(() => {
       let i = 0
-      return Array.from(richtext.segments()).map(segment => {
-        if (!segment.facet) {
-          return (
-            <Text key={i++} style={[pal.text, styles.textInputFormatting]}>
-              {segment.text}
-            </Text>
-          )
-        } else {
-          return (
-            <Text key={i++} style={[pal.link, styles.textInputFormatting]}>
-              {segment.text}
-            </Text>
-          )
-        }
-      })
+
+      return Array.from(richtext.segments()).map(segment => (
+        <Text
+          key={i++}
+          style={[
+            !segment.facet ? pal.text : pal.link,
+            styles.textInputFormatting,
+          ]}>
+          {segment.text}
+        </Text>
+      ))
     }, [richtext, pal.link, pal.text])
 
     return (
@@ -223,7 +225,6 @@ const styles = StyleSheet.create({
   textInput: {
     flex: 1,
     width: '100%',
-    minHeight: 80,
     padding: 5,
     paddingBottom: 20,
     marginLeft: 8,
-- 
cgit 1.4.1

