application/components/common/rich-editor/note-viewer.js
import React from 'react';
import FlatButton from 'material-ui/FlatButton';
import {List, ListItem} from 'material-ui/List';
import Divider from 'material-ui/Divider';
import Subheader from 'material-ui/Subheader';
import IconButton from 'material-ui/IconButton';
import FontIcon from 'material-ui/FontIcon';
import {timeToFormat} from './../../../util/strings';
import {CopyNoteService, CopyNoteStore} from './../../../services/copyNote-service';
import NoteCommentFlux from './../../../services/noteComment-service';
import CurrentUser from './../../../current-user';
import Gravatar from './../gravatar';
import Events from './../../../util/events';
import BaseView from './../../base-view';
import {Row, Col} from './../../flexbox';
import LoadingIndicator from './../loading-indicator';
import SimpleEditor from './../simple-editor';
import {stateToHTML} from 'draft-js-export-html';
// import stateToHTML from './../../../exportToHTML_modified';
/**
* Render the new comment box and container for individual
* note comments.
*/
class CommentBox extends BaseView {
constructor(props) {
super(props);
// The flux container allows us to load the object as
// an instance instead of a singleton
this.service = new NoteCommentFlux();
this.confirm = this.confirm.bind(this);
this.onChange = this.onChange.bind(this);
this.onInputKeyDown = this.onInputKeyDown.bind(this);
this.state = this.service.stores.noteComments.getState();
}
componentWillMount() {
this.state.isLoading = false;
this.service.stores.noteComments.listen(this.onChange);
this.service.actions.noteComments.fetch(this.props.parentReference);
}
componentDidMount() {
//setTimeout(() => this.refs.simpleEditor.focus(), 0);
}
componentWillUnmount() {
this.service.stores.noteComments.unlisten(this.onChange);
}
onChange(state) {
this.setState(state);
}
/**
* Save a new note comment and fetch any pending updates
*/
confirm(e) {
// const comment = this.refs.comment.value;
const data = {
comment: this.state.noteContent,
version: this.props.version,
draftUuid: this.props.parentReference,
contentUuid: this.props.contentUuid
};
this.setState({'isLoading': true});
this.service.actions.noteComments.createAndFetch(this.props.parentReference, data, () => {
this.setState({'isLoading': false});
this.props.handleNewComment();
});
}
/**
* Handle CMD+Enter to save comment
*/
onInputKeyDown(e) {
if (e.which === 13 && e.metaKey) {
this.confirm(e);
}
}
handleEditorChange(raw, state, name) {
this.setState({noteContent: stateToHTML(state.getCurrentContent())});
}
render() {
if (this.state.isLoading) {
return <LoadingIndicator size="small" />;
}
let commentBox = '';
if (this.props.currentlySelected) {
commentBox = (
<div>
<FullWidthDivider />
<SimpleEditor ref='simpleEditor' showToolbar={false} value='' onChange={this.handleEditorChange.bind(this)} style={{'fontSize': '12px'}} parentReference={this.props.parentReference}/>
<FlatButton onClick={this.confirm} label='Add reply' />
</div>
);
}
let comments = '';
if (this.state.comments.size) {
comments = this.state.comments.valueSeq().map((comment, i) => {
return (
<div key={i}>
<SingleNote contents={comment.get('comment')} user={comment.get('user').get('name')} email={comment.get('user').get('email')} version={comment.get('version')} created_at={comment.get('created_at')} parentReference={this.props.parentReference} currentlySelected={this.props.currentlySelected}/>
{
this.state.comments.size !== (i+1)
? (
<FullWidthDivider />
)
: ''
}
</div>
);
});
}
return (
<div>
{comments}
{commentBox}
</div>
);
}
}
/**
* NoteBox component renders the note container and its children.
*
* Looking for the style that changes the highlight color on select?
* Check the CopyNote service... yeah - sorry.
*/
class NoteBox extends BaseView {
constructor(props) {
super(props);
this.service = new NoteCommentFlux();
this.state = this.service.stores.noteComments.getState();
this.state.expanded = false;
this.onEditorUpdate = this.onEditorUpdate.bind(this);
this.onHandleClick = this.onHandleClick.bind(this);
this.onChange = this.onChange.bind(this);
this.handleNewComment = this.handleNewComment.bind(this);
}
componentWillMount() {
this.service.stores.noteComments.listen(this.onChange);
this.service.actions.noteComments.fetch(this.props.parentReference);
}
componentWillUnmount() {
this.service.stores.noteComments.unlisten(this.onChange);
}
onHandleClick() {
CopyNoteService.setLastSelectedNote(this.props.parentReference);
if (!this.state.expanded) {
this.toggleExpand();
}
}
onChange(state) {
this.setState(state);
}
handleNewComment(e) {
// if this is the first new comment we have to refresh comments for ui
if (!this.state.comments.size) {
this.service.actions.noteComments.fetch(this.props.parentReference);
}
}
/**
* Watches for changes in the editor state and repositions itself
* based on the note inline style's parent block
*/
onEditorUpdate(e) {
}
componentDidMount() {
// check bounding box for overflown objects?
}
toggleExpand() {
this.setState({'expanded': !this.state.expanded});
}
render() {
return (
<div
style={{'position':'relative'}}
className={this.props.currentlySelected ? "richEditor-copyNoteView selected" : "richEditor-copyNoteView"}
>
<IconButton tooltip="Expand/Collapse" onClick={this.toggleExpand.bind(this)} style={{'position':'absolute','top':0,'right':0,'zIndex':9}}>
<FontIcon className="mui-icons">{this.state.expanded ? 'expand_less' : 'expand_more'}</FontIcon>
</IconButton>
<div
onClick={this.onHandleClick.bind(this)}
>
{this.props.children}
</div>
{
this.state.expanded
? (
<Row
onClick={this.onHandleClick.bind(this)}
>
<Col xs={12}>
{
this.state.comments.size
? (
<FullWidthDivider />
)
:''
}
<CommentBox currentlySelected={this.props.currentlySelected} parentReference={this.props.parentReference} version={this.props.version} contentUuid={this.props.contentUuid} handleNewComment={this.handleNewComment} />
</Col>
</Row>
)
: ''
}
{
this.state.comments.size && !this.state.expanded
? (
<span className='more-indicator' onClick={this.toggleExpand.bind(this)}>
<i className='fa fa-angle-down'></i>
view replies
<i className='fa fa-angle-down'></i>
</span>
)
: ''
}
</div>
);
}
}
/**
* Render a single note or comment
*/
class SingleNote extends React.Component {
constructor(props) {
super(props);
this.state = {
height: null,
open: false,
maxHeight: '160px'
}
}
componentDidMount() {
setTimeout(() => {
let ref = this.refs.commentText;
this.setState({height: ref.clientHeight});
}, 1)
}
showMore() {
CopyNoteService.setLastSelectedNote(this.props.parentReference);
setTimeout(() => {
this.setState({maxHeight:'100%', open:true})
}, 1)
}
render() {
if (!this.props.currentlySelected) {
this.state.maxHeight = '160px';
this.state.open = false;
} else if (this.state.open) {
this.state.maxHeight = '100%';
}
const dhanjerruz = () => {
let str = this.props.contents;
str = str.replace(/(\@[\w]+)/g, "<span class=\"mention\">\$1</span>");
return {
'__html': str
};
};
return (
<div className="richEditor-singleComment" style={{border:'0px solid', marginBottom:'0px'}}>
<Row middle='xs'>
<Col xs={2}>
<Gravatar email={this.props.email} size={35} style={{'maxWidth': '100%'}} />
</Col>
<Col xs={10}>
<strong>{this.props.user}</strong>
<div className="meta">
{timeToFormat(this.props.created_at, 'hh:mma, M/DD')} (v {this.props.version})
</div>
</Col>
</Row>
<div ref='commentText' style={{maxHeight:this.state.maxHeight, overflow:'hidden'}} dangerouslySetInnerHTML={dhanjerruz()}></div>
{
this.state.height == 160 && !this.state.open
? (
<div style={{textAlign:'right'}}>
<FontIcon
className='mui-icons'
onClick={this.showMore.bind(this)}
style={{cursor:'pointer'}}
title='Show More'
>
more_horiz
</FontIcon>
</div>
)
: ''
}
</div>
);
}
}
/**
* Render the entered note as a floating box. This component has two parts,
* the original note, which is part of the document's meta data, and the note
* comments, which are retrieved from the API.
*/
class NoteViewer extends BaseView {
constructor(props) {
super(props);
this.displayName = 'NoteViewer';
this.onChange = this.onChange.bind(this);
this.state = CopyNoteStore.getState();
}
onChange(state) {
this.setState(state);
}
componentWillMount() {
CopyNoteStore.listen(this.onChange);
}
componentWillUnmount() {
CopyNoteStore.unlisten(this.onChange);
}
render() {
const children = this.state.notes.map((note, i) => {
return (
<NoteBox key={i} parentReference={note.id} version={this.props.version} currentlySelected={this.state.selection === note.id ? true : false} contentUuid={this.props.contentUuid}>
<SingleNote parentReference={note.id} contents={note.contents} currentlySelected={this.state.selection === note.id ? true : false} user={note.user} email={note.email} version={note.version} created_at={note.created_at} />
</NoteBox>
);
});
return <div>{children}</div>;
}
}
const FullWidthDivider = () => {
return (
<hr style={{
margin: '-1px -10px 20px -10px',
backgroundColor: 'rgb(224, 224, 224)',
height: '1px',
border: 'none'
}} />
)
}
export {NoteViewer as default, NoteBox, SingleNote, CommentBox};
// export default NoteViewer;