diff --git a/api/db/dev/.gitkeep b/api/db/dev/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/api/db/production/.gitkeep b/api/db/production/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/app/components/SimpleEditor/Editor.js b/app/components/SimpleEditor/Editor.js index 181f7f5594fc6415c09908ec378bda354ece35d6..ac7e0183fa63d4abea6bff0ac80cd392e3123ab9 100644 --- a/app/components/SimpleEditor/Editor.js +++ b/app/components/SimpleEditor/Editor.js @@ -123,6 +123,7 @@ class Editor extends ProseEditor { _renderEditor ($$) { const configurator = this.props.configurator const editing = this.props.disabled ? 'selection' : 'full' + return $$(ContainerEditor, { editing: editing, documentSession: this.documentSession, diff --git a/app/components/SimpleEditor/config.js b/app/components/SimpleEditor/config.js index 0a80dcf194e4564fff922d7266afbfc32a9c3749..dcc9e237af43a3d736a0633a0bdf4f1ec438e6e7 100644 --- a/app/components/SimpleEditor/config.js +++ b/app/components/SimpleEditor/config.js @@ -20,7 +20,11 @@ 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 TrackChangePackage from './elements/track_change/TrackChangePackage' + +import ImagePackage from './elements/images/ImagePackage' + // var DialoguePackage = require('./elements/dialogue/DialoguePackage') // var NumberedListPackage = require('./elements/numbered_list/NumberedListPackage') // var NoStyleListPackage = require('./elements/no_style_list/NoStyleListPackage') @@ -56,7 +60,9 @@ let config = { config.import(NotePackage) config.import(SourceNotePackage) config.import(CommentPackage) + config.import(TrackChangePackage) + 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 8105d4e9a5bedf9f2a263c9ca88a12fdfd71413a..520be25da67394599e515f6a3e2a15481910a33f 100644 --- a/app/components/SimpleEditor/elements/elements.scss +++ b/app/components/SimpleEditor/elements/elements.scss @@ -7,4 +7,8 @@ // @import './no_style_list/noStyleList'; // @import './numbered_list/numberedList'; @import './source_note/sourceNote'; + @import './track_change/trackChange'; + +@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 0000000000000000000000000000000000000000..67e4d8abf4b73a449bc378fdb6884b3514cefe5d --- /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 0000000000000000000000000000000000000000..30c6a73209b57e0b944f7afb2008289c806ed999 --- /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 0000000000000000000000000000000000000000..088352cacd44902b0aebb79054af991977bb1ead --- /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 0000000000000000000000000000000000000000..ec44a1de8330e9df514135b4961802d617ee4280 --- /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 0000000000000000000000000000000000000000..c2a2f5d0ad2740f07c6b03cd35869ad99d7f5860 --- /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 0000000000000000000000000000000000000000..adfb46c4d198f83708a7264cfc982d7b82d59c37 --- /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 0000000000000000000000000000000000000000..166907fe238872eb71f6aa3994d3a19cf51b2626 --- /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 0000000000000000000000000000000000000000..d870d20466b53314c3daf02a9742026713810551 --- /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 8f9fe04658aaebcb1efe93e9d85e772fe33958bc..6b5f464a6a91897c843437093a165420859ddb5e 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 Binary files /dev/null and b/public/uploads/e417958680511ad409805ea6f23c35c7 differ