feat: web composer image drag and drop

zio/stable
Mary 2024-01-20 10:39:28 +07:00
parent cd02922b03
commit fbf2970bae
No known key found for this signature in database
1 changed files with 54 additions and 9 deletions

View File

@ -18,6 +18,7 @@ import {Emoji} from './web/EmojiPicker.web'
import {LinkDecorator} from './web/LinkDecorator' import {LinkDecorator} from './web/LinkDecorator'
import {generateJSON} from '@tiptap/html' import {generateJSON} from '@tiptap/html'
import {useActorAutocompleteFn} from '#/state/queries/actor-autocomplete' import {useActorAutocompleteFn} from '#/state/queries/actor-autocomplete'
import {usePalette} from '#/lib/hooks/usePalette'
export interface TextInputRef { export interface TextInputRef {
focus: () => void focus: () => void
@ -53,7 +54,11 @@ export const TextInput = React.forwardRef(function TextInputImpl(
) { ) {
const autocomplete = useActorAutocompleteFn() const autocomplete = useActorAutocompleteFn()
const pal = usePalette('default')
const modeClass = useColorSchemeStyle('ProseMirror-light', 'ProseMirror-dark') const modeClass = useColorSchemeStyle('ProseMirror-light', 'ProseMirror-dark')
const [isDropping, setIsDropping] = React.useState(false)
const extensions = React.useMemo( const extensions = React.useMemo(
() => [ () => [
Document, Document,
@ -112,6 +117,32 @@ export const TextInput = React.forwardRef(function TextInputImpl(
return true 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), content: generateJSON(richtext.text.toString(), extensions),
autofocus: 'end', autofocus: 'end',
@ -179,6 +210,10 @@ export const TextInput = React.forwardRef(function TextInputImpl(
return ( return (
<View style={styles.container}> <View style={styles.container}>
<EditorContent editor={editor} /> <EditorContent editor={editor} />
{isDropping && (
<View style={[{borderColor: pal.colors.link}, styles.dropContainer]} />
)}
</View> </View>
) )
}) })
@ -210,6 +245,17 @@ const styles = StyleSheet.create({
marginLeft: 8, marginLeft: 8,
marginBottom: 10, marginBottom: 10,
}, },
dropContainer: {
pointerEvents: 'none',
position: 'absolute',
borderWidth: 4,
borderRadius: 8,
borderStyle: 'dashed',
top: 0,
bottom: 0,
left: 0,
right: 0,
},
}) })
function getImageFromUri( function getImageFromUri(
@ -218,25 +264,24 @@ function getImageFromUri(
) { ) {
for (let index = 0; index < items.length; index++) { for (let index = 0; index < items.length; index++) {
const item = items[index] const item = items[index]
const {kind, type} = item const type = item.type
if (type === 'text/plain') { if (type === 'text/plain') {
item.getAsString(async itemString => { item.getAsString(async itemString => {
if (isUriImage(itemString)) { if (isUriImage(itemString)) {
const response = await fetch(itemString) const response = await fetch(itemString)
const blob = await response.blob() 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))
}
} }
}) })
} } else if (type.startsWith('image/')) {
if (kind === 'file') {
const file = item.getAsFile() const file = item.getAsFile()
if (file instanceof Blob) { if (file) {
blobToDataUri(new Blob([file], {type: item.type})).then(callback, err => blobToDataUri(file).then(callback, err => console.error(err))
console.error(err),
)
} }
} }
} }