From fbf2970bae1030849a39410f026e030d2bf39786 Mon Sep 17 00:00:00 2001
From: Mary <pineapplecreamcheese@skiff.com>
Date: Sat, 20 Jan 2024 10:39:28 +0700
Subject: feat: web composer image drag and drop

---
 src/view/com/composer/text-input/TextInput.web.tsx | 63 ++++++++++++++++++----
 1 file changed, 54 insertions(+), 9 deletions(-)

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

diff --git a/src/view/com/composer/text-input/TextInput.web.tsx b/src/view/com/composer/text-input/TextInput.web.tsx
index ec3a042a3..351ca1d0c 100644
--- a/src/view/com/composer/text-input/TextInput.web.tsx
+++ b/src/view/com/composer/text-input/TextInput.web.tsx
@@ -18,6 +18,7 @@ import {Emoji} from './web/EmojiPicker.web'
 import {LinkDecorator} from './web/LinkDecorator'
 import {generateJSON} from '@tiptap/html'
 import {useActorAutocompleteFn} from '#/state/queries/actor-autocomplete'
+import {usePalette} from '#/lib/hooks/usePalette'
 
 export interface TextInputRef {
   focus: () => void
@@ -53,7 +54,11 @@ export const TextInput = React.forwardRef(function TextInputImpl(
 ) {
   const autocomplete = useActorAutocompleteFn()
 
+  const pal = usePalette('default')
   const modeClass = useColorSchemeStyle('ProseMirror-light', 'ProseMirror-dark')
+
+  const [isDropping, setIsDropping] = React.useState(false)
+
   const extensions = React.useMemo(
     () => [
       Document,
@@ -112,6 +117,32 @@ export const TextInput = React.forwardRef(function TextInputImpl(
             return true
           }
         },
+        handleDOMEvents: {
+          dragover: (_, event) => {
+            const transfer = event.dataTransfer
+            if (transfer && transfer.types.includes('Files')) {
+              setIsDropping(true)
+            }
+          },
+          dragleave: (_, _event) => {
+            setIsDropping(false)
+          },
+          drop: (_, event) => {
+            const transfer = event.dataTransfer
+            if (transfer) {
+              const items = transfer.items
+
+              if (items.length > 0) {
+                event.preventDefault()
+                getImageFromUri(items, (uri: string) => {
+                  textInputWebEmitter.emit('photo-pasted', uri)
+                })
+              }
+            }
+
+            setIsDropping(false)
+          },
+        },
       },
       content: generateJSON(richtext.text.toString(), extensions),
       autofocus: 'end',
@@ -179,6 +210,10 @@ export const TextInput = React.forwardRef(function TextInputImpl(
   return (
     <View style={styles.container}>
       <EditorContent editor={editor} />
+
+      {isDropping && (
+        <View style={[{borderColor: pal.colors.link}, styles.dropContainer]} />
+      )}
     </View>
   )
 })
@@ -210,6 +245,17 @@ const styles = StyleSheet.create({
     marginLeft: 8,
     marginBottom: 10,
   },
+  dropContainer: {
+    pointerEvents: 'none',
+    position: 'absolute',
+    borderWidth: 4,
+    borderRadius: 8,
+    borderStyle: 'dashed',
+    top: 0,
+    bottom: 0,
+    left: 0,
+    right: 0,
+  },
 })
 
 function getImageFromUri(
@@ -218,25 +264,24 @@ function getImageFromUri(
 ) {
   for (let index = 0; index < items.length; index++) {
     const item = items[index]
-    const {kind, type} = item
+    const type = item.type
 
     if (type === 'text/plain') {
       item.getAsString(async itemString => {
         if (isUriImage(itemString)) {
           const response = await fetch(itemString)
           const blob = await response.blob()
-          blobToDataUri(blob).then(callback, err => console.error(err))
+
+          if (blob.type.startsWith('image/')) {
+            blobToDataUri(blob).then(callback, err => console.error(err))
+          }
         }
       })
-    }
-
-    if (kind === 'file') {
+    } else if (type.startsWith('image/')) {
       const file = item.getAsFile()
 
-      if (file instanceof Blob) {
-        blobToDataUri(new Blob([file], {type: item.type})).then(callback, err =>
-          console.error(err),
-        )
+      if (file) {
+        blobToDataUri(file).then(callback, err => console.error(err))
       }
     }
   }
-- 
cgit 1.4.1

