import React, { PropTypes } from 'react' import ReactDOM from 'react-dom' import moment from 'moment' import _ from 'lodash' import ModeIcon from 'boost/components/ModeIcon' import MarkdownPreview from 'boost/components/MarkdownPreview' import CodeEditor from 'boost/components/CodeEditor' import { IDLE_MODE, CREATE_MODE, EDIT_MODE, switchMode, switchArticle, switchFolder, clearSearch, updateArticle, destroyArticle, NEW } from 'boost/actions' import linkState from 'boost/linkState' import FolderMark from 'boost/components/FolderMark' import TagLink from 'boost/components/TagLink' import TagSelect from 'boost/components/TagSelect' import ModeSelect from 'boost/components/ModeSelect' import activityRecord from 'boost/activityRecord' const BRAND_COLOR = '#18AF90' const editDeleteTutorialElement = ( Edit / Delete a post press `e`/`d` ) const tagSelectTutorialElement = ( Attach some tags here! ) const modeSelectTutorialElement = ( Select code syntax!! ) function makeInstantArticle (article) { return Object.assign({}, article) } export default class ArticleDetail extends React.Component { constructor (props) { super(props) this.state = { article: makeInstantArticle(props.activeArticle), previewMode: false } } componentDidMount () { this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000) } componentWillUnmount () { clearInterval(this.refreshTimer) } componentDidUpdate (prevProps) { let isModeChanged = prevProps.status.mode !== this.props.status.mode if (isModeChanged && this.props.status.mode !== IDLE_MODE) { ReactDOM.findDOMNode(this.refs.title).focus() } } componentWillReceiveProps (nextProps) { let nextState = {} let isArticleChanged = nextProps.activeArticle != null && (nextProps.activeArticle.key !== this.state.article.key) let isModeChanged = nextProps.status.mode !== this.props.status.mode // Reset article input if (isArticleChanged || (isModeChanged && nextProps.status.mode !== IDLE_MODE)) { Object.assign(nextState, { article: makeInstantArticle(nextProps.activeArticle) }) } // Clean state if (isModeChanged) { Object.assign(nextState, { openDeleteConfirmMenu: false, previewMode: false }) } this.setState(nextState) } renderEmpty () { return (
Command(⌘) + Enter to create a new post
) } handleEditButtonClick (e) { let { dispatch } = this.props dispatch(switchMode(EDIT_MODE)) } handleDeleteButtonClick (e) { this.setState({openDeleteConfirmMenu: true}) } handleDeleteConfirmButtonClick (e) { let { dispatch, activeArticle } = this.props dispatch(destroyArticle(activeArticle.key)) activityRecord.emit('ARTICLE_DESTROY') this.setState({openDeleteConfirmMenu: false}) } handleDeleteCancelButtonClick (e) { this.setState({openDeleteConfirmMenu: false}) } renderIdle () { let { status, activeArticle, folders } = this.props let tags = activeArticle.tags != null ? activeArticle.tags.length > 0 ? activeArticle.tags.map(tag => { return () }) : ( Not tagged yet ) : null let folder = _.findWhere(folders, {key: activeArticle.FolderKey}) return (
{this.state.openDeleteConfirmMenu ? (
Are you sure to delete this article?
) : (
{folder.name}  Created : {moment(activeArticle.createdAt).format('YYYY/MM/DD')}  Updated : {moment(activeArticle.updatedAt).format('YYYY/MM/DD')}
{tags}
{status.isTutorialOpen ? editDeleteTutorialElement : null}
) }
{activeArticle.title}
{activeArticle.mode === 'markdown' ? : this.handleContentChange(e, value)} mode={activeArticle.mode} code={activeArticle.content}/> }
) } handleCancelButtonClick (e) { let { activeArticle, dispatch } = this.props if (activeArticle.status === NEW) dispatch(switchArticle(null)) dispatch(switchMode(IDLE_MODE)) } handleSaveButtonClick (e) { let { dispatch, folders, filters } = this.props let article = this.state.article let newArticle = Object.assign({}, article) let folder = _.findWhere(folders, {key: article.FolderKey}) if (folder == null) return false delete newArticle.status newArticle.updatedAt = new Date() if (newArticle.createdAt == null) { newArticle.createdAt = new Date() activityRecord.emit('ARTICLE_CREATE') } else { activityRecord.emit('ARTICLE_UPDATE') } dispatch(updateArticle(newArticle)) dispatch(switchMode(IDLE_MODE)) // Folder filterがかかっている時に、 // Searchを初期化し、更新先のFolder filterをかける // かかれていない時に // Searchを初期化する if (filters.folder.length !== 0) dispatch(switchFolder(folder.name)) else dispatch(clearSearch()) dispatch(switchArticle(newArticle.key)) } handleFolderKeyChange (e) { let article = this.state.article article.FolderKey = e.target.value this.setState({article: article}) } handleTagsChange (newTag, tags) { let article = this.state.article article.tags = tags this.setState({article: article}) } handleModeChange (value) { let article = this.state.article article.mode = value this.setState({ article: article, previewMode: false }) } handleModeSelectBlur () { if (this.refs.code != null) { this.refs.code.editor.focus() } } handleContentChange (e, value) { let article = this.state.article article.content = value this.setState({article: article}) } handleTogglePreviewButtonClick (e) { this.setState({previewMode: !this.state.previewMode}) } handleTitleKeyDown (e) { if (e.keyCode === 9 && !e.shiftKey) { e.preventDefault() this.refs.mode.handleIdleSelectClick() } } renderEdit () { let { folders, status, tags } = this.props let folderOptions = folders.map(folder => { return ( ) }) return (
this.handleTagsChange(tags, tag)} suggestTags={tags} /> {status.isTutorialOpen ? tagSelectTutorialElement : null}
{ this.state.article.mode === 'markdown' ? () : null }
this.handleTitleKeyDown(e)} placeholder='Title' ref='title' valueLink={this.linkState('article.title')}/>
this.handleModeChange(e)} value={this.state.article.mode} className='mode' onBlur={() => this.handleModeSelectBlur()} /> {status.isTutorialOpen ? modeSelectTutorialElement : null}
{this.state.previewMode ? : ( this.handleContentChange(e, value)} readOnly={false} mode={this.state.article.mode} code={this.state.article.content} />) }
) } render () { let { status, activeArticle } = this.props if (activeArticle == null) return this.renderEmpty() switch (status.mode) { case CREATE_MODE: case EDIT_MODE: return this.renderEdit() case IDLE_MODE: default: return this.renderIdle() } } } ArticleDetail.propTypes = { status: PropTypes.shape(), activeArticle: PropTypes.shape(), activeUser: PropTypes.shape() } ArticleDetail.prototype.linkState = linkState