diff --git a/browser/main/HomePage/ArticleDetail.js b/browser/main/HomePage/ArticleDetail.js index 6ae52e7d..12fbe9a2 100644 --- a/browser/main/HomePage/ArticleDetail.js +++ b/browser/main/HomePage/ArticleDetail.js @@ -5,7 +5,20 @@ 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 { + IDLE_MODE, + CREATE_MODE, + EDIT_MODE, + switchMode, + switchArticle, + switchFolder, + clearSearch, + lockStatus, + unlockStatus, + updateArticle, + destroyArticle, + NEW +} from 'boost/actions' import linkState from 'boost/linkState' import FolderMark from 'boost/components/FolderMark' import TagLink from 'boost/components/TagLink' @@ -82,7 +95,12 @@ export default class ArticleDetail extends React.Component { this.state = { article: makeInstantArticle(props.activeArticle), - previewMode: false + previewMode: false, + isArticleEdited: false, + isTagChanged: false, + isTitleChanged: false, + isContentChanged: false, + isModeChanged: false } } @@ -117,7 +135,11 @@ export default class ArticleDetail extends React.Component { if (isModeChanged) { Object.assign(nextState, { openDeleteConfirmMenu: false, - previewMode: false + previewMode: false, + isArticleEdited: false, + isTagChanged: false, + isTitleChanged: false, + isContentChanged: false }) } @@ -224,6 +246,8 @@ export default class ArticleDetail extends React.Component { handleCancelButtonClick (e) { let { activeArticle, dispatch } = this.props + + dispatch(unlockStatus()) if (activeArticle.status === NEW) dispatch(switchArticle(null)) dispatch(switchMode(IDLE_MODE)) } @@ -236,6 +260,8 @@ export default class ArticleDetail extends React.Component { let folder = _.findWhere(folders, {key: article.FolderKey}) if (folder == null) return false + dispatch(unlockStatus()) + delete newArticle.status newArticle.updatedAt = new Date() if (newArticle.createdAt == null) { @@ -263,19 +289,85 @@ export default class ArticleDetail extends React.Component { this.setState({article: article}) } + handleTitleChange (e) { + let { article } = this.state + article.title = e.target.value + let _isTitleChanged = article.title !== this.props.activeArticle.title + + let { isTagChanged, isContentChanged, isArticleEdited, isModeChanged } = this.state + let _isArticleEdited = _isTitleChanged || isTagChanged || isContentChanged || isModeChanged + + this.setState({ + article, + isTitleChanged: _isTitleChanged, + isArticleEdited: _isArticleEdited + }, () => { + if (isArticleEdited !== _isArticleEdited) { + let { dispatch } = this.props + if (_isArticleEdited) { + console.log('lockit') + dispatch(lockStatus()) + } else { + console.log('unlockit') + dispatch(unlockStatus()) + } + } + }) + } + handleTagsChange (newTag, tags) { let article = this.state.article article.tags = tags this.setState({article: article}) + + let _isTagChanged = _.difference(article.tags, this.props.activeArticle.tags).length > 0 || _.difference(this.props.activeArticle.tags, article.tags).length > 0 + + let { isTitleChanged, isContentChanged, isArticleEdited, isModeChanged } = this.state + let _isArticleEdited = _isTagChanged || isTitleChanged || isContentChanged || isModeChanged + + this.setState({ + article, + isTagChanged: _isTagChanged, + isArticleEdited: _isArticleEdited + }, () => { + if (isArticleEdited !== _isArticleEdited) { + let { dispatch } = this.props + if (_isArticleEdited) { + console.log('lockit') + dispatch(lockStatus()) + } else { + console.log('unlockit') + dispatch(unlockStatus()) + } + } + }) } handleModeChange (value) { - let article = this.state.article + let { article } = this.state article.mode = value + let _isModeChanged = article.mode !== this.props.activeArticle.mode + + let { isTagChanged, isContentChanged, isArticleEdited, isTitleChanged } = this.state + let _isArticleEdited = _isModeChanged || isTagChanged || isContentChanged || isTitleChanged + this.setState({ - article: article, - previewMode: false + article, + previewMode: false, + isModeChanged: _isModeChanged, + isArticleEdited: _isArticleEdited + }, () => { + if (isArticleEdited !== _isArticleEdited) { + let { dispatch } = this.props + if (_isArticleEdited) { + console.log('lockit') + dispatch(lockStatus()) + } else { + console.log('unlockit') + dispatch(unlockStatus()) + } + } }) } @@ -286,9 +378,29 @@ export default class ArticleDetail extends React.Component { } handleContentChange (e, value) { - let article = this.state.article + let { article } = this.state article.content = value - this.setState({article: article}) + let _isContentChanged = article.content !== this.props.activeArticle.content + + let { isTagChanged, isModeChanged, isArticleEdited, isTitleChanged } = this.state + let _isArticleEdited = _isContentChanged || isTagChanged || isModeChanged || isTitleChanged + + this.setState({ + article, + isContentChanged: _isContentChanged, + isArticleEdited: _isArticleEdited + }, () => { + if (isArticleEdited !== _isArticleEdited) { + let { dispatch } = this.props + if (_isArticleEdited) { + console.log('lockit') + dispatch(lockStatus()) + } else { + console.log('unlockit') + dispatch(unlockStatus()) + } + } + }) } handleTogglePreviewButtonClick (e) { @@ -322,6 +434,7 @@ export default class ArticleDetail extends React.Component { > {folderOptions} + {this.state.isArticleEdited ? ' (edited)' : ''}
- this.handleTitleKeyDown(e)} placeholder='Title' ref='title' valueLink={this.linkState('article.title')}/> + this.handleTitleKeyDown(e)} placeholder='Title' ref='title' value={this.state.article.title} onChange={e => this.handleTitleChange(e)}/>
+
Your article is still editing!
+ +
Do you really want to leave without finishing?
+ +
+ + +
+
+ ) + } +} + +EditedAlert.propTypes = { + action: PropTypes.object, + close: PropTypes.func +} diff --git a/lib/reducer.js b/lib/reducer.js index 5b091872..baff2d55 100644 --- a/lib/reducer.js +++ b/lib/reducer.js @@ -1,14 +1,42 @@ import { combineReducers } from 'redux' import _ from 'lodash' -import { SWITCH_FOLDER, SWITCH_MODE, SWITCH_ARTICLE, SET_SEARCH_FILTER, SET_TAG_FILTER, CLEAR_SEARCH, TOGGLE_TUTORIAL, ARTICLE_UPDATE, ARTICLE_DESTROY, FOLDER_CREATE, FOLDER_UPDATE, FOLDER_DESTROY, FOLDER_REPLACE, IDLE_MODE, CREATE_MODE } from './actions' +import { + // Status action type + SWITCH_FOLDER, + SWITCH_MODE, + SWITCH_ARTICLE, + SET_SEARCH_FILTER, + SET_TAG_FILTER, + CLEAR_SEARCH, + LOCK_STATUS, + UNLOCK_STATUS, + TOGGLE_TUTORIAL, + + // Article action type + ARTICLE_UPDATE, + ARTICLE_DESTROY, + + // Folder action type + FOLDER_CREATE, + FOLDER_UPDATE, + FOLDER_DESTROY, + FOLDER_REPLACE, + + // view mode + IDLE_MODE, + CREATE_MODE +} from './actions' import dataStore from 'boost/dataStore' import keygen from 'boost/keygen' import activityRecord from 'boost/activityRecord' +import { openModal } from 'boost/modal' +import EditedAlert from 'boost/components/modal/EditedAlert' const initialStatus = { mode: IDLE_MODE, search: '', - isTutorialOpen: false + isTutorialOpen: false, + isStatusLocked: false } let data = dataStore.getData() @@ -134,10 +162,25 @@ function articles (state = initialArticles, action) { function status (state = initialStatus, action) { state = Object.assign({}, state) + switch (action.type) { case TOGGLE_TUTORIAL: state.isTutorialOpen = !state.isTutorialOpen return state + case LOCK_STATUS: + state.isStatusLocked = true + return state + case UNLOCK_STATUS: + state.isStatusLocked = false + return state + } + + // if status locked, status become unmutable + if (state.isStatusLocked) { + openModal(EditedAlert, {action}) + return state + } + switch (action.type) { case SWITCH_FOLDER: state.mode = IDLE_MODE state.search = `//${action.data} `