import React, { PropTypes } from 'react' import ReactDOM from 'react-dom' import moment from 'moment' import _ from 'lodash' import MarkdownPreview from 'browser/components/MarkdownPreview' import CodeEditor from 'browser/components/CodeEditor' import { switchFolder, cacheArticle, saveArticle } from '../../actions' import linkState from 'browser/lib/linkState' import TagSelect from 'browser/components/TagSelect' import ModeSelect from 'browser/components/ModeSelect' import activityRecord from 'browser/lib/activityRecord' import ShareButton from './ShareButton' import { openModal } from 'browser/lib/modal' import DeleteArticleModal from '../../modal/DeleteArticleModal' const electron = require('electron') const clipboard = electron.clipboard const OSX = process.platform === 'darwin' 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 notify (...args) { return new window.Notification(...args) } export default class ArticleDetail extends React.Component { constructor (props) { super(props) this.state = { article: Object.assign({content: ''}, props.activeArticle), previewMode: false, isArticleEdited: false, isTagChanged: false, isTitleChanged: false, isContentChanged: false, isModeChanged: false, openShareDropdown: false } if (props.activeArticle != null && props.activeArticle.content.trim().length > 0 && props.activeArticle.mode === 'markdown') this.state.previewMode = true } componentDidMount () { this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000) this.shareDropdownInterceptor = e => { e.stopPropagation() } } componentWillUnmount () { clearInterval(this.refreshTimer) } componentWillReceiveProps (nextProps) { if (nextProps.activeArticle == null || this.props.activeArticle == null || nextProps.activeArticle.key !== this.props.activeArticle.key) { let nextArticle = nextProps.activeArticle let nextModified = nextArticle != null ? _.findWhere(nextProps.modified, {key: nextArticle.key}) : null let article = Object.assign({content: ''}, nextProps.activeArticle, nextModified) let nextState = { article, previewMode: false } if (article.content.trim().length > 0 && article.mode === 'markdown') { nextState.previewMode = true } this.setState(nextState) } } cacheArticle () { let { dispatch } = this.props dispatch(cacheArticle(this.props.activeArticle.key, this.state.article)) } renderEmpty () { return (
Command(⌘) + N
to create a new post
) } handleClipboardButtonClick (e) { activityRecord.emit('MAIN_DETAIL_COPY') clipboard.writeText(this.props.activeArticle.content) notify('Saved to Clipboard!', { body: 'Paste it wherever you want!' }) } handleSaveButtonClick (e) { let { dispatch, folders, status } = this.props let targetFolderKey = this.state.article.FolderKey dispatch(saveArticle(this.props.activeArticle.key, this.state.article), true) if (status.targetFolders.length > 0) { let targetFolder = _.findWhere(folders, {key: targetFolderKey}) dispatch(switchFolder(targetFolder.name)) } } handleFolderKeyChange (e) { let article = this.state.article article.FolderKey = e.target.value this.setState({article: article}, () => this.cacheArticle()) } handleTitleChange (e) { let { article } = this.state article.title = e.target.value this.setState({ article }, () => this.cacheArticle()) } handleTagsChange (newTag, tags) { let article = this.state.article article.tags = tags this.setState({ article }, () => this.cacheArticle()) } handleModeChange (value) { let { article } = this.state article.mode = value this.setState({ article }, () => this.cacheArticle()) } handleModeSelectBlur () { if (this.refs.code != null) { this.refs.code.editor.focus() } } handleContentChange (e, value) { let { article } = this.state article.content = value this.setState({ article }, () => this.cacheArticle()) } handleTogglePreviewButtonClick (e) { if (this.state.article.mode === 'markdown') { if (!this.state.previewMode) { let cursorPosition = this.refs.code.getCursorPosition() let firstVisibleRow = this.refs.code.getFirstVisibleRow() this.setState({ previewMode: true, cursorPosition, firstVisibleRow }, function () { let previewEl = ReactDOM.findDOMNode(this.refs.preview) let anchors = previewEl.querySelectorAll('.lineAnchor') for (let i = 0; i < anchors.length; i++) { if (parseInt(anchors[i].dataset.key, 10) > cursorPosition.row || i === anchors.length - 1) { var targetAnchor = anchors[i > 0 ? i - 1 : 0] previewEl.scrollTop = targetAnchor.offsetTop - 100 break } } }) } else { this.setState({ previewMode: false }, function () { if (this.state.cursorPosition == null) return true this.refs.code.moveCursorTo(this.state.cursorPosition.row, this.state.cursorPosition.column) this.refs.code.scrollToLine(this.state.firstVisibleRow) this.refs.code.editor.focus() }) } } } handleDeleteButtonClick (e) { if (this.props.activeArticle) { openModal(DeleteArticleModal, {articleKey: this.props.activeArticle.key}) } } handleTitleKeyDown (e) { if (e.keyCode === 9 && !e.shiftKey) { e.preventDefault() this.refs.mode.handleIdleSelectClick() } } handlePreviewButtonDoubleClick (e) { this.setState({ previewMode: false }) } render () { let { folders, status, tags, activeArticle, modified, user } = this.props if (activeArticle == null) return this.renderEmpty() let folderOptions = folders.map(folder => { return ( ) }) let isUnsaved = !!_.findWhere(modified, {key: activeArticle.key}) return (
Unsaved : `Created : ${moment(this.state.article.createdAt).format('YYYY/MM/DD')} Updated : ${moment(this.state.article.updatedAt).format('YYYY/MM/DD')}` } />
{ this.state.article.mode === 'markdown' ? : null }
{status.isTutorialOpen ? editDeleteTutorialElement : null}
this.handleTagsChange(tags, tag)} suggestTags={tags} /> {status.isTutorialOpen ? tagSelectTutorialElement : null}
this.handleTitleKeyDown(e)} placeholder='(Untitled)' ref='title' value={this.state.article.title} onChange={e => this.handleTitleChange(e)} />
this.handleModeChange(e)} value={this.state.article.mode} className='ArticleDetail-panel-header-mode' onBlur={() => this.handleModeSelectBlur()} />
{status.isTutorialOpen ? modeSelectTutorialElement : null} {this.state.previewMode ? this.handlePreviewButtonDoubleClick(e)} content={this.state.article.content}/> : ( this.handleContentChange(e, value)} readOnly={false} mode={this.state.article.mode} code={this.state.article.content} />) }
) } } ArticleDetail.propTypes = { status: PropTypes.shape(), activeArticle: PropTypes.shape(), modified: PropTypes.array, user: PropTypes.shape(), folders: PropTypes.array, tags: PropTypes.array, dispatch: PropTypes.func } ArticleDetail.prototype.linkState = linkState