Skip to content

Commit 013f96a

Browse files
committed
記事が編集された状態で他の記事を見ようとすると警告をだす
1 parent df6a018 commit 013f96a

6 files changed

Lines changed: 248 additions & 13 deletions

File tree

browser/main/HomePage/ArticleDetail.js

Lines changed: 124 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,20 @@ import _ from 'lodash'
55
import ModeIcon from 'boost/components/ModeIcon'
66
import MarkdownPreview from 'boost/components/MarkdownPreview'
77
import CodeEditor from 'boost/components/CodeEditor'
8-
import { IDLE_MODE, CREATE_MODE, EDIT_MODE, switchMode, switchArticle, switchFolder, clearSearch, updateArticle, destroyArticle, NEW } from 'boost/actions'
8+
import {
9+
IDLE_MODE,
10+
CREATE_MODE,
11+
EDIT_MODE,
12+
switchMode,
13+
switchArticle,
14+
switchFolder,
15+
clearSearch,
16+
lockStatus,
17+
unlockStatus,
18+
updateArticle,
19+
destroyArticle,
20+
NEW
21+
} from 'boost/actions'
922
import linkState from 'boost/linkState'
1023
import FolderMark from 'boost/components/FolderMark'
1124
import TagLink from 'boost/components/TagLink'
@@ -82,7 +95,12 @@ export default class ArticleDetail extends React.Component {
8295

8396
this.state = {
8497
article: makeInstantArticle(props.activeArticle),
85-
previewMode: false
98+
previewMode: false,
99+
isArticleEdited: false,
100+
isTagChanged: false,
101+
isTitleChanged: false,
102+
isContentChanged: false,
103+
isModeChanged: false
86104
}
87105
}
88106

@@ -117,7 +135,11 @@ export default class ArticleDetail extends React.Component {
117135
if (isModeChanged) {
118136
Object.assign(nextState, {
119137
openDeleteConfirmMenu: false,
120-
previewMode: false
138+
previewMode: false,
139+
isArticleEdited: false,
140+
isTagChanged: false,
141+
isTitleChanged: false,
142+
isContentChanged: false
121143
})
122144
}
123145

@@ -224,6 +246,8 @@ export default class ArticleDetail extends React.Component {
224246

225247
handleCancelButtonClick (e) {
226248
let { activeArticle, dispatch } = this.props
249+
250+
dispatch(unlockStatus())
227251
if (activeArticle.status === NEW) dispatch(switchArticle(null))
228252
dispatch(switchMode(IDLE_MODE))
229253
}
@@ -236,6 +260,8 @@ export default class ArticleDetail extends React.Component {
236260
let folder = _.findWhere(folders, {key: article.FolderKey})
237261
if (folder == null) return false
238262

263+
dispatch(unlockStatus())
264+
239265
delete newArticle.status
240266
newArticle.updatedAt = new Date()
241267
if (newArticle.createdAt == null) {
@@ -263,19 +289,85 @@ export default class ArticleDetail extends React.Component {
263289
this.setState({article: article})
264290
}
265291

292+
handleTitleChange (e) {
293+
let { article } = this.state
294+
article.title = e.target.value
295+
let _isTitleChanged = article.title !== this.props.activeArticle.title
296+
297+
let { isTagChanged, isContentChanged, isArticleEdited, isModeChanged } = this.state
298+
let _isArticleEdited = _isTitleChanged || isTagChanged || isContentChanged || isModeChanged
299+
300+
this.setState({
301+
article,
302+
isTitleChanged: _isTitleChanged,
303+
isArticleEdited: _isArticleEdited
304+
}, () => {
305+
if (isArticleEdited !== _isArticleEdited) {
306+
let { dispatch } = this.props
307+
if (_isArticleEdited) {
308+
console.log('lockit')
309+
dispatch(lockStatus())
310+
} else {
311+
console.log('unlockit')
312+
dispatch(unlockStatus())
313+
}
314+
}
315+
})
316+
}
317+
266318
handleTagsChange (newTag, tags) {
267319
let article = this.state.article
268320
article.tags = tags
269321

270322
this.setState({article: article})
323+
324+
let _isTagChanged = _.difference(article.tags, this.props.activeArticle.tags).length > 0 || _.difference(this.props.activeArticle.tags, article.tags).length > 0
325+
326+
let { isTitleChanged, isContentChanged, isArticleEdited, isModeChanged } = this.state
327+
let _isArticleEdited = _isTagChanged || isTitleChanged || isContentChanged || isModeChanged
328+
329+
this.setState({
330+
article,
331+
isTagChanged: _isTagChanged,
332+
isArticleEdited: _isArticleEdited
333+
}, () => {
334+
if (isArticleEdited !== _isArticleEdited) {
335+
let { dispatch } = this.props
336+
if (_isArticleEdited) {
337+
console.log('lockit')
338+
dispatch(lockStatus())
339+
} else {
340+
console.log('unlockit')
341+
dispatch(unlockStatus())
342+
}
343+
}
344+
})
271345
}
272346

273347
handleModeChange (value) {
274-
let article = this.state.article
348+
let { article } = this.state
275349
article.mode = value
350+
let _isModeChanged = article.mode !== this.props.activeArticle.mode
351+
352+
let { isTagChanged, isContentChanged, isArticleEdited, isTitleChanged } = this.state
353+
let _isArticleEdited = _isModeChanged || isTagChanged || isContentChanged || isTitleChanged
354+
276355
this.setState({
277-
article: article,
278-
previewMode: false
356+
article,
357+
previewMode: false,
358+
isModeChanged: _isModeChanged,
359+
isArticleEdited: _isArticleEdited
360+
}, () => {
361+
if (isArticleEdited !== _isArticleEdited) {
362+
let { dispatch } = this.props
363+
if (_isArticleEdited) {
364+
console.log('lockit')
365+
dispatch(lockStatus())
366+
} else {
367+
console.log('unlockit')
368+
dispatch(unlockStatus())
369+
}
370+
}
279371
})
280372
}
281373

@@ -286,9 +378,29 @@ export default class ArticleDetail extends React.Component {
286378
}
287379

288380
handleContentChange (e, value) {
289-
let article = this.state.article
381+
let { article } = this.state
290382
article.content = value
291-
this.setState({article: article})
383+
let _isContentChanged = article.content !== this.props.activeArticle.content
384+
385+
let { isTagChanged, isModeChanged, isArticleEdited, isTitleChanged } = this.state
386+
let _isArticleEdited = _isContentChanged || isTagChanged || isModeChanged || isTitleChanged
387+
388+
this.setState({
389+
article,
390+
isContentChanged: _isContentChanged,
391+
isArticleEdited: _isArticleEdited
392+
}, () => {
393+
if (isArticleEdited !== _isArticleEdited) {
394+
let { dispatch } = this.props
395+
if (_isArticleEdited) {
396+
console.log('lockit')
397+
dispatch(lockStatus())
398+
} else {
399+
console.log('unlockit')
400+
dispatch(unlockStatus())
401+
}
402+
}
403+
})
292404
}
293405

294406
handleTogglePreviewButtonClick (e) {
@@ -322,6 +434,7 @@ export default class ArticleDetail extends React.Component {
322434
>
323435
{folderOptions}
324436
</select>
437+
{this.state.isArticleEdited ? ' (edited)' : ''}
325438

326439
<TagSelect
327440
tags={this.state.article.tags}
@@ -347,7 +460,7 @@ export default class ArticleDetail extends React.Component {
347460
<div className='detailPanel'>
348461
<div className='header'>
349462
<div className='title'>
350-
<input onKeyDown={e => this.handleTitleKeyDown(e)} placeholder='Title' ref='title' valueLink={this.linkState('article.title')}/>
463+
<input onKeyDown={e => this.handleTitleKeyDown(e)} placeholder='Title' ref='title' value={this.state.article.title} onChange={e => this.handleTitleChange(e)}/>
351464
</div>
352465
<ModeSelect
353466
ref='mode'
@@ -396,6 +509,7 @@ export default class ArticleDetail extends React.Component {
396509
ArticleDetail.propTypes = {
397510
status: PropTypes.shape(),
398511
activeArticle: PropTypes.shape(),
399-
activeUser: PropTypes.shape()
512+
activeUser: PropTypes.shape(),
513+
dispatch: PropTypes.func
400514
}
401515
ArticleDetail.prototype.linkState = linkState

browser/styles/main/HomeContainer/index.styl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@
99
@require './lib/CreateNewFolder'
1010
@require './lib/Preferences'
1111
@require './lib/Tutorial'
12+
@require './lib/EditedAlert'
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
.EditedAlert.modal
2+
width 350px
3+
top 100px
4+
.title
5+
font-size 24px
6+
margin-bottom 15px
7+
.message
8+
font-size 14px
9+
margin-bottom 15px
10+
.control
11+
text-align right
12+
button
13+
border-radius 5px
14+
height 33px
15+
padding 0 15px
16+
font-size 14px
17+
background-color white
18+
border 1px solid borderColor
19+
border-radius 5px
20+
margin-left 5px
21+
&:hover
22+
background-color darken(white, 10%)
23+
&.primary
24+
border-color brandColor
25+
background-color brandColor
26+
color white
27+
&:hover
28+
background-color lighten(brandColor, 10%)

lib/actions.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ export const SWITCH_ARTICLE = 'SWITCH_ARTICLE'
1212
export const SET_SEARCH_FILTER = 'SET_SEARCH_FILTER'
1313
export const SET_TAG_FILTER = 'SET_TAG_FILTER'
1414
export const CLEAR_SEARCH = 'CLEAR_SEARCH'
15+
export const LOCK_STATUS = 'LOCK_STATUS'
16+
export const UNLOCK_STATUS = 'UNLOCK_STATUS'
1517
export const TOGGLE_TUTORIAL = 'TOGGLE_TUTORIAL'
1618

1719
// Status - mode
@@ -109,7 +111,19 @@ export function clearSearch () {
109111
}
110112
}
111113

112-
export function toggleTutorial() {
114+
export function lockStatus () {
115+
return {
116+
type: LOCK_STATUS
117+
}
118+
}
119+
120+
export function unlockStatus () {
121+
return {
122+
type: UNLOCK_STATUS
123+
}
124+
}
125+
126+
export function toggleTutorial () {
113127
return {
114128
type: TOGGLE_TUTORIAL
115129
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import React, { PropTypes } from 'react'
2+
import store from 'boost/store'
3+
import { unlockStatus } from 'boost/actions'
4+
5+
export default class EditedAlert extends React.Component {
6+
handleNoButtonClick (e) {
7+
this.props.close()
8+
}
9+
10+
handleYesButtonClick (e) {
11+
store.dispatch(unlockStatus())
12+
store.dispatch(this.props.action)
13+
this.props.close()
14+
}
15+
16+
render () {
17+
return (
18+
<div className='EditedAlert modal'>
19+
<div className='title'>Your article is still editing!</div>
20+
21+
<div className='message'>Do you really want to leave without finishing?</div>
22+
23+
<div className='control'>
24+
<button onClick={e => this.handleNoButtonClick(e)}><i className='fa fa-fw fa-close'/> No</button>
25+
<button onClick={e => this.handleYesButtonClick(e)} className='primary'><i className='fa fa-fw fa-check'/> Yes</button>
26+
</div>
27+
</div>
28+
)
29+
}
30+
}
31+
32+
EditedAlert.propTypes = {
33+
action: PropTypes.object,
34+
close: PropTypes.func
35+
}

lib/reducer.js

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,42 @@
11
import { combineReducers } from 'redux'
22
import _ from 'lodash'
3-
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'
3+
import {
4+
// Status action type
5+
SWITCH_FOLDER,
6+
SWITCH_MODE,
7+
SWITCH_ARTICLE,
8+
SET_SEARCH_FILTER,
9+
SET_TAG_FILTER,
10+
CLEAR_SEARCH,
11+
LOCK_STATUS,
12+
UNLOCK_STATUS,
13+
TOGGLE_TUTORIAL,
14+
15+
// Article action type
16+
ARTICLE_UPDATE,
17+
ARTICLE_DESTROY,
18+
19+
// Folder action type
20+
FOLDER_CREATE,
21+
FOLDER_UPDATE,
22+
FOLDER_DESTROY,
23+
FOLDER_REPLACE,
24+
25+
// view mode
26+
IDLE_MODE,
27+
CREATE_MODE
28+
} from './actions'
429
import dataStore from 'boost/dataStore'
530
import keygen from 'boost/keygen'
631
import activityRecord from 'boost/activityRecord'
32+
import { openModal } from 'boost/modal'
33+
import EditedAlert from 'boost/components/modal/EditedAlert'
734

835
const initialStatus = {
936
mode: IDLE_MODE,
1037
search: '',
11-
isTutorialOpen: false
38+
isTutorialOpen: false,
39+
isStatusLocked: false
1240
}
1341

1442
let data = dataStore.getData()
@@ -134,10 +162,25 @@ function articles (state = initialArticles, action) {
134162

135163
function status (state = initialStatus, action) {
136164
state = Object.assign({}, state)
165+
137166
switch (action.type) {
138167
case TOGGLE_TUTORIAL:
139168
state.isTutorialOpen = !state.isTutorialOpen
140169
return state
170+
case LOCK_STATUS:
171+
state.isStatusLocked = true
172+
return state
173+
case UNLOCK_STATUS:
174+
state.isStatusLocked = false
175+
return state
176+
}
177+
178+
// if status locked, status become unmutable
179+
if (state.isStatusLocked) {
180+
openModal(EditedAlert, {action})
181+
return state
182+
}
183+
switch (action.type) {
141184
case SWITCH_FOLDER:
142185
state.mode = IDLE_MODE
143186
state.search = `//${action.data} `

0 commit comments

Comments
 (0)