From c0abbf3d3ed94f88f913c7390592520eec2fa4a5 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Wed, 29 May 2019 17:47:52 +0200 Subject: [PATCH] Allow uploading pictures into description by drag&drop them Signed-off-by: Thomas Citharel --- js/src/components/Editor.vue | 3 +- js/src/components/Editor/Image.ts | 94 +++++++++++++++++++++++++++++++ js/src/graphql/upload.ts | 6 +- 3 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 js/src/components/Editor/Image.ts diff --git a/js/src/components/Editor.vue b/js/src/components/Editor.vue index b16b616f7..8ff091c86 100644 --- a/js/src/components/Editor.vue +++ b/js/src/components/Editor.vue @@ -162,12 +162,11 @@ import { History, Placeholder, Mention, - Image, } from 'tiptap-extensions'; import tippy, { Instance } from 'tippy.js'; import { SEARCH_PERSONS } from '@/graphql/search'; import { IActor } from '@/types/actor'; - +import Image from '@/components/Editor/Image'; @Component({ components: { EditorContent, EditorMenuBar, EditorMenuBubble }, diff --git a/js/src/components/Editor/Image.ts b/js/src/components/Editor/Image.ts new file mode 100644 index 000000000..26304776c --- /dev/null +++ b/js/src/components/Editor/Image.ts @@ -0,0 +1,94 @@ +import { Node, Plugin } from 'tiptap'; +import { UPLOAD_PICTURE } from '@/graphql/upload'; +import { apolloProvider } from '@/vue-apollo'; +import ApolloClient from 'apollo-client'; +import { InMemoryCache } from 'apollo-cache-inmemory'; + +export default class Image extends Node { + + get name() { + return 'image'; + } + + get schema() { + return { + inline: true, + attrs: { + src: {}, + alt: { + default: null, + }, + title: { + default: null, + }, + }, + group: 'inline', + draggable: true, + parseDOM: [ + { + tag: 'img[src]', + getAttrs: dom => ({ + src: dom.getAttribute('src'), + title: dom.getAttribute('title'), + alt: dom.getAttribute('alt'), + }), + }, + ], + toDOM: node => ['img', node.attrs], + }; + } + + commands({ type }) { + return attrs => (state, dispatch) => { + const { selection } = state; + const position = selection.$cursor ? selection.$cursor.pos : selection.$to.pos; + const node = type.create(attrs); + const transaction = state.tr.insert(position, node); + dispatch(transaction); + }; + } + + get plugins() { + return [ + new Plugin({ + props: { + handleDOMEvents: { + async drop(view, event: DragEvent) { + if (!(event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files.length)) { + return; + } + + const images = Array + .from(event.dataTransfer.files) + .filter((file: any) => (/image/i).test(file.type)); + + if (images.length === 0) { + return; + } + + event.preventDefault(); + + const { schema } = view.state; + const coordinates = view.posAtCoords({ left: event.clientX, top: event.clientY }); + const client = apolloProvider.defaultClient as ApolloClient; + + for (const image of images) { + const { data } = await client.mutate({ + mutation: UPLOAD_PICTURE, + variables: { + file: image, + name: image.name, + }, + }); + const node = schema.nodes.image.create({ src: data.uploadPicture.url }); + const transaction = view.state.tr.insert(coordinates.pos, node); + view.dispatch(transaction); + } + }, + }, + }, + }), + ]; + } + +} diff --git a/js/src/graphql/upload.ts b/js/src/graphql/upload.ts index 138b5d2d3..490e08389 100644 --- a/js/src/graphql/upload.ts +++ b/js/src/graphql/upload.ts @@ -1,10 +1,10 @@ import gql from 'graphql-tag'; export const UPLOAD_PICTURE = gql` - mutation { - uploadPicture(file: "file") { + mutation UploadPicture($file: Upload!, $alt: String, $name: String!){ + uploadPicture(file: $file, alt: $alt, name: $name) { url, - url_thumbnail + id } } `;