From a908fa84e2028b6270852fc35bd14544cecd9eb4 Mon Sep 17 00:00:00 2001 From: Alexandros Georgantas <alexgeorg86@gmail.com> Date: Tue, 20 Dec 2016 15:17:23 +0200 Subject: [PATCH] Image implementation and ignore DS_Store --- .gitignore | 1 + api/db/dev/.gitkeep | 0 api/db/production/.gitkeep | 0 app/components/SimpleEditor/Editor.js | 2 +- app/components/SimpleEditor/config.js | 2 + .../SimpleEditor/elements/elements.scss | 2 + .../SimpleEditor/elements/images/Image.js | 11 ++ .../elements/images/ImageComponent.js | 51 +++++++++ .../elements/images/ImageHTMLConverter.js | 18 ++++ .../elements/images/ImagePackage.js | 24 +++++ .../elements/images/InsertImageCommand.js | 101 ++++++++++++++++++ .../elements/images/InsertImageTool.js | 29 +++++ .../SimpleEditor/elements/images/image.scss | 16 +++ .../elements/images/insert-image-tool.scss | 5 + config/dev.js | 2 +- .../uploads/e417958680511ad409805ea6f23c35c7 | Bin 0 -> 684 bytes 16 files changed, 262 insertions(+), 2 deletions(-) delete mode 100644 api/db/dev/.gitkeep delete mode 100644 api/db/production/.gitkeep create mode 100644 app/components/SimpleEditor/elements/images/Image.js create mode 100644 app/components/SimpleEditor/elements/images/ImageComponent.js create mode 100644 app/components/SimpleEditor/elements/images/ImageHTMLConverter.js create mode 100644 app/components/SimpleEditor/elements/images/ImagePackage.js create mode 100644 app/components/SimpleEditor/elements/images/InsertImageCommand.js create mode 100644 app/components/SimpleEditor/elements/images/InsertImageTool.js create mode 100644 app/components/SimpleEditor/elements/images/image.scss create mode 100644 app/components/SimpleEditor/elements/images/insert-image-tool.scss create mode 100644 public/uploads/e417958680511ad409805ea6f23c35c7 diff --git a/.gitignore b/.gitignore index e44031e..e67bc2e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ logs/* node_modules public/assets/* pubsweet.log +.DS_Store diff --git a/api/db/dev/.gitkeep b/api/db/dev/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/api/db/production/.gitkeep b/api/db/production/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/app/components/SimpleEditor/Editor.js b/app/components/SimpleEditor/Editor.js index 0b1a223..7bd5c3c 100644 --- a/app/components/SimpleEditor/Editor.js +++ b/app/components/SimpleEditor/Editor.js @@ -96,7 +96,7 @@ class Editor extends ProseEditor { _renderEditor ($$) { const configurator = this.props.configurator const editing = this.props.disabled ? 'selection' : 'full' - + console.log('ed', editing) return $$(ContainerEditor, { editing: editing, documentSession: this.documentSession, diff --git a/app/components/SimpleEditor/config.js b/app/components/SimpleEditor/config.js index a637603..79df1f7 100644 --- a/app/components/SimpleEditor/config.js +++ b/app/components/SimpleEditor/config.js @@ -20,6 +20,7 @@ import CommentPackage from './elements/comment/CommentPackage' import ExtractPackage from './elements/extract/ExtractPackage' import NotePackage from './elements/note/NotePackage' import SourceNotePackage from './elements/source_note/SourceNotePackage' +import ImagePackage from './elements/images/ImagePackage' // var DialoguePackage = require('./elements/dialogue/DialoguePackage') // var NumberedListPackage = require('./elements/numbered_list/NumberedListPackage') @@ -56,6 +57,7 @@ let config = { config.import(NotePackage) config.import(SourceNotePackage) config.import(CommentPackage) + config.import(ImagePackage) // config.import(DialoguePackage) // config.import(NoStyleListPackage) diff --git a/app/components/SimpleEditor/elements/elements.scss b/app/components/SimpleEditor/elements/elements.scss index 786bf0a..735e3a2 100644 --- a/app/components/SimpleEditor/elements/elements.scss +++ b/app/components/SimpleEditor/elements/elements.scss @@ -7,3 +7,5 @@ // @import './no_style_list/noStyleList'; // @import './numbered_list/numberedList'; @import './source_note/sourceNote'; +@import './images/image'; +@import './images/insert-image-tool'; diff --git a/app/components/SimpleEditor/elements/images/Image.js b/app/components/SimpleEditor/elements/images/Image.js new file mode 100644 index 0000000..67e4d8a --- /dev/null +++ b/app/components/SimpleEditor/elements/images/Image.js @@ -0,0 +1,11 @@ +import { DocumentNode } from 'substance' + +class Image extends DocumentNode {} + +Image.define({ + type: 'image', + src: { type: 'string', default: 'http://' }, + previewSrc: { type: 'string', optional: true } +}) + +export default Image diff --git a/app/components/SimpleEditor/elements/images/ImageComponent.js b/app/components/SimpleEditor/elements/images/ImageComponent.js new file mode 100644 index 0000000..30c6a73 --- /dev/null +++ b/app/components/SimpleEditor/elements/images/ImageComponent.js @@ -0,0 +1,51 @@ +import { BlockNodeComponent } from 'substance' + +class ImageComponent extends BlockNodeComponent { + + didMount () { + super.didMount.call(this) + const { node } = this.props + node.on('src:changed', this.rerender, this) + // TODO: we should try to factor this out for reuse + node.on('upload:started', this.onUploadStarted, this) + node.on('upload:finished', this.onUploadFinished, this) + } + + dispose () { + super.dispose.call(this) + const { node } = this.props + node.off(this) + } + + render ($$) { + let el = super.render.call(this, $$) + el.addClass('sc-image') + + el.append( + $$('img').attr({ + src: this.props.node.src + }).ref('image') + ) + + if (this.state.uploading) { + let progressBar = $$('div') + .addClass('se-progress-bar') + .ref('progressBar') + .append('Uploading ...') + el.append(progressBar) + } + + return el + } + + onUploadStarted () { + this.setState({ uploading: true }) + } + + onUploadFinished () { + this.setState({}) + } + +} + +export default ImageComponent diff --git a/app/components/SimpleEditor/elements/images/ImageHTMLConverter.js b/app/components/SimpleEditor/elements/images/ImageHTMLConverter.js new file mode 100644 index 0000000..088352c --- /dev/null +++ b/app/components/SimpleEditor/elements/images/ImageHTMLConverter.js @@ -0,0 +1,18 @@ +/* + * HTML converter for Paragraphs. + */ +export default { + + type: 'image', + tagName: 'img', + + import: function (el, node) { + node.src = el.attr('src') + node.previewSrc = el.attr('data-preview-src') + }, + + export: function (node, el) { + el.attr('src', node.src) + if (node.previewSrc) el.attr('data-preview-src', node.previewSrc) + } +} diff --git a/app/components/SimpleEditor/elements/images/ImagePackage.js b/app/components/SimpleEditor/elements/images/ImagePackage.js new file mode 100644 index 0000000..ec44a1d --- /dev/null +++ b/app/components/SimpleEditor/elements/images/ImagePackage.js @@ -0,0 +1,24 @@ +import ImageNode from './Image' +import ImageComponent from './ImageComponent' +import ImageHTMLConverter from './ImageHTMLConverter' +import InsertImageCommand from './InsertImageCommand' +import InsertImageTool from './InsertImageTool' + +export default { + name: 'image', + configure: function (config) { + config.addNode(ImageNode) + config.addComponent('image', ImageComponent) + config.addConverter('html', ImageHTMLConverter) + config.addCommand('insert-image', InsertImageCommand) + config.addTool('insert-image', InsertImageTool) + config.addIcon('insert-image', { 'fontawesome': 'fa-image' }) + config.addLabel('image', { en: 'Image' }) + config.addLabel('insert-image', { en: 'Insert image' }) + }, + ImageNode: ImageNode, + ImageComponent: ImageComponent, + ImageHTMLConverter: ImageHTMLConverter, + InsertImageCommand: InsertImageCommand, + InsertImageTool: InsertImageTool +} diff --git a/app/components/SimpleEditor/elements/images/InsertImageCommand.js b/app/components/SimpleEditor/elements/images/InsertImageCommand.js new file mode 100644 index 0000000..c2a2f5d --- /dev/null +++ b/app/components/SimpleEditor/elements/images/InsertImageCommand.js @@ -0,0 +1,101 @@ +import { Command, pasteContent } from 'substance' + +class ImageCommand extends Command { + constructor () { + super({ name: 'insert-image' }) + } + + getCommandState (params) { + let sel = params.selection + let surface = params.surface + let newState = { + disabled: true, + active: false + } + if (sel && !sel.isNull() && !sel.isCustomSelection() && + surface && surface.isContainerEditor()) { + newState.disabled = false + } + return newState + } + + /** + Inserts (stub) images and triggers a fileupload. + After upload has completed, the image URLs get updated. + */ + execute (params, context) { + let state = this.getCommandState(params) + // Return if command is disabled + if (state.disabled) return + + let documentSession = params.documentSession + let sel = params.selection + let surface = params.surface + let fileClient = context.fileClient + let files = params.files + + // can drop images only into container editors + if (!surface.isContainerEditor()) return + + // creating a small doc where we add the images + // and then we use the paste transformation to get this snippet + // into the real doc + let doc = surface.getDocument() + let snippet = doc.createSnippet() + + // as file upload takes longer we will insert stub images + let items = files.map(function (file) { + let node = snippet.create({ type: 'image' }) + snippet.show(node) + return { + file: file, + nodeId: node.id + } + }) + + surface.transaction(function (tx) { + tx.before.selection = sel + return pasteContent(tx, { + selection: sel, + containerId: surface.getContainerId(), + doc: snippet + }) + }) + + // start uploading + items.forEach(function (item) { + let nodeId = item.nodeId + // let file = item.file + console.log('item', item) + let node = doc.get(nodeId) + node.emit('upload:started') + // let node = doc.get(nodeId) + if (node) { + node.emit('upload:finished') + // documentSession.transaction(function (tx) { + // tx.set([nodeId, 'src'],) + // }) + } + // let channel = fileClient.uploadFile(file, function (err, url) { + // if (err) { + // url = 'error' + // } + // get the node again to make sure it still exists + // let node = doc.get(nodeId) + // if (node) { + // node.emit('upload:finished') + // documentSession.transaction(function (tx) { + // tx.set([nodeId, 'src'], url) + // }) + // } + // }) + }) + + return { + status: 'file-upload-process-started' + } + } + +} + +export default ImageCommand diff --git a/app/components/SimpleEditor/elements/images/InsertImageTool.js b/app/components/SimpleEditor/elements/images/InsertImageTool.js new file mode 100644 index 0000000..adfb46c --- /dev/null +++ b/app/components/SimpleEditor/elements/images/InsertImageTool.js @@ -0,0 +1,29 @@ +import { Tool } from 'substance' + +class InsertImageTool extends Tool { + + getClassNames () { + return 'sc-insert-image-tool' + } + + renderButton ($$) { + let button = super.renderButton($$) + let input = $$('input').attr('type', 'file').ref('input') + .on('change', this.onFileSelect) + return [button, input] + } + + onClick () { + this.refs.input.click() + } + + onFileSelect (e) { + let files = e.currentTarget.files + this.executeCommand({ + files: Array.prototype.slice.call(files) + }) + } + +} + +export default InsertImageTool diff --git a/app/components/SimpleEditor/elements/images/image.scss b/app/components/SimpleEditor/elements/images/image.scss new file mode 100644 index 0000000..166907f --- /dev/null +++ b/app/components/SimpleEditor/elements/images/image.scss @@ -0,0 +1,16 @@ +.sc-image { + display: block; + margin: 0 auto; + max-width: 100%; + position: relative; + + img { + max-width: 1600px; + } + + .se-progress-bar { + height: 300px; + width: 400px; + } + +} diff --git a/app/components/SimpleEditor/elements/images/insert-image-tool.scss b/app/components/SimpleEditor/elements/images/insert-image-tool.scss new file mode 100644 index 0000000..d870d20 --- /dev/null +++ b/app/components/SimpleEditor/elements/images/insert-image-tool.scss @@ -0,0 +1,5 @@ +.sc-insert-image-tool > input { + left: -1000px; + position: fixed; + top: -1000px; +} diff --git a/config/dev.js b/config/dev.js index 8f9fe04..6b5f464 100644 --- a/config/dev.js +++ b/config/dev.js @@ -12,7 +12,7 @@ module.exports = { }, 'pubsweet-backend': { dbPath: path.join(__dirname, '..', 'api', 'db'), - secret: 'c0d9a89b-fcce-49b3-83bc-0ccf3326d0ef', + secret: 'c2f6dba5-b5fa-418a-8dfe-430917a306ee', API_ENDPOINT: '/api' }, 'pubsweet-component-ink-backend': universal.inkBackend, diff --git a/public/uploads/e417958680511ad409805ea6f23c35c7 b/public/uploads/e417958680511ad409805ea6f23c35c7 new file mode 100644 index 0000000000000000000000000000000000000000..3074ec8386b48ffeebc1fb63061235129dc2e141 GIT binary patch literal 684 zcmV;d0#p5oP)<h;3K|Lk000e1NJLTq002k;002k`1ONa4|Kxkj0007WNkl<ZcmeI! z1FZITAIIVA(b4<Pwzqh}yqL3X+YV;iwr$(Cw_w)M%tybSsA-xWboXigcanatnBThE zzNV(89*26{KSSPfux^Q4%*P_<LyJmSPkv4|sY|jB6(tUmthYspHzn&IQR1&=P}W<b z#2b=zuqbhWWOe&c6uhrX!WJz|dfy@v*X<y0`fJD=4$!5kscIee@v7g3yksw35;w0m zM9vphm9R-(5(U3mQL=UvCHgEQQO}4{pIbnZeih}sV>Stj5j>;2y#|HH&C*_j!uytM zuR-Athv`T@sNi#Z`G4|31;4w)9#%A$Nht=MYUT*z5)5KZ-?rBvW_6aS>K~-j`Ys&9 zAXwbx{;qS-98NMQ#25#`ybkkjJ%hF}AVkGMz>;=zmDlu|YFwJ^3Lz>Cs<p}P740<W zGSN<hwh{7XdLZSXISh!LkF3NfgN`Y3gmrWpmrfKpPx`+sQOOUs(sG>!@@FSM2Y-u- z4$xxji_A<b$;TU_qUn~`QWuKMgNhv>D!X1wy(%(WE7s{fQPFo=s$XQ5P>hAVD=L|( zrKX9@6lHbV-|POYFsY@cicE+9-nb3w6PYF3VbIGWvrRh;I$vZSHd>F0%sKT8T36(( zY@}8fIcwH4s6&6*r=z4<eJV1&rqnZtV?@qV<&(_nIgxV|bq$)$4~5^4RTW#^ry{4< zEcFdyGg0t}?JT8Jhox=jF;TDybq?YJQNelCJ!q2K#2DRTV*P`d<U%n9=jZ3tIS4lM zgBZaNHj%IPLCoeT{h~y#qs>a~gG!oWZRdJXuYXL{>m}z}QwO9O)YQ~et$zU+brk~r Sek1_^0000<MNUMnLSTZXVMXZx literal 0 HcmV?d00001 -- GitLab