mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-14 02:06:29 +00:00
Key入力追加
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import React, { PropTypes} from 'react'
|
import React, { PropTypes} from 'react'
|
||||||
import { connect } from 'react-redux'
|
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 UserNavigator from './HomePage/UserNavigator'
|
||||||
import ArticleNavigator from './HomePage/ArticleNavigator'
|
import ArticleNavigator from './HomePage/ArticleNavigator'
|
||||||
import ArticleTopBar from './HomePage/ArticleTopBar'
|
import ArticleTopBar from './HomePage/ArticleTopBar'
|
||||||
@@ -8,24 +8,102 @@ import ArticleList from './HomePage/ArticleList'
|
|||||||
import ArticleDetail from './HomePage/ArticleDetail'
|
import ArticleDetail from './HomePage/ArticleDetail'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import keygen from 'boost/keygen'
|
import keygen from 'boost/keygen'
|
||||||
|
import { isModalOpen, closeModal } from 'boost/modal'
|
||||||
|
|
||||||
const TEXT_FILTER = 'TEXT_FILTER'
|
const TEXT_FILTER = 'TEXT_FILTER'
|
||||||
const FOLDER_FILTER = 'FOLDER_FILTER'
|
const FOLDER_FILTER = 'FOLDER_FILTER'
|
||||||
const TAG_FILTER = 'TAG_FILTER'
|
const TAG_FILTER = 'TAG_FILTER'
|
||||||
|
|
||||||
class HomePage extends React.Component {
|
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 () {
|
render () {
|
||||||
let { dispatch, status, articles, activeArticle, folders } = this.props
|
let { dispatch, status, articles, activeArticle, folders, filters } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='HomePage'>
|
<div className='HomePage'>
|
||||||
<ArticleNavigator
|
<ArticleNavigator
|
||||||
|
ref='nav'
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
folders={folders}
|
folders={folders}
|
||||||
status={status}
|
status={status}
|
||||||
/>
|
/>
|
||||||
<ArticleTopBar dispatch={dispatch} status={status}/>
|
<ArticleTopBar
|
||||||
|
ref='top'
|
||||||
|
dispatch={dispatch}
|
||||||
|
status={status}
|
||||||
|
/>
|
||||||
<ArticleList
|
<ArticleList
|
||||||
|
ref='list'
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
folders={folders}
|
folders={folders}
|
||||||
articles={articles}
|
articles={articles}
|
||||||
@@ -33,10 +111,12 @@ class HomePage extends React.Component {
|
|||||||
activeArticle={activeArticle}
|
activeArticle={activeArticle}
|
||||||
/>
|
/>
|
||||||
<ArticleDetail
|
<ArticleDetail
|
||||||
|
ref='detail'
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
activeArticle={activeArticle}
|
activeArticle={activeArticle}
|
||||||
folders={folders}
|
folders={folders}
|
||||||
status={status}
|
status={status}
|
||||||
|
filters={filters}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -139,7 +219,12 @@ function remap (state) {
|
|||||||
folders,
|
folders,
|
||||||
status,
|
status,
|
||||||
articles,
|
articles,
|
||||||
activeArticle
|
activeArticle,
|
||||||
|
filters: {
|
||||||
|
folder: folderFilters,
|
||||||
|
tag: tagFilters,
|
||||||
|
text: textFilters
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +238,12 @@ HomePage.propTypes = {
|
|||||||
articles: PropTypes.array,
|
articles: PropTypes.array,
|
||||||
activeArticle: PropTypes.shape(),
|
activeArticle: PropTypes.shape(),
|
||||||
dispatch: PropTypes.func,
|
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)
|
export default connect(remap)(HomePage)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ 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, 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 aceModes from 'boost/ace-modes'
|
||||||
import Select from 'react-select'
|
import Select from 'react-select'
|
||||||
import linkState from 'boost/linkState'
|
import linkState from 'boost/linkState'
|
||||||
@@ -38,6 +38,11 @@ export default class ArticleDetail extends React.Component {
|
|||||||
console.log('receive props')
|
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 () {
|
renderEmpty () {
|
||||||
@@ -135,7 +140,7 @@ export default class ArticleDetail extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleSaveButtonClick (e) {
|
handleSaveButtonClick (e) {
|
||||||
let { dispatch, folders } = this.props
|
let { dispatch, folders, filters } = this.props
|
||||||
let article = this.state.article
|
let article = this.state.article
|
||||||
let newArticle = Object.assign({}, article)
|
let newArticle = Object.assign({}, article)
|
||||||
|
|
||||||
@@ -147,6 +152,8 @@ export default class ArticleDetail extends React.Component {
|
|||||||
|
|
||||||
dispatch(updateArticle(newArticle))
|
dispatch(updateArticle(newArticle))
|
||||||
dispatch(switchMode(IDLE_MODE))
|
dispatch(switchMode(IDLE_MODE))
|
||||||
|
if (filters.folder.length !== 0) dispatch(switchFolder(folder.name))
|
||||||
|
dispatch(switchArticle(newArticle.key))
|
||||||
}
|
}
|
||||||
|
|
||||||
handleFolderKeyChange (e) {
|
handleFolderKeyChange (e) {
|
||||||
|
|||||||
@@ -15,6 +15,26 @@ export default class ArticleList extends React.Component {
|
|||||||
clearInterval(this.refreshTimer)
|
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) {
|
handleArticleClick (article) {
|
||||||
let { dispatch } = this.props
|
let { dispatch } = this.props
|
||||||
return function (e) {
|
return function (e) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import Preferences from 'boost/components/modal/Preferences'
|
|||||||
import CreateNewFolder from 'boost/components/modal/CreateNewFolder'
|
import CreateNewFolder from 'boost/components/modal/CreateNewFolder'
|
||||||
|
|
||||||
import remote from 'remote'
|
import remote from 'remote'
|
||||||
|
let userName = remote.getGlobal('process').env.USER
|
||||||
|
|
||||||
export default class ArticleNavigator extends React.Component {
|
export default class ArticleNavigator extends React.Component {
|
||||||
handlePreferencesButtonClick (e) {
|
handlePreferencesButtonClick (e) {
|
||||||
@@ -51,8 +52,6 @@ export default class ArticleNavigator extends React.Component {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
let userName = remote.getGlobal('process').env.USER
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='ArticleNavigator'>
|
<div className='ArticleNavigator'>
|
||||||
<div className='userInfo'>
|
<div className='userInfo'>
|
||||||
|
|||||||
@@ -4,16 +4,29 @@ import ExternalLink from 'boost/components/ExternalLink'
|
|||||||
import { setSearchFilter } from 'boost/actions'
|
import { setSearchFilter } from 'boost/actions'
|
||||||
|
|
||||||
export default class ArticleTopBar extends React.Component {
|
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) {
|
handleSearchChange (e) {
|
||||||
let { dispatch } = this.props
|
let { dispatch } = this.props
|
||||||
|
|
||||||
dispatch(setSearchFilter(e.target.value))
|
dispatch(setSearchFilter(e.target.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSearchClearButton (e) {
|
handleSearchClearButton (e) {
|
||||||
let { dispatch } = this.props
|
let { dispatch } = this.props
|
||||||
|
|
||||||
dispatch(setSearchFilter(''))
|
dispatch(setSearchFilter(''))
|
||||||
ReactDOM.findDOMNode(this.refs.searchInput).focus()
|
this.focusInput()
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import HomePage from './HomePage'
|
|||||||
// import auth from 'boost/auth'
|
// import auth from 'boost/auth'
|
||||||
import store from 'boost/store'
|
import store from 'boost/store'
|
||||||
import ReactDOM from 'react-dom'
|
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')
|
require('../styles/main/index.styl')
|
||||||
|
|
||||||
let routes = (
|
let routes = (
|
||||||
@@ -25,21 +27,4 @@ ReactDOM.render((
|
|||||||
), el, function () {
|
), el, function () {
|
||||||
let loadingCover = document.getElementById('loadingCover')
|
let loadingCover = document.getElementById('loadingCover')
|
||||||
loadingCover.parentNode.removeChild(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')
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export const SWITCH_MODE = 'SWITCH_MODE'
|
|||||||
export const SWITCH_ARTICLE = 'SWITCH_ARTICLE'
|
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'
|
||||||
|
|
||||||
// Status - mode
|
// Status - mode
|
||||||
export const IDLE_MODE = 'IDLE_MODE'
|
export const IDLE_MODE = 'IDLE_MODE'
|
||||||
@@ -18,8 +19,6 @@ export const EDIT_MODE = 'EDIT_MODE'
|
|||||||
|
|
||||||
// Article status
|
// Article status
|
||||||
export const NEW = 'NEW'
|
export const NEW = 'NEW'
|
||||||
export const SYNCING = 'SYNCING'
|
|
||||||
export const UNSYNCED = 'UNSYNCED'
|
|
||||||
|
|
||||||
// DB
|
// DB
|
||||||
export function updateArticle (article) {
|
export function updateArticle (article) {
|
||||||
@@ -91,3 +90,9 @@ export function setTagFilter (tag) {
|
|||||||
data: tag
|
data: tag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function clearSearch () {
|
||||||
|
return {
|
||||||
|
type: CLEAR_SEARCH
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,7 +17,12 @@ export default class TagSelect extends React.Component {
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
let tags = this.props.tags.slice(0)
|
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.push(newTag)
|
||||||
tags = _.uniq(tags)
|
tags = _.uniq(tags)
|
||||||
|
|||||||
@@ -40,3 +40,7 @@ export function closeModal () {
|
|||||||
if (modalBase == null) { return }
|
if (modalBase == null) { return }
|
||||||
modalBase.setState({component: null, componentProps: null, isHidden: true})
|
modalBase.setState({component: null, componentProps: null, isHidden: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isModalOpen () {
|
||||||
|
return !modalBase.state.isHidden
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
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, 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 dataStore from 'boost/dataStore'
|
||||||
import keygen from 'boost/keygen'
|
import keygen from 'boost/keygen'
|
||||||
|
|
||||||
@@ -134,6 +134,11 @@ function status (state = initialStatus, action) {
|
|||||||
state.search = `#${action.data}`
|
state.search = `#${action.data}`
|
||||||
state.mode = IDLE_MODE
|
state.mode = IDLE_MODE
|
||||||
|
|
||||||
|
return state
|
||||||
|
case CLEAR_SEARCH:
|
||||||
|
state.search = ''
|
||||||
|
state.mode = IDLE_MODE
|
||||||
|
|
||||||
return state
|
return state
|
||||||
default:
|
default:
|
||||||
return state
|
return state
|
||||||
|
|||||||
Reference in New Issue
Block a user