about summary refs log tree commit diff
path: root/src/view/com/composer/text-input/TextInput.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/composer/text-input/TextInput.tsx')
-rw-r--r--src/view/com/composer/text-input/TextInput.tsx330
1 files changed, 164 insertions, 166 deletions
diff --git a/src/view/com/composer/text-input/TextInput.tsx b/src/view/com/composer/text-input/TextInput.tsx
index 32fdb4aa2..c5d094ea5 100644
--- a/src/view/com/composer/text-input/TextInput.tsx
+++ b/src/view/com/composer/text-input/TextInput.tsx
@@ -51,181 +51,179 @@ interface Selection {
   end: number
 }
 
-export const TextInput = forwardRef(
-  (
-    {
-      richtext,
-      placeholder,
-      suggestedLinks,
-      autocompleteView,
-      setRichText,
-      onPhotoPasted,
-      onSuggestedLinksChanged,
-      onError,
-      ...props
-    }: TextInputProps,
-    ref,
-  ) => {
-    const pal = usePalette('default')
-    const textInput = useRef<PasteInputRef>(null)
-    const textInputSelection = useRef<Selection>({start: 0, end: 0})
-    const theme = useTheme()
-
-    React.useImperativeHandle(ref, () => ({
-      focus: () => textInput.current?.focus(),
-      blur: () => {
-        textInput.current?.blur()
-      },
-    }))
-
-    const onChangeText = useCallback(
-      (newText: string) => {
-        /*
-         * This is a hack to bump the rendering of our styled
-         * `textDecorated` to _after_ whatever processing is happening
-         * within the `PasteInput` library. Without this, the elements in
-         * `textDecorated` are not correctly painted to screen.
-         *
-         * NB: we tried a `0` timeout as well, but only positive values worked.
-         *
-         * @see https://github.com/bluesky-social/social-app/issues/929
-         */
-        setTimeout(async () => {
-          const newRt = new RichText({text: newText})
-          newRt.detectFacetsWithoutResolution()
-          setRichText(newRt)
-
-          const prefix = getMentionAt(
-            newText,
-            textInputSelection.current?.start || 0,
-          )
-          if (prefix) {
-            autocompleteView.setActive(true)
-            autocompleteView.setPrefix(prefix.value)
-          } else {
-            autocompleteView.setActive(false)
-          }
+export const TextInput = forwardRef(function TextInputImpl(
+  {
+    richtext,
+    placeholder,
+    suggestedLinks,
+    autocompleteView,
+    setRichText,
+    onPhotoPasted,
+    onSuggestedLinksChanged,
+    onError,
+    ...props
+  }: TextInputProps,
+  ref,
+) {
+  const pal = usePalette('default')
+  const textInput = useRef<PasteInputRef>(null)
+  const textInputSelection = useRef<Selection>({start: 0, end: 0})
+  const theme = useTheme()
+
+  React.useImperativeHandle(ref, () => ({
+    focus: () => textInput.current?.focus(),
+    blur: () => {
+      textInput.current?.blur()
+    },
+  }))
+
+  const onChangeText = useCallback(
+    (newText: string) => {
+      /*
+       * This is a hack to bump the rendering of our styled
+       * `textDecorated` to _after_ whatever processing is happening
+       * within the `PasteInput` library. Without this, the elements in
+       * `textDecorated` are not correctly painted to screen.
+       *
+       * NB: we tried a `0` timeout as well, but only positive values worked.
+       *
+       * @see https://github.com/bluesky-social/social-app/issues/929
+       */
+      setTimeout(async () => {
+        const newRt = new RichText({text: newText})
+        newRt.detectFacetsWithoutResolution()
+        setRichText(newRt)
+
+        const prefix = getMentionAt(
+          newText,
+          textInputSelection.current?.start || 0,
+        )
+        if (prefix) {
+          autocompleteView.setActive(true)
+          autocompleteView.setPrefix(prefix.value)
+        } else {
+          autocompleteView.setActive(false)
+        }
 
-          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)) {
-                  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)
+        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)) {
+                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)
-          }
-        }, 1)
-      },
-      [
-        setRichText,
-        autocompleteView,
-        suggestedLinks,
-        onSuggestedLinksChanged,
-        onPhotoPasted,
-      ],
-    )
-
-    const onPaste = useCallback(
-      async (err: string | undefined, files: PastedFile[]) => {
-        if (err) {
-          return onError(cleanError(err))
         }
 
-        const uris = files.map(f => f.uri)
-        const uri = uris.find(isUriImage)
-
-        if (uri) {
-          onPhotoPasted(uri)
+        if (!isEqual(set, suggestedLinks)) {
+          onSuggestedLinksChanged(set)
         }
-      },
-      [onError, onPhotoPasted],
-    )
-
-    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
-      },
-      [textInputSelection],
-    )
-
-    const onSelectAutocompleteItem = useCallback(
-      (item: string) => {
-        onChangeText(
-          insertMentionAt(
-            richtext.text,
-            textInputSelection.current?.start || 0,
-            item,
-          ),
-        )
-        autocompleteView.setActive(false)
-      },
-      [onChangeText, richtext, autocompleteView],
-    )
-
-    const textDecorated = useMemo(() => {
-      let i = 0
-
-      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 (
-      <View style={styles.container}>
-        <PasteInput
-          testID="composerTextInput"
-          ref={textInput}
-          onChangeText={onChangeText}
-          onPaste={onPaste}
-          onSelectionChange={onSelectionChange}
-          placeholder={placeholder}
-          placeholderTextColor={pal.colors.textLight}
-          keyboardAppearance={theme.colorScheme}
-          autoFocus={true}
-          allowFontScaling
-          multiline
-          style={[pal.text, styles.textInput, styles.textInputFormatting]}
-          {...props}>
-          {textDecorated}
-        </PasteInput>
-        <Autocomplete
-          view={autocompleteView}
-          onSelect={onSelectAutocompleteItem}
-        />
-      </View>
-    )
-  },
-)
+      }, 1)
+    },
+    [
+      setRichText,
+      autocompleteView,
+      suggestedLinks,
+      onSuggestedLinksChanged,
+      onPhotoPasted,
+    ],
+  )
+
+  const onPaste = useCallback(
+    async (err: string | undefined, files: PastedFile[]) => {
+      if (err) {
+        return onError(cleanError(err))
+      }
+
+      const uris = files.map(f => f.uri)
+      const uri = uris.find(isUriImage)
+
+      if (uri) {
+        onPhotoPasted(uri)
+      }
+    },
+    [onError, onPhotoPasted],
+  )
+
+  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
+    },
+    [textInputSelection],
+  )
+
+  const onSelectAutocompleteItem = useCallback(
+    (item: string) => {
+      onChangeText(
+        insertMentionAt(
+          richtext.text,
+          textInputSelection.current?.start || 0,
+          item,
+        ),
+      )
+      autocompleteView.setActive(false)
+    },
+    [onChangeText, richtext, autocompleteView],
+  )
+
+  const textDecorated = useMemo(() => {
+    let i = 0
+
+    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 (
+    <View style={styles.container}>
+      <PasteInput
+        testID="composerTextInput"
+        ref={textInput}
+        onChangeText={onChangeText}
+        onPaste={onPaste}
+        onSelectionChange={onSelectionChange}
+        placeholder={placeholder}
+        placeholderTextColor={pal.colors.textLight}
+        keyboardAppearance={theme.colorScheme}
+        autoFocus={true}
+        allowFontScaling
+        multiline
+        style={[pal.text, styles.textInput, styles.textInputFormatting]}
+        {...props}>
+        {textDecorated}
+      </PasteInput>
+      <Autocomplete
+        view={autocompleteView}
+        onSelect={onSelectAutocompleteItem}
+      />
+    </View>
+  )
+})
 
 const styles = StyleSheet.create({
   container: {