|
|
@@ -2,6 +2,9 @@ import React from 'react';
|
|
|
import PropTypes from 'prop-types';
|
|
|
import ReactUtils from '../ReactUtils';
|
|
|
|
|
|
+import * as toastr from 'toastr';
|
|
|
+
|
|
|
+import Editor from '../PageEditor/Editor';
|
|
|
import CommentPreview from '../PageComment/CommentPreview';
|
|
|
|
|
|
import Button from 'react-bootstrap/es/Button';
|
|
|
@@ -23,27 +26,36 @@ export default class CommentForm extends React.Component {
|
|
|
constructor(props) {
|
|
|
super(props);
|
|
|
|
|
|
+ const config = this.props.crowi.getConfig();
|
|
|
+ const isUploadable = config.upload.image || config.upload.file;
|
|
|
+ const isUploadableFile = config.upload.file;
|
|
|
+
|
|
|
this.state = {
|
|
|
comment: '',
|
|
|
isMarkdown: true,
|
|
|
html: '',
|
|
|
key: 1,
|
|
|
+ isUploadable,
|
|
|
+ isUploadableFile,
|
|
|
+ errorMessage: undefined,
|
|
|
};
|
|
|
|
|
|
this.updateState = this.updateState.bind(this);
|
|
|
+ this.updateStateCheckbox = this.updateStateCheckbox.bind(this);
|
|
|
this.postComment = this.postComment.bind(this);
|
|
|
this.renderHtml = this.renderHtml.bind(this);
|
|
|
this.handleSelect = this.handleSelect.bind(this);
|
|
|
+ this.apiErrorHandler = this.apiErrorHandler.bind(this);
|
|
|
+ this.onUpload = this.onUpload.bind(this);
|
|
|
}
|
|
|
|
|
|
- updateState(event) {
|
|
|
- const target = event.target;
|
|
|
- const value = target.type === 'checkbox' ? target.checked : target.value;
|
|
|
- const name = target.name;
|
|
|
+ updateState(value) {
|
|
|
+ this.setState({comment: value});
|
|
|
+ }
|
|
|
|
|
|
- this.setState({
|
|
|
- [name]: value
|
|
|
- });
|
|
|
+ updateStateCheckbox(event) {
|
|
|
+ const value = event.target.checked;
|
|
|
+ this.setState({isMarkdown: value});
|
|
|
}
|
|
|
|
|
|
handleSelect(key) {
|
|
|
@@ -74,7 +86,14 @@ export default class CommentForm extends React.Component {
|
|
|
isMarkdown: true,
|
|
|
html: '',
|
|
|
key: 1,
|
|
|
+ errorMessage: undefined,
|
|
|
});
|
|
|
+ // reset value
|
|
|
+ this.refs.editor.setValue('');
|
|
|
+ })
|
|
|
+ .catch(err => {
|
|
|
+ const errorMessage = err.message || 'An unknown error occured when posting comment';
|
|
|
+ this.setState({ errorMessage });
|
|
|
});
|
|
|
}
|
|
|
|
|
|
@@ -121,6 +140,49 @@ export default class CommentForm extends React.Component {
|
|
|
return {__html: html};
|
|
|
}
|
|
|
|
|
|
+ onUpload(file) {
|
|
|
+ const endpoint = '/attachments.add';
|
|
|
+
|
|
|
+ // create a FromData instance
|
|
|
+ const formData = new FormData();
|
|
|
+ formData.append('_csrf', this.props.crowi.csrfToken);
|
|
|
+ formData.append('file', file);
|
|
|
+ formData.append('path', this.props.pagePath);
|
|
|
+ formData.append('page_id', this.props.pageId || 0);
|
|
|
+
|
|
|
+ // post
|
|
|
+ this.props.crowi.apiPost(endpoint, formData)
|
|
|
+ .then((res) => {
|
|
|
+ const url = res.url;
|
|
|
+ const attachment = res.attachment;
|
|
|
+ const fileName = attachment.originalName;
|
|
|
+
|
|
|
+ let insertText = `[${fileName}](${url})`;
|
|
|
+ // when image
|
|
|
+ if (attachment.fileFormat.startsWith('image/')) {
|
|
|
+ // modify to "" syntax
|
|
|
+ insertText = '!' + insertText;
|
|
|
+ }
|
|
|
+ this.refs.editor.insertText(insertText);
|
|
|
+ })
|
|
|
+ .catch(this.apiErrorHandler)
|
|
|
+ // finally
|
|
|
+ .then(() => {
|
|
|
+ this.refs.editor.terminateUploadingState();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ apiErrorHandler(error) {
|
|
|
+ console.error(error);
|
|
|
+ toastr.error(error.message, 'Error occured', {
|
|
|
+ closeButton: true,
|
|
|
+ progressBar: true,
|
|
|
+ newestOnTop: false,
|
|
|
+ showDuration: '100',
|
|
|
+ hideDuration: '100',
|
|
|
+ timeOut: '3000',
|
|
|
+ });
|
|
|
+ }
|
|
|
|
|
|
render() {
|
|
|
const crowi = this.props.crowi;
|
|
|
@@ -129,7 +191,9 @@ export default class CommentForm extends React.Component {
|
|
|
const creatorsPage = `/user/${username}`;
|
|
|
const comment = this.state.comment;
|
|
|
const commentPreview = this.state.isMarkdown ? this.getCommentHtml(): ReactUtils.nl2br(comment);
|
|
|
+ const emojiStrategy = this.props.crowi.getEmojiStrategy();
|
|
|
|
|
|
+ const editorOptions = Object.assign(this.props.editorOptions || {}, { lineNumbers: false });
|
|
|
return (
|
|
|
<div>
|
|
|
<form className="form page-comment-form" id="page-comment-form" onSubmit={this.postComment}>
|
|
|
@@ -144,8 +208,16 @@ export default class CommentForm extends React.Component {
|
|
|
<div className="comment-write">
|
|
|
<Tabs activeKey={this.state.key} id="comment-form-tabs" onSelect={this.handleSelect} animation={false}>
|
|
|
<Tab eventKey={1} title="Write">
|
|
|
- <textarea className="comment-form-comment form-control" id="comment-form-comment" name="comment" required placeholder="Write comments here..." value={this.state.comment} onChange={this.updateState} >
|
|
|
- </textarea>
|
|
|
+ <Editor ref="editor"
|
|
|
+ value={this.state.comment}
|
|
|
+ editorOptions={editorOptions}
|
|
|
+ isMobile={this.props.crowi.isMobile}
|
|
|
+ isUploadable={this.state.isUploadable}
|
|
|
+ isUploadableFile={this.state.isUploadableFile}
|
|
|
+ emojiStrategy={emojiStrategy}
|
|
|
+ onChange={this.updateState}
|
|
|
+ onUpload={this.onUpload}
|
|
|
+ />
|
|
|
</Tab>
|
|
|
{ this.state.isMarkdown == true &&
|
|
|
<Tab eventKey={2} title="Preview">
|
|
|
@@ -156,21 +228,19 @@ export default class CommentForm extends React.Component {
|
|
|
}
|
|
|
</Tabs>
|
|
|
</div>
|
|
|
- <div className="comment-submit">
|
|
|
- <div className="pull-left">
|
|
|
+ <div className="comment-submit d-flex">
|
|
|
{ this.state.key == 1 &&
|
|
|
<label>
|
|
|
- <input type="checkbox" id="comment-form-is-markdown" name="isMarkdown" checked={this.state.isMarkdown} value="1" onChange={this.updateState} /> Markdown
|
|
|
+ <input type="checkbox" id="comment-form-is-markdown" name="isMarkdown" checked={this.state.isMarkdown} value="1" onChange={this.updateStateCheckbox} /> Markdown
|
|
|
</label>
|
|
|
}
|
|
|
- </div>
|
|
|
- <div className="pull-right">
|
|
|
- <Button type="submit" value="Submit" bsStyle="primary" className="fcbtn btn btn-sm btn-primary btn-outline btn-rounded btn-1b">
|
|
|
- Comment
|
|
|
- </Button>
|
|
|
- </div>
|
|
|
- <div className="clearfix">
|
|
|
- </div>
|
|
|
+ <div style={{flex: 1}}></div>{/* spacer */}
|
|
|
+ { this.state.errorMessage &&
|
|
|
+ <span className="text-danger text-right mr-2">{this.state.errorMessage}</span>
|
|
|
+ }
|
|
|
+ <Button type="submit" value="Submit" bsStyle="primary" className="fcbtn btn btn-sm btn-primary btn-outline btn-rounded btn-1b">
|
|
|
+ Comment
|
|
|
+ </Button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -183,8 +253,10 @@ export default class CommentForm extends React.Component {
|
|
|
|
|
|
CommentForm.propTypes = {
|
|
|
crowi: PropTypes.object.isRequired,
|
|
|
+ crowiRenderer: PropTypes.object.isRequired,
|
|
|
onPostComplete: PropTypes.func,
|
|
|
pageId: PropTypes.string,
|
|
|
revisionId: PropTypes.string,
|
|
|
- crowiRenderer: PropTypes.object.isRequired,
|
|
|
+ pagePath: PropTypes.string,
|
|
|
+ editorOptions: PropTypes.object,
|
|
|
};
|