diff --git a/browser/main/HomePage.js b/browser/main/HomePage.js
index ffd3b790..4ed0f81b 100644
--- a/browser/main/HomePage.js
+++ b/browser/main/HomePage.js
@@ -1,6 +1,6 @@
import React, { PropTypes} from 'react'
import { connect } from 'react-redux'
-import { CREATE_MODE, IDLE_MODE, NEW } from 'boost/actions'
+import { CREATE_MODE, EDIT_MODE, IDLE_MODE, NEW } from 'boost/actions'
// import UserNavigator from './HomePage/UserNavigator'
import ArticleNavigator from './HomePage/ArticleNavigator'
import ArticleTopBar from './HomePage/ArticleTopBar'
@@ -8,24 +8,102 @@ import ArticleList from './HomePage/ArticleList'
import ArticleDetail from './HomePage/ArticleDetail'
import _ from 'lodash'
import keygen from 'boost/keygen'
+import { isModalOpen, closeModal } from 'boost/modal'
const TEXT_FILTER = 'TEXT_FILTER'
const FOLDER_FILTER = 'FOLDER_FILTER'
const TAG_FILTER = 'TAG_FILTER'
class HomePage extends React.Component {
+ componentDidMount () {
+ this.listener = (e) => this.handleKeyDown(e)
+ window.addEventListener('keydown', this.listener)
+ }
+
+ componentWillUnmount () {
+ window.removeEventListener('keydown', this.listener)
+ }
+
+ handleKeyDown (e) {
+ if (isModalOpen() && e.keyCode === 27) {
+ closeModal()
+ return
+ }
+
+ let { status } = this.props
+ let { nav, top, list, detail } = this.refs
+
+ if (top.isInputFocused() && !e.metaKey) {
+ if (e.keyCode === 13 || e.keyCode === 27) top.blurInput()
+ return
+ }
+
+ switch (status.mode) {
+ case CREATE_MODE:
+ case EDIT_MODE:
+ if (e.keyCode === 27) {
+ detail.handleCancelButtonClick()
+ }
+ if (e.keyCode === 13 && e.metaKey) {
+ detail.handleSaveButtonClick()
+ }
+ break
+ case IDLE_MODE:
+ if (e.keyCode === 69) {
+ detail.handleEditButtonClick()
+ }
+ if (e.keyCode === 68) {
+ detail.handleDeleteButtonClick()
+ }
+
+ // `detail`の`openDeleteConfirmMenu`の時。
+ if (detail.state.openDeleteConfirmMenu) {
+ if (e.keyCode === 27) {
+ detail.handleDeleteCancleButtonClick()
+ }
+ if (e.keyCode === 13 && e.metaKey) {
+ detail.handleDeleteConfirmButtonClick()
+ }
+ break
+ }
+
+ // `detail`の`openDeleteConfirmMenu`が`true`なら呼ばれない。
+ if (e.keyCode === 27) {
+ top.focusInput()
+ }
+
+ if (e.keyCode === 38) {
+ list.selectPriorArticle()
+ }
+
+ if (e.keyCode === 40) {
+ list.selectNextArticle()
+ }
+
+ if (e.keyCode === 13 && e.metaKey) {
+ nav.handleNewPostButtonClick()
+ }
+ }
+ }
+
render () {
- let { dispatch, status, articles, activeArticle, folders } = this.props
+ let { dispatch, status, articles, activeArticle, folders, filters } = this.props
return (
)
@@ -139,7 +219,12 @@ function remap (state) {
folders,
status,
articles,
- activeArticle
+ activeArticle,
+ filters: {
+ folder: folderFilters,
+ tag: tagFilters,
+ text: textFilters
+ }
}
}
@@ -153,7 +238,12 @@ HomePage.propTypes = {
articles: PropTypes.array,
activeArticle: PropTypes.shape(),
dispatch: PropTypes.func,
- folders: PropTypes.array
+ folders: PropTypes.array,
+ filters: PropTypes.shape({
+ folder: PropTypes.array,
+ tag: PropTypes.array,
+ text: PropTypes.array
+ })
}
export default connect(remap)(HomePage)
diff --git a/browser/main/HomePage/ArticleDetail.js b/browser/main/HomePage/ArticleDetail.js
index 9a33af2a..a8b1c72c 100644
--- a/browser/main/HomePage/ArticleDetail.js
+++ b/browser/main/HomePage/ArticleDetail.js
@@ -4,7 +4,7 @@ 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, updateArticle, destroyArticle } from 'boost/actions'
+import { IDLE_MODE, CREATE_MODE, EDIT_MODE, switchMode, switchArticle, switchFolder, updateArticle, destroyArticle } from 'boost/actions'
import aceModes from 'boost/ace-modes'
import Select from 'react-select'
import linkState from 'boost/linkState'
@@ -38,6 +38,11 @@ export default class ArticleDetail extends React.Component {
console.log('receive props')
})
}
+
+ let isEdit = nextProps.status.mode === EDIT_MODE || nextProps.status.mode === CREATE_MODE
+ if (isEdit && this.state.openDeleteConfirmMenu) {
+ this.setState({openDeleteConfirmMenu: false})
+ }
}
renderEmpty () {
@@ -135,7 +140,7 @@ export default class ArticleDetail extends React.Component {
}
handleSaveButtonClick (e) {
- let { dispatch, folders } = this.props
+ let { dispatch, folders, filters } = this.props
let article = this.state.article
let newArticle = Object.assign({}, article)
@@ -147,6 +152,8 @@ export default class ArticleDetail extends React.Component {
dispatch(updateArticle(newArticle))
dispatch(switchMode(IDLE_MODE))
+ if (filters.folder.length !== 0) dispatch(switchFolder(folder.name))
+ dispatch(switchArticle(newArticle.key))
}
handleFolderKeyChange (e) {
diff --git a/browser/main/HomePage/ArticleList.js b/browser/main/HomePage/ArticleList.js
index 67442513..3fc307b8 100644
--- a/browser/main/HomePage/ArticleList.js
+++ b/browser/main/HomePage/ArticleList.js
@@ -15,6 +15,26 @@ export default class ArticleList extends React.Component {
clearInterval(this.refreshTimer)
}
+ selectPriorArticle () {
+ let { articles, activeArticle, dispatch } = this.props
+ let targetIndex = articles.indexOf(activeArticle) - 1
+ let targetArticle = articles[targetIndex]
+
+ if (targetArticle != null) {
+ dispatch(switchArticle(targetArticle.key))
+ }
+ }
+
+ selectNextArticle () {
+ let { articles, activeArticle, dispatch } = this.props
+ let targetIndex = articles.indexOf(activeArticle) + 1
+ let targetArticle = articles[targetIndex]
+
+ if (targetArticle != null) {
+ dispatch(switchArticle(targetArticle.key))
+ }
+ }
+
handleArticleClick (article) {
let { dispatch } = this.props
return function (e) {
diff --git a/browser/main/HomePage/ArticleNavigator.js b/browser/main/HomePage/ArticleNavigator.js
index 5db3b832..1d6ea82c 100644
--- a/browser/main/HomePage/ArticleNavigator.js
+++ b/browser/main/HomePage/ArticleNavigator.js
@@ -7,6 +7,7 @@ import Preferences from 'boost/components/modal/Preferences'
import CreateNewFolder from 'boost/components/modal/CreateNewFolder'
import remote from 'remote'
+let userName = remote.getGlobal('process').env.USER
export default class ArticleNavigator extends React.Component {
handlePreferencesButtonClick (e) {
@@ -51,8 +52,6 @@ export default class ArticleNavigator extends React.Component {
)
})
- let userName = remote.getGlobal('process').env.USER
-
return (
diff --git a/browser/main/HomePage/ArticleTopBar.js b/browser/main/HomePage/ArticleTopBar.js
index 317d7e4a..c78ecf37 100644
--- a/browser/main/HomePage/ArticleTopBar.js
+++ b/browser/main/HomePage/ArticleTopBar.js
@@ -4,16 +4,29 @@ import ExternalLink from 'boost/components/ExternalLink'
import { setSearchFilter } from 'boost/actions'
export default class ArticleTopBar extends React.Component {
+ isInputFocused () {
+ return document.activeElement === ReactDOM.findDOMNode(this.refs.searchInput)
+ }
+
+ focusInput () {
+ ReactDOM.findDOMNode(this.refs.searchInput).focus()
+ }
+
+ blurInput () {
+ ReactDOM.findDOMNode(this.refs.searchInput).blur()
+ }
+
handleSearchChange (e) {
let { dispatch } = this.props
dispatch(setSearchFilter(e.target.value))
}
+
handleSearchClearButton (e) {
let { dispatch } = this.props
dispatch(setSearchFilter(''))
- ReactDOM.findDOMNode(this.refs.searchInput).focus()
+ this.focusInput()
}
render () {
diff --git a/browser/main/index.js b/browser/main/index.js
index 32a4f106..33e468fb 100644
--- a/browser/main/index.js
+++ b/browser/main/index.js
@@ -7,6 +7,8 @@ import HomePage from './HomePage'
// import auth from 'boost/auth'
import store from 'boost/store'
import ReactDOM from 'react-dom'
+import { isModalOpen, closeModal } from 'boost/modal'
+import { IDLE_MODE, CREATE_MODE, EDIT_MODE } from 'boost/actions'
require('../styles/main/index.styl')
let routes = (
@@ -25,21 +27,4 @@ ReactDOM.render((
), el, function () {
let loadingCover = document.getElementById('loadingCover')
loadingCover.parentNode.removeChild(loadingCover)
-
- // Refresh user information
- // if (auth.user() != null) {
- // fetchCurrentUser()
- // .then(function (res) {
- // let user = res.body
- // store.dispatch(updateUser(user))
- // })
- // .catch(function (err) {
- // if (err.status === 401) {
- // auth.clear()
- // if (window != null) window.location.reload()
- // }
- // console.error(err.message)
- // console.log('Fetch failed')
- // })
- // }
})
diff --git a/lib/actions.js b/lib/actions.js
index ac0a2059..ba0f668e 100644
--- a/lib/actions.js
+++ b/lib/actions.js
@@ -10,6 +10,7 @@ export const SWITCH_MODE = 'SWITCH_MODE'
export const SWITCH_ARTICLE = 'SWITCH_ARTICLE'
export const SET_SEARCH_FILTER = 'SET_SEARCH_FILTER'
export const SET_TAG_FILTER = 'SET_TAG_FILTER'
+export const CLEAR_SEARCH = 'CLEAR_SEARCH'
// Status - mode
export const IDLE_MODE = 'IDLE_MODE'
@@ -18,8 +19,6 @@ export const EDIT_MODE = 'EDIT_MODE'
// Article status
export const NEW = 'NEW'
-export const SYNCING = 'SYNCING'
-export const UNSYNCED = 'UNSYNCED'
// DB
export function updateArticle (article) {
@@ -91,3 +90,9 @@ export function setTagFilter (tag) {
data: tag
}
}
+
+export function clearSearch () {
+ return {
+ type: CLEAR_SEARCH
+ }
+}
diff --git a/lib/components/TagSelect.js b/lib/components/TagSelect.js
index 91a2ed63..b7fbead8 100644
--- a/lib/components/TagSelect.js
+++ b/lib/components/TagSelect.js
@@ -17,7 +17,12 @@ export default class TagSelect extends React.Component {
e.preventDefault()
let tags = this.props.tags.slice(0)
- let newTag = this.state.input
+ let newTag = this.state.input.trim()
+
+ if (newTag.length === 0) {
+ this.setState({input: ''})
+ return
+ }
tags.push(newTag)
tags = _.uniq(tags)
diff --git a/lib/modal.js b/lib/modal.js
index ae720490..a445ec33 100644
--- a/lib/modal.js
+++ b/lib/modal.js
@@ -40,3 +40,7 @@ export function closeModal () {
if (modalBase == null) { return }
modalBase.setState({component: null, componentProps: null, isHidden: true})
}
+
+export function isModalOpen () {
+ return !modalBase.state.isHidden
+}
diff --git a/lib/reducer.js b/lib/reducer.js
index a51d48a8..e74f1370 100644
--- a/lib/reducer.js
+++ b/lib/reducer.js
@@ -1,6 +1,6 @@
import { combineReducers } from 'redux'
import _ from 'lodash'
-import { SWITCH_FOLDER, SWITCH_MODE, SWITCH_ARTICLE, SET_SEARCH_FILTER, SET_TAG_FILTER, ARTICLE_UPDATE, ARTICLE_DESTROY, FOLDER_CREATE, FOLDER_UPDATE, FOLDER_DESTROY, IDLE_MODE, CREATE_MODE } from './actions'
+import { SWITCH_FOLDER, SWITCH_MODE, SWITCH_ARTICLE, SET_SEARCH_FILTER, SET_TAG_FILTER, CLEAR_SEARCH, ARTICLE_UPDATE, ARTICLE_DESTROY, FOLDER_CREATE, FOLDER_UPDATE, FOLDER_DESTROY, IDLE_MODE, CREATE_MODE } from './actions'
import dataStore from 'boost/dataStore'
import keygen from 'boost/keygen'
@@ -134,6 +134,11 @@ function status (state = initialStatus, action) {
state.search = `#${action.data}`
state.mode = IDLE_MODE
+ return state
+ case CLEAR_SEARCH:
+ state.search = ''
+ state.mode = IDLE_MODE
+
return state
default:
return state