mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 09:46:22 +00:00
記事が編集された状態で他の記事を見ようとすると警告をだす
This commit is contained in:
@@ -5,7 +5,20 @@ import _ from 'lodash'
|
|||||||
import ModeIcon from 'boost/components/ModeIcon'
|
import ModeIcon from 'boost/components/ModeIcon'
|
||||||
import MarkdownPreview from 'boost/components/MarkdownPreview'
|
import MarkdownPreview from 'boost/components/MarkdownPreview'
|
||||||
import CodeEditor from 'boost/components/CodeEditor'
|
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 linkState from 'boost/linkState'
|
||||||
import FolderMark from 'boost/components/FolderMark'
|
import FolderMark from 'boost/components/FolderMark'
|
||||||
import TagLink from 'boost/components/TagLink'
|
import TagLink from 'boost/components/TagLink'
|
||||||
@@ -82,7 +95,12 @@ export default class ArticleDetail extends React.Component {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
article: makeInstantArticle(props.activeArticle),
|
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) {
|
if (isModeChanged) {
|
||||||
Object.assign(nextState, {
|
Object.assign(nextState, {
|
||||||
openDeleteConfirmMenu: false,
|
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) {
|
handleCancelButtonClick (e) {
|
||||||
let { activeArticle, dispatch } = this.props
|
let { activeArticle, dispatch } = this.props
|
||||||
|
|
||||||
|
dispatch(unlockStatus())
|
||||||
if (activeArticle.status === NEW) dispatch(switchArticle(null))
|
if (activeArticle.status === NEW) dispatch(switchArticle(null))
|
||||||
dispatch(switchMode(IDLE_MODE))
|
dispatch(switchMode(IDLE_MODE))
|
||||||
}
|
}
|
||||||
@@ -236,6 +260,8 @@ export default class ArticleDetail extends React.Component {
|
|||||||
let folder = _.findWhere(folders, {key: article.FolderKey})
|
let folder = _.findWhere(folders, {key: article.FolderKey})
|
||||||
if (folder == null) return false
|
if (folder == null) return false
|
||||||
|
|
||||||
|
dispatch(unlockStatus())
|
||||||
|
|
||||||
delete newArticle.status
|
delete newArticle.status
|
||||||
newArticle.updatedAt = new Date()
|
newArticle.updatedAt = new Date()
|
||||||
if (newArticle.createdAt == null) {
|
if (newArticle.createdAt == null) {
|
||||||
@@ -263,19 +289,85 @@ export default class ArticleDetail extends React.Component {
|
|||||||
this.setState({article: article})
|
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) {
|
handleTagsChange (newTag, tags) {
|
||||||
let article = this.state.article
|
let article = this.state.article
|
||||||
article.tags = tags
|
article.tags = tags
|
||||||
|
|
||||||
this.setState({article: article})
|
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) {
|
handleModeChange (value) {
|
||||||
let article = this.state.article
|
let { article } = this.state
|
||||||
article.mode = value
|
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({
|
this.setState({
|
||||||
article: article,
|
article,
|
||||||
previewMode: false
|
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) {
|
handleContentChange (e, value) {
|
||||||
let article = this.state.article
|
let { article } = this.state
|
||||||
article.content = value
|
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) {
|
handleTogglePreviewButtonClick (e) {
|
||||||
@@ -322,6 +434,7 @@ export default class ArticleDetail extends React.Component {
|
|||||||
>
|
>
|
||||||
{folderOptions}
|
{folderOptions}
|
||||||
</select>
|
</select>
|
||||||
|
{this.state.isArticleEdited ? ' (edited)' : ''}
|
||||||
|
|
||||||
<TagSelect
|
<TagSelect
|
||||||
tags={this.state.article.tags}
|
tags={this.state.article.tags}
|
||||||
@@ -347,7 +460,7 @@ export default class ArticleDetail extends React.Component {
|
|||||||
<div className='detailPanel'>
|
<div className='detailPanel'>
|
||||||
<div className='header'>
|
<div className='header'>
|
||||||
<div className='title'>
|
<div className='title'>
|
||||||
<input onKeyDown={e => this.handleTitleKeyDown(e)} placeholder='Title' ref='title' valueLink={this.linkState('article.title')}/>
|
<input onKeyDown={e => this.handleTitleKeyDown(e)} placeholder='Title' ref='title' value={this.state.article.title} onChange={e => this.handleTitleChange(e)}/>
|
||||||
</div>
|
</div>
|
||||||
<ModeSelect
|
<ModeSelect
|
||||||
ref='mode'
|
ref='mode'
|
||||||
@@ -396,6 +509,7 @@ export default class ArticleDetail extends React.Component {
|
|||||||
ArticleDetail.propTypes = {
|
ArticleDetail.propTypes = {
|
||||||
status: PropTypes.shape(),
|
status: PropTypes.shape(),
|
||||||
activeArticle: PropTypes.shape(),
|
activeArticle: PropTypes.shape(),
|
||||||
activeUser: PropTypes.shape()
|
activeUser: PropTypes.shape(),
|
||||||
|
dispatch: PropTypes.func
|
||||||
}
|
}
|
||||||
ArticleDetail.prototype.linkState = linkState
|
ArticleDetail.prototype.linkState = linkState
|
||||||
|
|||||||
@@ -9,3 +9,4 @@
|
|||||||
@require './lib/CreateNewFolder'
|
@require './lib/CreateNewFolder'
|
||||||
@require './lib/Preferences'
|
@require './lib/Preferences'
|
||||||
@require './lib/Tutorial'
|
@require './lib/Tutorial'
|
||||||
|
@require './lib/EditedAlert'
|
||||||
|
|||||||
28
browser/styles/main/HomeContainer/lib/EditedAlert.styl
Normal file
28
browser/styles/main/HomeContainer/lib/EditedAlert.styl
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
.EditedAlert.modal
|
||||||
|
width 350px
|
||||||
|
top 100px
|
||||||
|
.title
|
||||||
|
font-size 24px
|
||||||
|
margin-bottom 15px
|
||||||
|
.message
|
||||||
|
font-size 14px
|
||||||
|
margin-bottom 15px
|
||||||
|
.control
|
||||||
|
text-align right
|
||||||
|
button
|
||||||
|
border-radius 5px
|
||||||
|
height 33px
|
||||||
|
padding 0 15px
|
||||||
|
font-size 14px
|
||||||
|
background-color white
|
||||||
|
border 1px solid borderColor
|
||||||
|
border-radius 5px
|
||||||
|
margin-left 5px
|
||||||
|
&:hover
|
||||||
|
background-color darken(white, 10%)
|
||||||
|
&.primary
|
||||||
|
border-color brandColor
|
||||||
|
background-color brandColor
|
||||||
|
color white
|
||||||
|
&:hover
|
||||||
|
background-color lighten(brandColor, 10%)
|
||||||
@@ -12,6 +12,8 @@ export const SWITCH_ARTICLE = 'SWITCH_ARTICLE'
|
|||||||
export const SET_SEARCH_FILTER = 'SET_SEARCH_FILTER'
|
export const SET_SEARCH_FILTER = 'SET_SEARCH_FILTER'
|
||||||
export const SET_TAG_FILTER = 'SET_TAG_FILTER'
|
export const SET_TAG_FILTER = 'SET_TAG_FILTER'
|
||||||
export const CLEAR_SEARCH = 'CLEAR_SEARCH'
|
export const CLEAR_SEARCH = 'CLEAR_SEARCH'
|
||||||
|
export const LOCK_STATUS = 'LOCK_STATUS'
|
||||||
|
export const UNLOCK_STATUS = 'UNLOCK_STATUS'
|
||||||
export const TOGGLE_TUTORIAL = 'TOGGLE_TUTORIAL'
|
export const TOGGLE_TUTORIAL = 'TOGGLE_TUTORIAL'
|
||||||
|
|
||||||
// Status - mode
|
// Status - mode
|
||||||
@@ -109,7 +111,19 @@ export function clearSearch () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toggleTutorial() {
|
export function lockStatus () {
|
||||||
|
return {
|
||||||
|
type: LOCK_STATUS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unlockStatus () {
|
||||||
|
return {
|
||||||
|
type: UNLOCK_STATUS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggleTutorial () {
|
||||||
return {
|
return {
|
||||||
type: TOGGLE_TUTORIAL
|
type: TOGGLE_TUTORIAL
|
||||||
}
|
}
|
||||||
|
|||||||
35
lib/components/modal/EditedAlert.js
Normal file
35
lib/components/modal/EditedAlert.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import React, { PropTypes } from 'react'
|
||||||
|
import store from 'boost/store'
|
||||||
|
import { unlockStatus } from 'boost/actions'
|
||||||
|
|
||||||
|
export default class EditedAlert extends React.Component {
|
||||||
|
handleNoButtonClick (e) {
|
||||||
|
this.props.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
handleYesButtonClick (e) {
|
||||||
|
store.dispatch(unlockStatus())
|
||||||
|
store.dispatch(this.props.action)
|
||||||
|
this.props.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div className='EditedAlert modal'>
|
||||||
|
<div className='title'>Your article is still editing!</div>
|
||||||
|
|
||||||
|
<div className='message'>Do you really want to leave without finishing?</div>
|
||||||
|
|
||||||
|
<div className='control'>
|
||||||
|
<button onClick={e => this.handleNoButtonClick(e)}><i className='fa fa-fw fa-close'/> No</button>
|
||||||
|
<button onClick={e => this.handleYesButtonClick(e)} className='primary'><i className='fa fa-fw fa-check'/> Yes</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditedAlert.propTypes = {
|
||||||
|
action: PropTypes.object,
|
||||||
|
close: PropTypes.func
|
||||||
|
}
|
||||||
@@ -1,14 +1,42 @@
|
|||||||
import { combineReducers } from 'redux'
|
import { combineReducers } from 'redux'
|
||||||
import _ from 'lodash'
|
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 dataStore from 'boost/dataStore'
|
||||||
import keygen from 'boost/keygen'
|
import keygen from 'boost/keygen'
|
||||||
import activityRecord from 'boost/activityRecord'
|
import activityRecord from 'boost/activityRecord'
|
||||||
|
import { openModal } from 'boost/modal'
|
||||||
|
import EditedAlert from 'boost/components/modal/EditedAlert'
|
||||||
|
|
||||||
const initialStatus = {
|
const initialStatus = {
|
||||||
mode: IDLE_MODE,
|
mode: IDLE_MODE,
|
||||||
search: '',
|
search: '',
|
||||||
isTutorialOpen: false
|
isTutorialOpen: false,
|
||||||
|
isStatusLocked: false
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = dataStore.getData()
|
let data = dataStore.getData()
|
||||||
@@ -134,10 +162,25 @@ function articles (state = initialArticles, action) {
|
|||||||
|
|
||||||
function status (state = initialStatus, action) {
|
function status (state = initialStatus, action) {
|
||||||
state = Object.assign({}, state)
|
state = Object.assign({}, state)
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case TOGGLE_TUTORIAL:
|
case TOGGLE_TUTORIAL:
|
||||||
state.isTutorialOpen = !state.isTutorialOpen
|
state.isTutorialOpen = !state.isTutorialOpen
|
||||||
return state
|
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:
|
case SWITCH_FOLDER:
|
||||||
state.mode = IDLE_MODE
|
state.mode = IDLE_MODE
|
||||||
state.search = `//${action.data} `
|
state.search = `//${action.data} `
|
||||||
|
|||||||
Reference in New Issue
Block a user