mirror of
https://github.com/BoostIo/Boostnote
synced 2026-01-30 17:17:17 +00:00
Detail
This commit is contained in:
@@ -8,10 +8,10 @@
|
||||
height 320px
|
||||
display flex
|
||||
align-items center
|
||||
|
||||
.empty-message
|
||||
width 100%
|
||||
font-size 42px
|
||||
line-height 72px
|
||||
text-align center
|
||||
color $ui-inactive-text-color
|
||||
|
||||
155
browser/main/Detail/NoteDetail.js
Normal file
155
browser/main/Detail/NoteDetail.js
Normal file
@@ -0,0 +1,155 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './NoteDetail.styl'
|
||||
import MarkdownEditor from 'browser/components/MarkdownEditor'
|
||||
import queue from 'browser/main/lib/queue'
|
||||
|
||||
class NoteDetail extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
note: Object.assign({}, props.note),
|
||||
isDispatchQueued: false
|
||||
}
|
||||
this.dispatchTimer = null
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps, prevState) {
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.note.key !== this.props.note.key) {
|
||||
if (this.state.isDispatchQueued) {
|
||||
this.dispatch()
|
||||
}
|
||||
this.setState({
|
||||
note: Object.assign({}, nextProps.note),
|
||||
isDispatchQueued: false
|
||||
}, () => {
|
||||
this.refs.content.reload()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
findTitle (value) {
|
||||
let splitted = value.split('\n')
|
||||
let title = null
|
||||
|
||||
for (let i = 0; i < splitted.length; i++) {
|
||||
let trimmedLine = splitted[i].trim()
|
||||
if (trimmedLine.match(/^# .+/)) {
|
||||
title = trimmedLine.substring(1, trimmedLine.length).trim()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (title == null) {
|
||||
for (let i = 0; i < splitted.length; i++) {
|
||||
let trimmedLine = splitted[i].trim()
|
||||
if (trimmedLine.length > 0) {
|
||||
title = trimmedLine
|
||||
break
|
||||
}
|
||||
}
|
||||
if (title == null) {
|
||||
title = ''
|
||||
}
|
||||
}
|
||||
|
||||
return title
|
||||
}
|
||||
|
||||
handleChange (e) {
|
||||
let { note } = this.state
|
||||
|
||||
note.content = this.refs.content.value
|
||||
|
||||
this.setState({
|
||||
note,
|
||||
isDispatchQueued: true
|
||||
}, () => {
|
||||
this.queueDispatch()
|
||||
})
|
||||
}
|
||||
|
||||
cancelDispatchQueue () {
|
||||
if (this.dispatchTimer != null) {
|
||||
window.clearTimeout(this.dispatchTimer)
|
||||
this.dispatchTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
queueDispatch () {
|
||||
this.cancelDispatchQueue()
|
||||
|
||||
this.dispatchTimer = window.setTimeout(() => {
|
||||
this.dispatch()
|
||||
this.setState({
|
||||
isDispatchQueued: false
|
||||
})
|
||||
}, 500)
|
||||
}
|
||||
|
||||
dispatch () {
|
||||
let { note } = this.state
|
||||
note = Object.assign({}, note)
|
||||
let repoKey = note._repository.key
|
||||
note.title = this.findTitle(note.content)
|
||||
|
||||
let { dispatch } = this.props
|
||||
dispatch({
|
||||
type: 'SAVE_NOTE',
|
||||
repository: repoKey,
|
||||
note: note
|
||||
})
|
||||
queue.save(repoKey, note)
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className='NoteDetail'
|
||||
style={this.props.style}
|
||||
styleName='root'
|
||||
>
|
||||
<div styleName='info'>
|
||||
<div styleName='info-left'>
|
||||
<div styleName='info-left-folderSelect'>FOLDER SELECT</div>
|
||||
<div styleName='info-left-tagSelect'>TAG SELECT</div>
|
||||
</div>
|
||||
<div styleName='info-right'>
|
||||
<button styleName='info-right-button'>
|
||||
<i className='fa fa-clipboard fa-fw'/>
|
||||
</button>
|
||||
<button styleName='info-right-button'>
|
||||
<i className='fa fa-share-alt fa-fw'/>
|
||||
</button>
|
||||
<button styleName='info-right-button'>
|
||||
<i className='fa fa-ellipsis-v'/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div styleName='body'>
|
||||
<MarkdownEditor
|
||||
ref='content'
|
||||
styleName='body-noteEditor'
|
||||
value={this.state.note.content}
|
||||
onChange={(e) => this.handleChange(e)}
|
||||
ignorePreviewPointerEvents={this.props.ignorePreviewPointerEvents}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
NoteDetail.propTypes = {
|
||||
dispatch: PropTypes.func,
|
||||
repositories: PropTypes.array,
|
||||
style: PropTypes.shape({
|
||||
left: PropTypes.number
|
||||
}),
|
||||
ignorePreviewPointerEvents: PropTypes.bool
|
||||
}
|
||||
|
||||
export default CSSModules(NoteDetail, styles)
|
||||
38
browser/main/Detail/NoteDetail.styl
Normal file
38
browser/main/Detail/NoteDetail.styl
Normal file
@@ -0,0 +1,38 @@
|
||||
.root
|
||||
absolute top bottom right
|
||||
border-width 1px 0
|
||||
border-style solid
|
||||
border-color $ui-borderColor
|
||||
|
||||
.info
|
||||
absolute top left right
|
||||
height 50px
|
||||
border-bottom $ui-border
|
||||
background-color $ui-backgroundColor
|
||||
|
||||
.info-left
|
||||
float left
|
||||
|
||||
.info-right
|
||||
float right
|
||||
|
||||
.info-right-button
|
||||
width 34px
|
||||
height 34px
|
||||
border-radius 17px
|
||||
navButtonColor()
|
||||
border $ui-border
|
||||
font-size 14px
|
||||
margin 8px 2px
|
||||
padding 0
|
||||
&:active
|
||||
border-color $ui-button--active-backgroundColor
|
||||
&:hover .left-control-newPostButton-tooltip
|
||||
display block
|
||||
|
||||
.body
|
||||
absolute bottom left right
|
||||
top 50px
|
||||
|
||||
.body-noteEditor
|
||||
absolute top bottom left right
|
||||
65
browser/main/Detail/index.js
Normal file
65
browser/main/Detail/index.js
Normal file
@@ -0,0 +1,65 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './Detail.styl'
|
||||
import _ from 'lodash'
|
||||
import NoteDetail from './NoteDetail'
|
||||
|
||||
const electron = require('electron')
|
||||
|
||||
const OSX = global.process.platform === 'darwin'
|
||||
|
||||
class Detail extends React.Component {
|
||||
componentDidUpdate (prevProps, prevState) {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { repositories, location } = this.props
|
||||
let note = null
|
||||
if (location.query.key != null) {
|
||||
let splitted = location.query.key.split('-')
|
||||
let repoKey = splitted.shift()
|
||||
let noteKey = splitted.shift()
|
||||
let repo = _.find(repositories, {key: repoKey})
|
||||
if (_.isObject(repo) && _.isArray(repo.notes)) {
|
||||
note = _.find(repo.notes, {key: noteKey})
|
||||
}
|
||||
}
|
||||
|
||||
if (note == null) {
|
||||
return (
|
||||
<div className='Detail'
|
||||
style={this.props.style}
|
||||
styleName='root'
|
||||
tabIndex='0'
|
||||
>
|
||||
<div styleName='empty'>
|
||||
<div styleName='empty-message'>{OSX ? 'Command(⌘)' : 'Ctrl(^)'} + N<br/>to create a new post</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<NoteDetail
|
||||
note={note}
|
||||
{..._.pick(this.props, [
|
||||
'dispatch',
|
||||
'repositories',
|
||||
'style',
|
||||
'ignorePreviewPointerEvents'
|
||||
])}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Detail.propTypes = {
|
||||
dispatch: PropTypes.func,
|
||||
repositories: PropTypes.array,
|
||||
style: PropTypes.shape({
|
||||
left: PropTypes.number
|
||||
}),
|
||||
ignorePreviewPointerEvents: PropTypes.bool
|
||||
}
|
||||
|
||||
export default CSSModules(Detail, styles)
|
||||
@@ -5,7 +5,7 @@ import { connect } from 'react-redux'
|
||||
import SideNav from './SideNav'
|
||||
import TopBar from './TopBar'
|
||||
import NoteList from './NoteList'
|
||||
import NoteDetail from './NoteDetail'
|
||||
import Detail from './Detail'
|
||||
import Repository from 'browser/lib/Repository'
|
||||
import StatusBar from './StatusBar'
|
||||
import _ from 'lodash'
|
||||
@@ -111,8 +111,16 @@ class Main extends React.Component {
|
||||
>
|
||||
<div styleName='slider-hitbox'/>
|
||||
</div>
|
||||
<NoteDetail
|
||||
<Detail
|
||||
style={{left: this.state.listWidth + 1}}
|
||||
{..._.pick(this.props, [
|
||||
'dispatch',
|
||||
'repositories',
|
||||
'config',
|
||||
'params',
|
||||
'location'
|
||||
])}
|
||||
ignorePreviewPointerEvents={this.state.isSliderFocused}
|
||||
/>
|
||||
</div>
|
||||
<StatusBar
|
||||
|
||||
@@ -1,243 +0,0 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import MarkdownPreview from 'browser/components/MarkdownPreview'
|
||||
import CodeEditor from 'browser/components/CodeEditor'
|
||||
import activityRecord from 'browser/lib/activityRecord'
|
||||
import fetchConfig from 'browser/lib/fetchConfig'
|
||||
|
||||
const electron = require('electron')
|
||||
const ipc = electron.ipcRenderer
|
||||
|
||||
export const PREVIEW_MODE = 'PREVIEW_MODE'
|
||||
export const EDIT_MODE = 'EDIT_MODE'
|
||||
|
||||
let config = fetchConfig()
|
||||
ipc.on('config-apply', function (e, newConfig) {
|
||||
config = newConfig
|
||||
})
|
||||
|
||||
export default class ArticleEditor extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.configApplyHandler = (e, config) => this.handleConfigApply(e, config)
|
||||
this.isMouseDown = false
|
||||
this.state = {
|
||||
status: PREVIEW_MODE,
|
||||
cursorPosition: null,
|
||||
firstVisibleRow: null,
|
||||
switchPreview: config['switch-preview'],
|
||||
isTemporary: false
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
ipc.on('config-apply', this.configApplyHandler)
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
ipc.removeListener('config-apply', this.configApplyHandler)
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.article.key !== this.props.article.key) {
|
||||
this.setState({
|
||||
content: this.props.article.content
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleConfigApply (e, newConfig) {
|
||||
this.setState({
|
||||
switchPreview: newConfig['switch-preview']
|
||||
})
|
||||
}
|
||||
|
||||
resetCursorPosition () {
|
||||
this.setState({
|
||||
cursorPosition: null,
|
||||
firstVisibleRow: null
|
||||
}, function () {
|
||||
let previewEl = ReactDOM.findDOMNode(this.refs.preview)
|
||||
if (previewEl) previewEl.scrollTop = 0
|
||||
})
|
||||
}
|
||||
|
||||
switchPreviewMode (isTemporary = false) {
|
||||
if (this.props.article.mode !== 'markdown') return true
|
||||
let cursorPosition = this.refs.editor.getCursorPosition()
|
||||
let firstVisibleRow = this.refs.editor.getFirstVisibleRow()
|
||||
this.setState({
|
||||
status: PREVIEW_MODE,
|
||||
cursorPosition,
|
||||
firstVisibleRow,
|
||||
isTemporary: isTemporary
|
||||
}, function () {
|
||||
let previewEl = ReactDOM.findDOMNode(this.refs.preview)
|
||||
let anchors = previewEl.querySelectorAll('.lineAnchor')
|
||||
for (let i = 0; i < anchors.length; i++) {
|
||||
if (parseInt(anchors[i].dataset.key, 10) > cursorPosition.row || i === anchors.length - 1) {
|
||||
var targetAnchor = anchors[i > 0 ? i - 1 : 0]
|
||||
previewEl.scrollTop = targetAnchor.offsetTop - 100
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
switchEditMode (isTemporary = false) {
|
||||
this.setState({
|
||||
status: EDIT_MODE,
|
||||
isTemporary: false
|
||||
}, function () {
|
||||
if (this.state.cursorPosition != null) {
|
||||
this.refs.editor.moveCursorTo(this.state.cursorPosition.row, this.state.cursorPosition.column)
|
||||
this.refs.editor.scrollToLine(this.state.firstVisibleRow)
|
||||
}
|
||||
this.refs.editor.editor.focus()
|
||||
|
||||
if (!isTemporary) activityRecord.emit('ARTICLE_UPDATE', this.props.article)
|
||||
})
|
||||
}
|
||||
|
||||
handleBlurCodeEditor (e) {
|
||||
let isFocusingToThis = e.relatedTarget === ReactDOM.findDOMNode(this)
|
||||
if (isFocusingToThis || this.state.switchPreview !== 'blur') {
|
||||
return
|
||||
}
|
||||
|
||||
let { article } = this.props
|
||||
if (article.mode === 'markdown') {
|
||||
this.switchPreviewMode()
|
||||
}
|
||||
}
|
||||
|
||||
handleCodeEditorChange (value) {
|
||||
this.props.onChange(value)
|
||||
}
|
||||
|
||||
handleRightClick (e) {
|
||||
let { article } = this.props
|
||||
if (this.state.switchPreview === 'rightclick' && article.mode === 'markdown') {
|
||||
if (this.state.status === EDIT_MODE) this.switchPreviewMode()
|
||||
else this.switchEditMode()
|
||||
}
|
||||
}
|
||||
|
||||
handleMouseUp (e) {
|
||||
let { article } = this.props
|
||||
let showPreview = article.mode === 'markdown' && this.state.status === PREVIEW_MODE
|
||||
if (!showPreview) {
|
||||
return false
|
||||
}
|
||||
|
||||
switch (this.state.switchPreview) {
|
||||
case 'blur':
|
||||
switch (e.button) {
|
||||
case 0:
|
||||
this.isMouseDown = false
|
||||
this.moveCount = 0
|
||||
if (!this.isDrag) {
|
||||
this.switchEditMode()
|
||||
}
|
||||
break
|
||||
case 2:
|
||||
if (this.state.isTemporary) this.switchEditMode(true)
|
||||
}
|
||||
break
|
||||
case 'rightclick':
|
||||
}
|
||||
}
|
||||
|
||||
handleMouseMove (e) {
|
||||
let { article } = this.props
|
||||
let showPreview = article.mode === 'markdown' && this.state.status === PREVIEW_MODE
|
||||
if (!showPreview) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (this.state.switchPreview === 'blur' && this.isMouseDown) {
|
||||
this.moveCount++
|
||||
if (this.moveCount > 5) {
|
||||
this.isDrag = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleMouseDowm (e) {
|
||||
let { article } = this.props
|
||||
let showPreview = article.mode === 'markdown' && this.state.status === PREVIEW_MODE
|
||||
if (!showPreview) {
|
||||
return false
|
||||
}
|
||||
|
||||
switch (this.state.switchPreview) {
|
||||
case 'blur':
|
||||
switch (e.button) {
|
||||
case 0:
|
||||
this.isDrag = false
|
||||
this.isMouseDown = true
|
||||
this.moveCount = 0
|
||||
break
|
||||
case 2:
|
||||
if (this.state.status === EDIT_MODE && this.props.article.mode === 'markdown') {
|
||||
this.switchPreviewMode(true)
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'rightclick':
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
let { article } = this.props
|
||||
let showPreview = article.mode === 'markdown' && this.state.status === PREVIEW_MODE
|
||||
|
||||
return (
|
||||
<div
|
||||
tabIndex='5'
|
||||
onContextMenu={e => this.handleRightClick(e)}
|
||||
onMouseUp={e => this.handleMouseUp(e)}
|
||||
onMouseMove={e => this.handleMouseMove(e)}
|
||||
onMouseDown={e => this.handleMouseDowm(e)}
|
||||
className='ArticleEditor'
|
||||
>
|
||||
{showPreview
|
||||
? <MarkdownPreview
|
||||
ref='preview'
|
||||
content={article.content}
|
||||
/>
|
||||
: <CodeEditor
|
||||
ref='editor'
|
||||
onBlur={e => this.handleBlurCodeEditor(e)}
|
||||
onChange={value => this.handleCodeEditorChange(value)}
|
||||
article={article}
|
||||
/>
|
||||
}
|
||||
{article.mode === 'markdown'
|
||||
? <div className='ArticleDetail-panel-content-tooltip' children={
|
||||
showPreview
|
||||
? this.state.switchPreview === 'blur'
|
||||
? 'Click to Edit'
|
||||
: 'Right Click to Edit'
|
||||
: this.state.switchPreview === 'blur'
|
||||
? 'Press ESC to Watch Preview'
|
||||
: 'Right Click to Watch Preview'
|
||||
}
|
||||
/>
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ArticleEditor.propTypes = {
|
||||
article: PropTypes.shape({
|
||||
content: PropTypes.string,
|
||||
key: PropTypes.string,
|
||||
mode: PropTypes.string
|
||||
}),
|
||||
onChange: PropTypes.func,
|
||||
parent: PropTypes.object
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './NoteDetail.styl'
|
||||
const electron = require('electron')
|
||||
|
||||
const OSX = global.process.platform === 'darwin'
|
||||
|
||||
class NoteDetail extends React.Component {
|
||||
componentDidUpdate (prevProps, prevState) {
|
||||
}
|
||||
|
||||
renderEmpty () {
|
||||
return (
|
||||
<div styleName='empty'>
|
||||
<div styleName='empty-message'>{OSX ? 'Command(⌘)' : 'Ctrl(^)'} + N<br/>to create a new post</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
let isEmpty = true
|
||||
let view = isEmpty
|
||||
? this.renderEmpty()
|
||||
: null
|
||||
return (
|
||||
<div className='NoteDetail'
|
||||
style={this.props.style}
|
||||
styleName='root'
|
||||
tabIndex='0'
|
||||
>
|
||||
{view}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
NoteDetail.propTypes = {
|
||||
dispatch: PropTypes.func,
|
||||
repositories: PropTypes.array,
|
||||
style: PropTypes.shape({
|
||||
left: PropTypes.number
|
||||
})
|
||||
}
|
||||
|
||||
export default CSSModules(NoteDetail, styles)
|
||||
@@ -1,15 +1,9 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './NoteList.styl'
|
||||
import ReactDOM from 'react-dom'
|
||||
import ModeIcon from 'browser/components/ModeIcon'
|
||||
import moment from 'moment'
|
||||
import _ from 'lodash'
|
||||
|
||||
const electron = require('electron')
|
||||
const remote = electron.remote
|
||||
const ipc = electron.ipcRenderer
|
||||
|
||||
class NoteList extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
@@ -64,63 +58,56 @@ class NoteList extends React.Component {
|
||||
|
||||
// 移動ができなかったらfalseを返す:
|
||||
selectPriorArticle () {
|
||||
let { articles, activeArticle, dispatch } = this.props
|
||||
let targetIndex = articles.indexOf(activeArticle) - 1
|
||||
let targetArticle = articles[targetIndex]
|
||||
return false
|
||||
// let { articles, activeArticle, dispatch } = this.props
|
||||
// let targetIndex = articles.indexOf(activeArticle) - 1
|
||||
// let targetArticle = articles[targetIndex]
|
||||
// return false
|
||||
}
|
||||
|
||||
selectNextArticle () {
|
||||
let { articles, activeArticle, dispatch } = this.props
|
||||
let targetIndex = articles.indexOf(activeArticle) + 1
|
||||
let targetArticle = articles[targetIndex]
|
||||
// let { articles, activeArticle, dispatch } = this.props
|
||||
// let targetIndex = articles.indexOf(activeArticle) + 1
|
||||
// let targetArticle = articles[targetIndex]
|
||||
|
||||
if (targetArticle != null) {
|
||||
dispatch(switchArticle(targetArticle.key))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
handleArticleClick (article) {
|
||||
let { dispatch } = this.props
|
||||
return function (e) {
|
||||
dispatch(switchArticle(article.key))
|
||||
}
|
||||
// if (targetArticle != null) {
|
||||
// dispatch(switchArticle(targetArticle.key))
|
||||
// return true
|
||||
// }
|
||||
// return false
|
||||
}
|
||||
|
||||
handleNoteListKeyDown (e) {
|
||||
if (e.metaKey || e.ctrlKey) return true
|
||||
|
||||
if (e.keyCode === 65 && !e.shiftKey) {
|
||||
e.preventDefault()
|
||||
remote.getCurrentWebContents().send('top-new-post')
|
||||
}
|
||||
// if (e.keyCode === 65 && !e.shiftKey) {
|
||||
// e.preventDefault()
|
||||
// remote.getCurrentWebContents().send('top-new-post')
|
||||
// }
|
||||
|
||||
if (e.keyCode === 65 && e.shiftKey) {
|
||||
e.preventDefault()
|
||||
remote.getCurrentWebContents().send('nav-new-folder')
|
||||
}
|
||||
// if (e.keyCode === 65 && e.shiftKey) {
|
||||
// e.preventDefault()
|
||||
// remote.getCurrentWebContents().send('nav-new-folder')
|
||||
// }
|
||||
|
||||
if (e.keyCode === 68) {
|
||||
e.preventDefault()
|
||||
remote.getCurrentWebContents().send('detail-delete')
|
||||
}
|
||||
// if (e.keyCode === 68) {
|
||||
// e.preventDefault()
|
||||
// remote.getCurrentWebContents().send('detail-delete')
|
||||
// }
|
||||
|
||||
if (e.keyCode === 84) {
|
||||
e.preventDefault()
|
||||
remote.getCurrentWebContents().send('detail-title')
|
||||
}
|
||||
// if (e.keyCode === 84) {
|
||||
// e.preventDefault()
|
||||
// remote.getCurrentWebContents().send('detail-title')
|
||||
// }
|
||||
|
||||
if (e.keyCode === 69) {
|
||||
e.preventDefault()
|
||||
remote.getCurrentWebContents().send('detail-edit')
|
||||
}
|
||||
// if (e.keyCode === 69) {
|
||||
// e.preventDefault()
|
||||
// remote.getCurrentWebContents().send('detail-edit')
|
||||
// }
|
||||
|
||||
if (e.keyCode === 83) {
|
||||
e.preventDefault()
|
||||
remote.getCurrentWebContents().send('detail-save')
|
||||
}
|
||||
// if (e.keyCode === 83) {
|
||||
// e.preventDefault()
|
||||
// remote.getCurrentWebContents().send('detail-save')
|
||||
// }
|
||||
|
||||
if (e.keyCode === 38) {
|
||||
e.preventDefault()
|
||||
@@ -186,41 +173,42 @@ class NoteList extends React.Component {
|
||||
render () {
|
||||
let { location } = this.props
|
||||
let notes = this.notes = this.getNotes()
|
||||
let noteElements = notes.map((note) => {
|
||||
let folder = _.find(note._repository.folders, {key: note.folder})
|
||||
let tagElements = note.tags.map((tag) => {
|
||||
return <span key='tag'>{tag}</span>
|
||||
})
|
||||
let key = `${note._repository.key}-${note.key}`
|
||||
let isActive = location.query.key === key
|
||||
return (
|
||||
<div styleName={isActive
|
||||
? 'item--active'
|
||||
: 'item'
|
||||
}
|
||||
key={key}
|
||||
onClick={(e) => this.handleNoteClick(key)(e)}
|
||||
>
|
||||
<div styleName='item-border'/>
|
||||
<div styleName='item-info'>
|
||||
let noteElements = notes
|
||||
.map((note) => {
|
||||
let folder = _.find(note._repository.folders, {key: note.folder})
|
||||
let tagElements = note.tags.map((tag) => {
|
||||
return <span key='tag'>{tag}</span>
|
||||
})
|
||||
let key = `${note._repository.key}-${note.key}`
|
||||
let isActive = location.query.key === key
|
||||
return (
|
||||
<div styleName={isActive
|
||||
? 'item--active'
|
||||
: 'item'
|
||||
}
|
||||
key={key}
|
||||
onClick={(e) => this.handleNoteClick(key)(e)}
|
||||
>
|
||||
<div styleName='item-border'/>
|
||||
<div styleName='item-info'>
|
||||
|
||||
<div styleName='item-info-left'>
|
||||
<i className='fa fa-cube fa-fw' style={{color: folder.color}}/> {folder.name}
|
||||
</div>
|
||||
|
||||
<div styleName='item-info-right'>
|
||||
{moment(note.createdAt).fromNow()}
|
||||
</div>
|
||||
|
||||
<div styleName='item-info-left'>
|
||||
<i className='fa fa-cube fa-fw' style={{color: folder.color}}/> {folder.name}
|
||||
</div>
|
||||
|
||||
<div styleName='item-info-right'>
|
||||
{moment(note.createdAt).fromNow()}
|
||||
</div>
|
||||
<div styleName='item-title'>{note.title}</div>
|
||||
|
||||
<div styleName='item-tags'><i className='fa fa-tags fa-fw'/>{tagElements.length > 0 ? tagElements : <span>Not tagged yet</span>}</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div styleName='item-title'>{note.title}</div>
|
||||
|
||||
<div styleName='item-tags'><i className='fa fa-tags fa-fw'/>{tagElements.length > 0 ? tagElements : <span>Not tagged yet</span>}</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<div className='NoteList'
|
||||
|
||||
57
browser/main/lib/queue.js
Normal file
57
browser/main/lib/queue.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import Repository from 'browser/lib/Repository'
|
||||
import _ from 'lodash'
|
||||
|
||||
let tasks = []
|
||||
|
||||
function _save (task, repoKey, note) {
|
||||
delete note._repository
|
||||
|
||||
task.status = 'process'
|
||||
|
||||
Repository
|
||||
.find(repoKey)
|
||||
.then((repo) => {
|
||||
return repo.updateNote(note.key, note)
|
||||
})
|
||||
.then((note) => {
|
||||
tasks.splice(tasks.indexOf(task), 1)
|
||||
console.log(tasks)
|
||||
console.info('Note saved', note)
|
||||
})
|
||||
.catch((err) => {
|
||||
tasks.splice(tasks.indexOf(task), 1)
|
||||
console.error('Failed to save note', note)
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
const queueSaving = function (repoKey, note) {
|
||||
let key = `${repoKey}-${note.key}`
|
||||
|
||||
let taskIndex = _.findIndex(tasks, {
|
||||
type: 'SAVE_NOTE',
|
||||
key: key,
|
||||
status: 'idle'
|
||||
})
|
||||
let task = tasks[taskIndex]
|
||||
if (taskIndex < 0) {
|
||||
task = {
|
||||
type: 'SAVE_NOTE',
|
||||
key: key,
|
||||
status: 'idle',
|
||||
timer: null
|
||||
}
|
||||
} else {
|
||||
tasks.splice(taskIndex, 1)
|
||||
window.clearTimeout(task.timer)
|
||||
}
|
||||
|
||||
task.timer = window.setTimeout(() => {
|
||||
_save(task, repoKey, note)
|
||||
}, 1500)
|
||||
tasks.push(task)
|
||||
}
|
||||
|
||||
export default {
|
||||
save: queueSaving
|
||||
}
|
||||
@@ -115,6 +115,22 @@ function repositories (state = initialRepositories, action) {
|
||||
if (targetRepo == null) return state
|
||||
targetRepo.notes.push(action.note)
|
||||
|
||||
return repos
|
||||
}
|
||||
case 'SAVE_NOTE':
|
||||
{
|
||||
let repos = state.slice()
|
||||
let targetRepo = _.find(repos, {key: action.repository})
|
||||
|
||||
if (targetRepo == null) return state
|
||||
|
||||
let targetNoteIndex = _.findIndex(targetRepo.notes, {key: action.note.key})
|
||||
if (targetNoteIndex > -1) {
|
||||
targetRepo.notes.splice(targetNoteIndex, 1, action.note)
|
||||
} else {
|
||||
targetRepo.notes.push(action.note)
|
||||
}
|
||||
|
||||
return repos
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user