Implemented drag and drop file uploads
This commit is contained in:
@@ -54,6 +54,7 @@
|
||||
"normalize.css": "^3.0.3",
|
||||
"react": "^0.14.7",
|
||||
"react-codemirror": "^0.2.5",
|
||||
"react-dropzone": "^3.3.2",
|
||||
"react-medium-editor": "^1.6.2",
|
||||
"react-redux": "^4.4.0",
|
||||
"react-router": "^2.0.0",
|
||||
|
||||
@@ -7,6 +7,7 @@ import keyMirror from 'fbjs/lib/keyMirror';
|
||||
export const UPDATE_TEXT = 'UPDATE_TEXT';
|
||||
export const TOGGLE_EDITORS = 'TOGGLE_EDITORS';
|
||||
export const ADD_REVISION = 'ADD_REVISION';
|
||||
export const REPLACE_TEXT= 'REPLACE_TEXT';
|
||||
|
||||
/*
|
||||
* Other Constants
|
||||
@@ -32,3 +33,7 @@ export function toggleEditors(toggledEditor) {
|
||||
export function addRevision(createdAt) {
|
||||
return { type: ADD_REVISION, createdAt };
|
||||
}
|
||||
|
||||
export function replaceText(originalText, replacedText) {
|
||||
return { type: REPLACE_TEXT, originalText, replacedText };
|
||||
}
|
||||
@@ -3,14 +3,22 @@ import Codemirror from 'react-codemirror';
|
||||
import 'codemirror/mode/gfm/gfm';
|
||||
import 'codemirror/mode/javascript/javascript';
|
||||
import 'codemirror/addon/edit/continuelist';
|
||||
import Dropzone from 'react-dropzone';
|
||||
|
||||
import styles from './MarkdownEditor.scss';
|
||||
import './codemirror.css';
|
||||
|
||||
import { client } from '../../Utils/ApiClient';
|
||||
|
||||
class MarkdownAtlas extends React.Component {
|
||||
static propTypes = {
|
||||
text: React.PropTypes.string,
|
||||
onChange: React.PropTypes.func,
|
||||
replaceText: React.PropTypes.func,
|
||||
}
|
||||
|
||||
getEditorInstance = () => {
|
||||
return this.refs.editor.getCodeMirror();
|
||||
}
|
||||
|
||||
onChange = (newText) => {
|
||||
@@ -19,6 +27,63 @@ class MarkdownAtlas extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount = () => {
|
||||
console.log(this.props);
|
||||
}
|
||||
|
||||
onDropAccepted = (files) => {
|
||||
const file = files[0];
|
||||
const editor = this.getEditorInstance();
|
||||
|
||||
const cursorPosition = editor.getCursor();
|
||||
const insertOnNewLine = cursorPosition.ch !== 0;
|
||||
let newCursorPositionLine;
|
||||
|
||||
// Lets set up the upload text
|
||||
const pendingUploadTag = ``;
|
||||
if (insertOnNewLine) {
|
||||
editor.replaceSelection('\n' + pendingUploadTag + '\n');
|
||||
newCursorPositionLine = cursorPosition.line + 3;
|
||||
} else {
|
||||
editor.replaceSelection(pendingUploadTag + '\n');
|
||||
newCursorPositionLine = cursorPosition.line + 2;
|
||||
}
|
||||
editor.setCursor(newCursorPositionLine, 0);
|
||||
|
||||
client.post('/v0/user/s3', {
|
||||
kind: file.type,
|
||||
size: file.size,
|
||||
filename: file.name,
|
||||
})
|
||||
.then(data => {
|
||||
// Upload using FormData API
|
||||
let formData = new FormData();
|
||||
|
||||
for (let key in data.form) {
|
||||
formData.append(key, data.form[key]);
|
||||
}
|
||||
|
||||
if (file.blob) {
|
||||
formData.append('file', file.file);
|
||||
} else {
|
||||
formData.append('file', file);
|
||||
}
|
||||
|
||||
fetch(data.upload_url, {
|
||||
method: 'post',
|
||||
body: formData
|
||||
})
|
||||
.then(s3Response => {
|
||||
this.props.replaceText(pendingUploadTag, ``);
|
||||
editor.setCursor(newCursorPositionLine, 0);
|
||||
})
|
||||
.catch(err => {
|
||||
this.props.replaceText(pendingUploadTag, '');
|
||||
editor.setCursor(newCursorPositionLine, 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
render = () => {
|
||||
// https://github.com/jbt/markdown-editor/blob/master/index.html
|
||||
const options = {
|
||||
@@ -39,12 +104,21 @@ class MarkdownAtlas extends React.Component {
|
||||
// - Emojify
|
||||
// -
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<Codemirror
|
||||
value={this.props.text}
|
||||
onChange={this.onChange}
|
||||
options={options}
|
||||
/>
|
||||
<div>
|
||||
<Dropzone
|
||||
onDropAccepted={this.onDropAccepted}
|
||||
disableClick={true}
|
||||
multiple={false}
|
||||
accept={'image/*'}
|
||||
className={styles.container}
|
||||
>
|
||||
<Codemirror
|
||||
value={this.props.text}
|
||||
onChange={this.onChange}
|
||||
options={options}
|
||||
ref="editor"
|
||||
/>
|
||||
</Dropzone>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
@import url(https://fonts.googleapis.com/css?family=Cousine:400,700,700italic,400italic);
|
||||
|
||||
/* Custom styling */
|
||||
.cm-s-atlas.CodeMirror {
|
||||
background: #ffffff;
|
||||
color: #202020;
|
||||
@@ -21,6 +22,11 @@
|
||||
background: #90CAF9;
|
||||
}
|
||||
|
||||
/* Disable ondrag cursor for file uploads */
|
||||
.cm-s-atlas div.CodeMirror-dragcursors {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.cm-s-atlas .CodeMirror-line::selection,
|
||||
.cm-s-atlas .CodeMirror-line > span::selection,
|
||||
.cm-s-atlas .CodeMirror-line > span > span::selection {
|
||||
|
||||
@@ -11,7 +11,7 @@ const keys = keyMirror({
|
||||
// Constant values
|
||||
const constants = {
|
||||
API_USER_AGENT: `${name}/${version}`,
|
||||
API_BASE_URL: 'http://localhost:3000/api',
|
||||
API_BASE_URL: 'http://localhost:8000/api',
|
||||
LOGIN_PATH: '/login',
|
||||
LOGIN_SUCCESS_PATH: '/dashboard',
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
TOGGLE_EDITORS,
|
||||
TOGGLE_HISTORY_SIDEBAR,
|
||||
ADD_REVISION,
|
||||
REPLACE_TEXT,
|
||||
ActiveEditors,
|
||||
} from '../Actions';
|
||||
|
||||
@@ -89,6 +90,14 @@ const text = (state = textDefaultState, action) => {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
case REPLACE_TEXT: {
|
||||
const newText = state.text.replace(action.originalText, action.replacedText);
|
||||
|
||||
return {
|
||||
...state,
|
||||
text: newText,
|
||||
};
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,12 @@ import MarkdownEditor from '../../Components/MarkdownEditor';
|
||||
import TextEditor from '../../Components/TextEditor';
|
||||
|
||||
import { toMarkdown } from '../../Utils/Markdown';
|
||||
import { updateText } from '../../Actions';
|
||||
import {
|
||||
updateText,
|
||||
replaceText,
|
||||
} from '../../Actions';
|
||||
|
||||
import Constants from '../../Constants';
|
||||
|
||||
import styles from './Dashboard.scss';
|
||||
|
||||
@@ -14,28 +19,26 @@ class Dashboard extends Component {
|
||||
editMarkdown: React.PropTypes.func.isRequired,
|
||||
editText: React.PropTypes.func.isRequired,
|
||||
text: React.PropTypes.string,
|
||||
replaceText: React.PropTypes.func.isRequired,
|
||||
activeEditors: React.PropTypes.arrayOf(React.PropTypes.string),
|
||||
showHistorySidebar: React.PropTypes.bool.isRequired,
|
||||
}
|
||||
|
||||
// componentDidMount = () => {
|
||||
// client.get('/user')
|
||||
// .then(data => {
|
||||
// this.setState({ user: data });
|
||||
// });
|
||||
// }
|
||||
|
||||
render() {
|
||||
const activeEditors = this.props.activeEditors;
|
||||
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<div className={styles.container}>
|
||||
{
|
||||
activeEditors.includes('MARKDOWN') ? (
|
||||
<div className={ `${activeEditors.length > 1 ?
|
||||
styles.panel : styles.fullscreen} ${styles.markdown}`}
|
||||
>
|
||||
<MarkdownEditor onChange={this.props.editMarkdown} text={this.props.text} />
|
||||
<MarkdownEditor
|
||||
onChange={this.props.editMarkdown}
|
||||
text={this.props.text}
|
||||
replaceText={this.props.replaceText}
|
||||
/>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
@@ -72,6 +75,9 @@ const mapDispatchToProps = (dispatch) => {
|
||||
const text = toMarkdown(html);
|
||||
dispatch(updateText(text, 'text'));
|
||||
},
|
||||
replaceText: (originalText, replacedText) => {
|
||||
dispatch(replaceText(originalText, replacedText));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user