diff --git a/js/package.json b/js/package.json index 58a014302..f0b61c7f3 100644 --- a/js/package.json +++ b/js/package.json @@ -27,6 +27,9 @@ "lodash": "^4.17.11", "ngeohash": "^0.6.3", "register-service-worker": "^1.6.2", + "tippy.js": "^4.3.1", + "tiptap": "^1.20.1", + "tiptap-extensions": "^1.20.1", "typeface-signika": "0.0.72", "vue": "^2.6.10", "vue-apollo": "^3.0.0-beta.28", @@ -34,7 +37,6 @@ "vue-gettext": "^2.1.3", "vue-property-decorator": "^8.1.0", "vue-router": "^3.0.6", - "vue-simple-markdown": "^1.0.9", "vue2-leaflet": "^2.0.3", "vuex": "^3.1.0" }, diff --git a/js/src/components/Editor.vue b/js/src/components/Editor.vue new file mode 100644 index 000000000..a40f5640e --- /dev/null +++ b/js/src/components/Editor.vue @@ -0,0 +1,680 @@ + + + + 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/search.ts b/js/src/graphql/search.ts index f5af5221b..562a8eab1 100644 --- a/js/src/graphql/search.ts +++ b/js/src/graphql/search.ts @@ -34,3 +34,21 @@ query SearchGroups($searchText: String!) { } } `; + +export const SEARCH_PERSONS = gql` + query SearchPersons($searchText: String!) { + searchPersons(search: $searchText) { + total, + elements { + id, + avatar { + url + }, + domain, + preferredUsername, + name, + __typename + } + } + } +`; 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 } } `; diff --git a/js/src/main.ts b/js/src/main.ts index 5610610b0..7ef88bf0b 100644 --- a/js/src/main.ts +++ b/js/src/main.ts @@ -1,7 +1,6 @@ // The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue'; -import VueSimpleMarkdown from 'vue-simple-markdown'; import Buefy from 'buefy'; import GetTextPlugin from 'vue-gettext'; import App from '@/App.vue'; @@ -12,7 +11,6 @@ const translations = require('@/i18n/translations.json'); Vue.config.productionTip = false; -Vue.use(VueSimpleMarkdown); Vue.use(Buefy, { defaultContainerElement: '#mobilizon', }); diff --git a/js/src/types/search.model.ts b/js/src/types/search.model.ts index 1bd731439..afe27ba7b 100644 --- a/js/src/types/search.model.ts +++ b/js/src/types/search.model.ts @@ -1,4 +1,4 @@ -import { IGroup } from '@/types/actor'; +import { IGroup, IPerson } from '@/types/actor'; import { IEvent } from '@/types/event.model'; export interface SearchEvent { @@ -10,3 +10,8 @@ export interface SearchGroup { total: number; elements: IGroup[]; } + +export interface SearchPerson { + total: number; + elements: IPerson[]; +} diff --git a/js/src/utils/upload.ts b/js/src/utils/upload.ts new file mode 100644 index 000000000..b415f0fa4 --- /dev/null +++ b/js/src/utils/upload.ts @@ -0,0 +1,36 @@ +/** + * Async function to upload a file + */ +export function listenFileUpload(): Promise { + return new Promise((resolve, reject) => { + const inputElement = document.createElement('input'); + inputElement.type = 'file'; + inputElement.onchange = () => { + if (inputElement.files && inputElement.files.length > 0) { + resolve(inputElement.files[0]); + } + }; + + inputElement.onerror = reject; + inputElement.click(); + }); +} + +/** + * Async function to upload a file + */ +export function listenFileUploads(): Promise { + return new Promise((resolve, reject) => { + const inputElement = document.createElement('input'); + inputElement.type = 'file'; + inputElement.multiple = true; + inputElement.onchange = () => { + if (inputElement.files && inputElement.files.length > 0) { + resolve(inputElement.files); + } + }; + + inputElement.onerror = reject; + inputElement.click(); + }); +} diff --git a/js/src/views/Event/Create.vue b/js/src/views/Event/Create.vue index 60055caf1..4e3a8d08f 100644 --- a/js/src/views/Event/Create.vue +++ b/js/src/views/Event/Create.vue @@ -4,14 +4,19 @@ Create a new event
Loading...
-
-
+
+ +
+ + +
+