mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 09:46:22 +00:00
embed hotkey, config to preferences modal & fix datasaving sequence(Async, Queue)
This commit is contained in:
@@ -3,10 +3,35 @@ import ReactDOM from 'react-dom'
|
|||||||
import modes from '../lib/modes'
|
import modes from '../lib/modes'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
|
||||||
const remote = require('electron').remote
|
const electron = require('electron')
|
||||||
|
const remote = electron.remote
|
||||||
|
const ipc = electron.ipcRenderer
|
||||||
|
|
||||||
const ace = window.ace
|
const ace = window.ace
|
||||||
|
|
||||||
|
function getConfig () {
|
||||||
|
return Object.assign({}, remote.getGlobal('config'))
|
||||||
|
}
|
||||||
|
|
||||||
|
let config = getConfig()
|
||||||
|
|
||||||
export default class CodeEditor extends React.Component {
|
export default class CodeEditor extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.configApplyHandler = e => this.handleConfigApply(e)
|
||||||
|
this.changeHandler = e => this.handleChange(e)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
fontSize: config['editor-font-size'],
|
||||||
|
fontFamily: config['editor-font-family'],
|
||||||
|
indentType: config['editor-indent-type'],
|
||||||
|
indentSize: config['editor-indent-size']
|
||||||
|
}
|
||||||
|
|
||||||
|
this.silentChange = false
|
||||||
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps (nextProps) {
|
||||||
if (nextProps.readOnly !== this.props.readOnly) {
|
if (nextProps.readOnly !== this.props.readOnly) {
|
||||||
this.editor.setReadOnly(!!nextProps.readOnly)
|
this.editor.setReadOnly(!!nextProps.readOnly)
|
||||||
@@ -14,6 +39,7 @@ export default class CodeEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
|
let { article } = this.props
|
||||||
var el = ReactDOM.findDOMNode(this)
|
var el = ReactDOM.findDOMNode(this)
|
||||||
var editor = this.editor = ace.edit(el)
|
var editor = this.editor = ace.edit(el)
|
||||||
editor.$blockScrolling = Infinity
|
editor.$blockScrolling = Infinity
|
||||||
@@ -21,6 +47,7 @@ export default class CodeEditor extends React.Component {
|
|||||||
editor.setTheme('ace/theme/xcode')
|
editor.setTheme('ace/theme/xcode')
|
||||||
editor.moveCursorTo(0, 0)
|
editor.moveCursorTo(0, 0)
|
||||||
editor.setReadOnly(!!this.props.readOnly)
|
editor.setReadOnly(!!this.props.readOnly)
|
||||||
|
editor.setFontSize(this.state.fontSize)
|
||||||
|
|
||||||
editor.commands.addCommand({
|
editor.commands.addCommand({
|
||||||
name: 'Emacs cursor up',
|
name: 'Emacs cursor up',
|
||||||
@@ -45,36 +72,62 @@ export default class CodeEditor extends React.Component {
|
|||||||
})
|
})
|
||||||
|
|
||||||
var session = editor.getSession()
|
var session = editor.getSession()
|
||||||
let mode = _.findWhere(modes, {name: this.props.mode})
|
let mode = _.findWhere(modes, {name: article.mode})
|
||||||
let syntaxMode = mode != null
|
let syntaxMode = mode != null
|
||||||
? mode.mode
|
? mode.mode
|
||||||
: 'text'
|
: 'text'
|
||||||
session.setMode('ace/mode/' + syntaxMode)
|
session.setMode('ace/mode/' + syntaxMode)
|
||||||
|
|
||||||
session.setUseSoftTabs(true)
|
session.setUseSoftTabs(this.state.indentType === 'space')
|
||||||
session.setOption('useWorker', false)
|
session.setTabSize(!isNaN(this.state.indentSize) ? parseInt(this.state.indentSize, 10) : 4)
|
||||||
|
session.setOption('useWorker', true)
|
||||||
session.setUseWrapMode(true)
|
session.setUseWrapMode(true)
|
||||||
session.setValue(this.props.code)
|
session.setValue(this.props.article.content)
|
||||||
|
|
||||||
session.on('change', e => {
|
session.on('change', this.changeHandler)
|
||||||
if (this.props.onChange != null) {
|
|
||||||
var value = editor.getValue()
|
ipc.on('config-apply', this.configApplyHandler)
|
||||||
this.props.onChange(e, value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount () {
|
||||||
|
ipc.removeListener('config-apply', this.configApplyHandler)
|
||||||
|
this.editor.getSession().removeListener('change', this.changeHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate (prevProps, prevState) {
|
||||||
|
var session = this.editor.getSession()
|
||||||
|
if (this.props.article.key !== prevProps.article.key) {
|
||||||
|
session.removeListener('change', this.changeHandler)
|
||||||
|
session.setValue(this.props.article.content)
|
||||||
|
session.getUndoManager().reset()
|
||||||
|
session.on('change', this.changeHandler)
|
||||||
|
}
|
||||||
|
if (prevProps.article.mode !== this.props.article.mode) {
|
||||||
|
let mode = _.findWhere(modes, {name: this.props.article.mode})
|
||||||
|
let syntaxMode = mode != null
|
||||||
|
? mode.mode
|
||||||
|
: 'text'
|
||||||
|
session.setMode('ace/mode/' + syntaxMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleConfigApply () {
|
||||||
|
config = getConfig()
|
||||||
|
this.setState({
|
||||||
|
fontSize: config['editor-font-size'],
|
||||||
|
fontFamily: config['editor-font-family'],
|
||||||
|
indentType: config['editor-indent-type'],
|
||||||
|
indentSize: config['editor-indent-size']
|
||||||
|
}, function () {
|
||||||
|
var session = this.editor.getSession()
|
||||||
|
session.setUseSoftTabs(this.state.indentType === 'space')
|
||||||
|
session.setTabSize(!isNaN(this.state.indentSize) ? parseInt(this.state.indentSize, 10) : 4)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
handleChange (e) {
|
||||||
componentDidUpdate (prevProps) {
|
if (this.props.onChange) {
|
||||||
var session = this.editor.getSession()
|
var value = this.editor.getValue()
|
||||||
if (this.editor.getValue() !== this.props.code) {
|
this.props.onChange(value)
|
||||||
session.setValue(this.props.code)
|
|
||||||
}
|
|
||||||
if (prevProps.mode !== this.props.mode) {
|
|
||||||
let mode = _.findWhere(modes, {name: this.props.mode})
|
|
||||||
let syntaxMode = mode != null
|
|
||||||
? mode.mode
|
|
||||||
: 'text'
|
|
||||||
session.setMode('ace/mode/' + syntaxMode)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,14 +149,23 @@ export default class CodeEditor extends React.Component {
|
|||||||
|
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<div className={this.props.className == null ? 'CodeEditor' : 'CodeEditor ' + this.props.className}></div>
|
<div
|
||||||
|
className={this.props.className == null ? 'CodeEditor' : 'CodeEditor ' + this.props.className}
|
||||||
|
style={{
|
||||||
|
fontSize: this.state.fontSize,
|
||||||
|
fontFamily: this.state.fontFamily
|
||||||
|
}}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeEditor.propTypes = {
|
CodeEditor.propTypes = {
|
||||||
code: PropTypes.string,
|
article: PropTypes.shape({
|
||||||
|
content: PropTypes.string,
|
||||||
mode: PropTypes.string,
|
mode: PropTypes.string,
|
||||||
|
key: PropTypes.string
|
||||||
|
}),
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
onBlur: PropTypes.func,
|
onBlur: PropTypes.func,
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import React, { PropTypes } from 'react'
|
|||||||
import markdown from '../lib/markdown'
|
import markdown from '../lib/markdown'
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
import sanitizeHtml from '@rokt33r/sanitize-html'
|
import sanitizeHtml from '@rokt33r/sanitize-html'
|
||||||
import hljs from 'highlight.js'
|
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
|
||||||
const electron = require('electron')
|
const electron = require('electron')
|
||||||
const shell = electron.shell
|
const shell = electron.shell
|
||||||
|
const remote = electron.remote
|
||||||
|
const ipc = electron.ipcRenderer
|
||||||
|
|
||||||
const katex = window.katex
|
const katex = window.katex
|
||||||
|
|
||||||
@@ -65,41 +66,43 @@ function math2Katex (display) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getConfig () {
|
||||||
|
return Object.assign({}, remote.getGlobal('config'))
|
||||||
|
}
|
||||||
|
|
||||||
|
let config = getConfig()
|
||||||
|
|
||||||
export default class MarkdownPreview extends React.Component {
|
export default class MarkdownPreview extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.configApplyHandler = e => this.handleConfigApply(e)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
fontSize: config['preview-font-size'],
|
||||||
|
fontFamily: config['preview-font-family']
|
||||||
|
}
|
||||||
|
}
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
this.addListener()
|
this.addListener()
|
||||||
// this.renderCode()
|
|
||||||
this.renderMath()
|
this.renderMath()
|
||||||
|
ipc.on('config-apply', this.configApplyHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate () {
|
componentDidUpdate () {
|
||||||
this.addListener()
|
this.addListener()
|
||||||
// this.renderCode()
|
|
||||||
this.renderMath()
|
this.renderMath()
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
this.removeListener()
|
this.removeListener()
|
||||||
|
ipc.removeListener('config-apply', this.configApplyHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUpdate () {
|
componentWillUpdate () {
|
||||||
this.removeListener()
|
this.removeListener()
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCode () {
|
|
||||||
let codes = ReactDOM.findDOMNode(this).querySelectorAll('code:not(.rendered)')
|
|
||||||
Array.prototype.forEach.call(codes, el => {
|
|
||||||
let matched = el.className.match(/language-(.+)/)
|
|
||||||
let lang = matched ? matched[1] : null
|
|
||||||
try {
|
|
||||||
let result = hljs.highlight(lang, el.innerHTML)
|
|
||||||
el.innerHTML = result.value
|
|
||||||
el.className += ' rendered'
|
|
||||||
} catch (e) {
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
renderMath () {
|
renderMath () {
|
||||||
let inline = ReactDOM.findDOMNode(this).querySelectorAll('span.math')
|
let inline = ReactDOM.findDOMNode(this).querySelectorAll('span.math')
|
||||||
Array.prototype.forEach.call(inline, math2Katex(false))
|
Array.prototype.forEach.call(inline, math2Katex(false))
|
||||||
@@ -157,6 +160,14 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleConfigApply () {
|
||||||
|
config = getConfig()
|
||||||
|
this.setState({
|
||||||
|
fontSize: config['preview-font-size'],
|
||||||
|
fontFamily: config['preview-font-family']
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let isEmpty = this.props.content.trim().length === 0
|
let isEmpty = this.props.content.trim().length === 0
|
||||||
let content = isEmpty
|
let content = isEmpty
|
||||||
@@ -174,6 +185,7 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
onMouseMove={e => this.handleMouseMove(e)}
|
onMouseMove={e => this.handleMouseMove(e)}
|
||||||
onMouseUp={e => this.handleMouseUp(e)}
|
onMouseUp={e => this.handleMouseUp(e)}
|
||||||
dangerouslySetInnerHTML={{__html: ' ' + content}}
|
dangerouslySetInnerHTML={{__html: ' ' + content}}
|
||||||
|
style={{fontSize: this.state.fontSize, fontFamily: this.state.fontFamily}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export default class FinderDetail extends React.Component {
|
|||||||
<div className='content'>
|
<div className='content'>
|
||||||
{activeArticle.mode === 'markdown'
|
{activeArticle.mode === 'markdown'
|
||||||
? <MarkdownPreview content={activeArticle.content}/>
|
? <MarkdownPreview content={activeArticle.content}/>
|
||||||
: <CodeEditor readOnly mode={activeArticle.mode} code={activeArticle.content}/>
|
: <CodeEditor readOnly article={activeArticle}/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export function init () {
|
|||||||
folders: [defaultFolder],
|
folders: [defaultFolder],
|
||||||
version: '0.4'
|
version: '0.4'
|
||||||
}
|
}
|
||||||
jetpack.write(getLocalPath(), data)
|
queueSave()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,18 +114,44 @@ export function getData () {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let timer = null
|
||||||
|
let isSaving = false
|
||||||
|
let saveAgain = false
|
||||||
|
function saveData () {
|
||||||
|
timer = null
|
||||||
|
isSaving = true
|
||||||
|
jetpack.writeAsync(getLocalPath(), data)
|
||||||
|
.then(function () {
|
||||||
|
isSaving = false
|
||||||
|
if (saveAgain) {
|
||||||
|
saveAgain = false
|
||||||
|
queueSave()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function queueSave () {
|
||||||
|
if (!isSaving) {
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer)
|
||||||
|
}
|
||||||
|
timer = setTimeout(saveData, 3000)
|
||||||
|
} else {
|
||||||
|
saveAgain = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function setArticles (articles) {
|
export function setArticles (articles) {
|
||||||
if (!_.isArray(articles)) throw new Error('Articles must be an array')
|
if (!_.isArray(articles)) throw new Error('Articles must be an array')
|
||||||
let data = getData()
|
let data = getData()
|
||||||
data.articles = articles
|
data.articles = articles
|
||||||
jetpack.write(getLocalPath(), data)
|
queueSave()
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setFolders (folders) {
|
export function setFolders (folders) {
|
||||||
if (!_.isArray(folders)) throw new Error('Folders must be an array')
|
if (!_.isArray(folders)) throw new Error('Folders must be an array')
|
||||||
let data = getData()
|
let data = getData()
|
||||||
data.folders = folders
|
data.folders = folders
|
||||||
jetpack.write(getLocalPath(), data)
|
queueSave()
|
||||||
}
|
}
|
||||||
|
|
||||||
function isFinderCalled () {
|
function isFinderCalled () {
|
||||||
|
|||||||
@@ -17,6 +17,14 @@ export default class ArticleEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps (nextProps) {
|
||||||
|
if (nextProps.article.key !== this.props.article.key) {
|
||||||
|
this.setState({
|
||||||
|
content: this.props.article.content
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resetCursorPosition () {
|
resetCursorPosition () {
|
||||||
this.setState({
|
this.setState({
|
||||||
cursorPosition: null,
|
cursorPosition: null,
|
||||||
@@ -77,13 +85,19 @@ export default class ArticleEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleBlurCodeEditor () {
|
handleBlurCodeEditor () {
|
||||||
if (this.props.mode === 'markdown') {
|
let { article } = this.props
|
||||||
|
if (article.mode === 'markdown') {
|
||||||
this.switchPreviewMode()
|
this.switchPreviewMode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleCodeEditorChange (value) {
|
||||||
|
this.props.onChange(value)
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let showPreview = this.props.mode === 'markdown' && this.state.status === PREVIEW_MODE
|
let { article } = this.props
|
||||||
|
let showPreview = article.mode === 'markdown' && this.state.status === PREVIEW_MODE
|
||||||
if (showPreview) {
|
if (showPreview) {
|
||||||
return (
|
return (
|
||||||
<div className='ArticleEditor'>
|
<div className='ArticleEditor'>
|
||||||
@@ -92,7 +106,7 @@ export default class ArticleEditor extends React.Component {
|
|||||||
onMouseUp={e => this.handlePreviewMouseUp(e)}
|
onMouseUp={e => this.handlePreviewMouseUp(e)}
|
||||||
onMouseDown={e => this.handlePreviewMouseDown(e)}
|
onMouseDown={e => this.handlePreviewMouseDown(e)}
|
||||||
onMouseMove={e => this.handlePreviewMouseMove(e)}
|
onMouseMove={e => this.handlePreviewMouseMove(e)}
|
||||||
content={this.props.content}
|
content={article.content}
|
||||||
/>
|
/>
|
||||||
<div className='ArticleDetail-panel-content-tooltip'>Click to Edit</div>
|
<div className='ArticleDetail-panel-content-tooltip'>Click to Edit</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -104,11 +118,10 @@ export default class ArticleEditor extends React.Component {
|
|||||||
<CodeEditor
|
<CodeEditor
|
||||||
ref='editor'
|
ref='editor'
|
||||||
onBlur={e => this.handleBlurCodeEditor(e)}
|
onBlur={e => this.handleBlurCodeEditor(e)}
|
||||||
onChange={this.props.onChange}
|
onChange={value => this.handleCodeEditorChange(value)}
|
||||||
mode={this.props.mode}
|
article={article}
|
||||||
code={this.props.content}
|
|
||||||
/>
|
/>
|
||||||
{this.props.mode === 'markdown'
|
{article.mode === 'markdown'
|
||||||
? (
|
? (
|
||||||
<div className='ArticleDetail-panel-content-tooltip'>Press ESC to watch Preview</div>
|
<div className='ArticleDetail-panel-content-tooltip'>Press ESC to watch Preview</div>
|
||||||
)
|
)
|
||||||
@@ -120,7 +133,10 @@ export default class ArticleEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ArticleEditor.propTypes = {
|
ArticleEditor.propTypes = {
|
||||||
|
article: PropTypes.shape({
|
||||||
content: PropTypes.string,
|
content: PropTypes.string,
|
||||||
mode: PropTypes.string,
|
key: PropTypes.string,
|
||||||
|
mode: PropTypes.string
|
||||||
|
}),
|
||||||
onChange: PropTypes.func
|
onChange: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ import DeleteArticleModal from '../../modal/DeleteArticleModal'
|
|||||||
import ArticleEditor from './ArticleEditor'
|
import ArticleEditor from './ArticleEditor'
|
||||||
const electron = require('electron')
|
const electron = require('electron')
|
||||||
const ipc = electron.ipcRenderer
|
const ipc = electron.ipcRenderer
|
||||||
const remote = electron.remote
|
let count = 0
|
||||||
const { Menu, MenuItem } = remote
|
// const remote = electron.remote
|
||||||
|
// const { Menu, MenuItem } = remote
|
||||||
// const othersMenu = new Menu()
|
// const othersMenu = new Menu()
|
||||||
// othersMenu.append(new MenuItem({
|
// othersMenu.append(new MenuItem({
|
||||||
// label: 'Delete Post',
|
// label: 'Delete Post',
|
||||||
@@ -152,18 +152,6 @@ export default class ArticleDetail extends React.Component {
|
|||||||
ipc.removeListener('detail-edit', this.editHandler)
|
ipc.removeListener('detail-edit', this.editHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
|
||||||
let nextArticle = nextProps.activeArticle
|
|
||||||
let nextModified = nextArticle != null ? _.findWhere(nextProps.modified, {key: nextArticle.key}) : null
|
|
||||||
|
|
||||||
let article = Object.assign({content: ''}, nextProps.activeArticle, nextModified)
|
|
||||||
let nextState = {
|
|
||||||
article
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState(nextState)
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate (prevProps, prevState) {
|
componentDidUpdate (prevProps, prevState) {
|
||||||
if (this.props.activeArticle == null || prevProps.activeArticle == null || this.props.activeArticle.key !== prevProps.activeArticle.key) {
|
if (this.props.activeArticle == null || prevProps.activeArticle == null || this.props.activeArticle.key !== prevProps.activeArticle.key) {
|
||||||
this.refs.editor.resetCursorPosition()
|
this.refs.editor.resetCursorPosition()
|
||||||
@@ -175,20 +163,6 @@ export default class ArticleDetail extends React.Component {
|
|||||||
ReactDOM.findDOMNode(this.refs.title).select()
|
ReactDOM.findDOMNode(this.refs.title).select()
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheArticle () {
|
|
||||||
let { dispatch, status, folders } = this.props
|
|
||||||
|
|
||||||
let input = Object.assign({}, this.props.activeArticle.key, this.state.article, {updatedAt: new Date()})
|
|
||||||
|
|
||||||
dispatch(updateArticle(input))
|
|
||||||
|
|
||||||
let targetFolderKey = this.state.article.FolderKey
|
|
||||||
if (status.targetFolders.length > 0) {
|
|
||||||
let targetFolder = _.findWhere(folders, {key: targetFolderKey})
|
|
||||||
dispatch(switchFolder(targetFolder.name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderEmpty () {
|
renderEmpty () {
|
||||||
return (
|
return (
|
||||||
<div className='ArticleDetail empty'>
|
<div className='ArticleDetail empty'>
|
||||||
@@ -199,68 +173,64 @@ export default class ArticleDetail extends React.Component {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSaveButtonClick (e) {
|
|
||||||
// let { dispatch, folders, status } = this.props
|
|
||||||
|
|
||||||
// let targetFolderKey = this.state.article.FolderKey
|
|
||||||
// dispatch(saveArticle(this.props.activeArticle.key, this.state.article), true)
|
|
||||||
// if (status.targetFolders.length > 0) {
|
|
||||||
// let targetFolder = _.findWhere(folders, {key: targetFolderKey})
|
|
||||||
// dispatch(switchFolder(targetFolder.name))
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
handleOthersButtonClick (e) {
|
handleOthersButtonClick (e) {
|
||||||
this.deleteHandler()
|
this.deleteHandler()
|
||||||
}
|
}
|
||||||
|
|
||||||
handleFolderKeyChange (e) {
|
handleFolderKeyChange (e) {
|
||||||
let article = this.state.article
|
let { dispatch, activeArticle, status, folders } = this.props
|
||||||
article.FolderKey = e.target.value
|
let article = Object.assign({}, activeArticle, {
|
||||||
|
FolderKey: e.target.value,
|
||||||
|
updatedAt: new Date()
|
||||||
|
})
|
||||||
|
|
||||||
this.setState({article: article}, () => this.cacheArticle())
|
dispatch(updateArticle(article))
|
||||||
|
|
||||||
|
let targetFolderKey = this.state.article.FolderKey
|
||||||
|
if (status.targetFolders.length > 0) {
|
||||||
|
let targetFolder = _.findWhere(folders, {key: targetFolderKey})
|
||||||
|
dispatch(switchFolder(targetFolder.name))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTitleChange (e) {
|
handleTitleChange (e) {
|
||||||
let { article } = this.state
|
let { dispatch, activeArticle } = this.props
|
||||||
article.title = e.target.value
|
let article = Object.assign({}, activeArticle, {
|
||||||
|
title: e.target.value,
|
||||||
this.setState({
|
updatedAt: new Date()
|
||||||
article
|
})
|
||||||
}, () => this.cacheArticle())
|
dispatch(updateArticle(article))
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTagsChange (newTag, tags) {
|
handleTagsChange (newTag, tags) {
|
||||||
let article = this.state.article
|
let { dispatch, activeArticle } = this.props
|
||||||
article.tags = tags
|
let article = Object.assign({}, activeArticle, {
|
||||||
|
tags: tags,
|
||||||
|
updatedAt: new Date()
|
||||||
|
})
|
||||||
|
|
||||||
this.setState({
|
dispatch(updateArticle(article))
|
||||||
article
|
|
||||||
}, () => this.cacheArticle())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModeChange (value) {
|
handleModeChange (value) {
|
||||||
let { article } = this.state
|
let { dispatch, activeArticle } = this.props
|
||||||
article.mode = value
|
let article = Object.assign({}, activeArticle, {
|
||||||
this.setState({
|
mode: value,
|
||||||
article
|
updatedAt: new Date()
|
||||||
}, () => this.cacheArticle())
|
|
||||||
}
|
|
||||||
|
|
||||||
handleContentChange (e, value) {
|
|
||||||
let { article } = this.state
|
|
||||||
article.content = value
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
article
|
|
||||||
}, () => this.cacheArticle())
|
|
||||||
}
|
|
||||||
|
|
||||||
handleCodeEditorBlur (e) {
|
|
||||||
if (this.state.article.mode === 'markdown' && !this.state.previewMode) {
|
|
||||||
this.setState({
|
|
||||||
previewMode: true
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
dispatch(updateArticle(article))
|
||||||
|
}
|
||||||
|
|
||||||
|
handleContentChange (value) {
|
||||||
|
let { dispatch, activeArticle } = this.props
|
||||||
|
if (activeArticle.content !== value) {
|
||||||
|
let article = Object.assign({}, activeArticle, {
|
||||||
|
content: value,
|
||||||
|
updatedAt: new Date()
|
||||||
|
})
|
||||||
|
|
||||||
|
dispatch(updateArticle(article))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,13 +240,6 @@ export default class ArticleDetail extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUncache (e) {
|
|
||||||
if (this.props.activeArticle) {
|
|
||||||
let { dispatch, activeArticle } = this.props
|
|
||||||
dispatch(uncacheArticle(activeArticle.key))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleTitleKeyDown (e) {
|
handleTitleKeyDown (e) {
|
||||||
if (e.keyCode === 9 && !e.shiftKey) {
|
if (e.keyCode === 9 && !e.shiftKey) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
@@ -312,7 +275,7 @@ export default class ArticleDetail extends React.Component {
|
|||||||
<div className='ArticleDetail-info-row'>
|
<div className='ArticleDetail-info-row'>
|
||||||
<select
|
<select
|
||||||
className='ArticleDetail-info-folder'
|
className='ArticleDetail-info-folder'
|
||||||
value={this.state.article.FolderKey}
|
value={activeArticle.FolderKey}
|
||||||
onChange={e => this.handleFolderKeyChange(e)}
|
onChange={e => this.handleFolderKeyChange(e)}
|
||||||
>
|
>
|
||||||
{folderOptions}
|
{folderOptions}
|
||||||
@@ -321,7 +284,7 @@ export default class ArticleDetail extends React.Component {
|
|||||||
children={
|
children={
|
||||||
isUnsaved
|
isUnsaved
|
||||||
? <span> <span className='unsaved-mark'>●</span> Unsaved</span>
|
? <span> <span className='unsaved-mark'>●</span> Unsaved</span>
|
||||||
: `Created : ${moment(this.state.article.createdAt).format('YYYY/MM/DD')} Updated : ${moment(this.state.article.updatedAt).format('YYYY/MM/DD')}`
|
: `Created : ${moment(activeArticle.createdAt).format('YYYY/MM/DD')} Updated : ${moment(activeArticle.updatedAt).format('YYYY/MM/DD')}`
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -351,7 +314,7 @@ export default class ArticleDetail extends React.Component {
|
|||||||
{status.isTutorialOpen ? editDeleteTutorialElement : null}
|
{status.isTutorialOpen ? editDeleteTutorialElement : null}
|
||||||
<div>
|
<div>
|
||||||
<TagSelect
|
<TagSelect
|
||||||
tags={this.state.article.tags}
|
tags={activeArticle.tags}
|
||||||
onChange={(tags, tag) => this.handleTagsChange(tags, tag)}
|
onChange={(tags, tag) => this.handleTagsChange(tags, tag)}
|
||||||
suggestTags={tags}
|
suggestTags={tags}
|
||||||
/>
|
/>
|
||||||
@@ -367,7 +330,7 @@ export default class ArticleDetail extends React.Component {
|
|||||||
onKeyDown={e => this.handleTitleKeyDown(e)}
|
onKeyDown={e => this.handleTitleKeyDown(e)}
|
||||||
placeholder='(Untitled)'
|
placeholder='(Untitled)'
|
||||||
ref='title'
|
ref='title'
|
||||||
value={this.state.article.title}
|
value={activeArticle.title}
|
||||||
onChange={e => this.handleTitleChange(e)}
|
onChange={e => this.handleTitleChange(e)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -375,16 +338,15 @@ export default class ArticleDetail extends React.Component {
|
|||||||
ref='mode'
|
ref='mode'
|
||||||
onChange={e => this.handleModeChange(e)}
|
onChange={e => this.handleModeChange(e)}
|
||||||
onKeyDown={e => this.handleModeSelectKeyDown(e)}
|
onKeyDown={e => this.handleModeSelectKeyDown(e)}
|
||||||
value={this.state.article.mode}
|
value={activeArticle.mode}
|
||||||
className='ArticleDetail-panel-header-mode'
|
className='ArticleDetail-panel-header-mode'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{status.isTutorialOpen ? modeSelectTutorialElement : null}
|
{status.isTutorialOpen ? modeSelectTutorialElement : null}
|
||||||
<ArticleEditor
|
<ArticleEditor
|
||||||
ref='editor'
|
ref='editor'
|
||||||
content={this.state.article.content}
|
article={activeArticle}
|
||||||
mode={this.state.article.mode}
|
onChange={content => this.handleContentChange(content)}
|
||||||
onChange={(e, content) => this.handleContentChange(e, content)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ export default class ArticleList extends React.Component {
|
|||||||
let modifiedArticle = _.findWhere(modified, {key: article.key})
|
let modifiedArticle = _.findWhere(modified, {key: article.key})
|
||||||
let originalArticle = article
|
let originalArticle = article
|
||||||
if (modifiedArticle) {
|
if (modifiedArticle) {
|
||||||
article = Object.assign({}, article, modifiedArticle)
|
article = Object.assign({}, article)
|
||||||
}
|
}
|
||||||
let tagElements = Array.isArray(article.tags) && article.tags.length > 0
|
let tagElements = Array.isArray(article.tags) && article.tags.length > 0
|
||||||
? article.tags.slice().map(tag => {
|
? article.tags.slice().map(tag => {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default class CreateNewFolder extends React.Component {
|
|||||||
name,
|
name,
|
||||||
color
|
color
|
||||||
}
|
}
|
||||||
|
console.log(input)
|
||||||
try {
|
try {
|
||||||
store.dispatch(createFolder(input))
|
store.dispatch(createFolder(input))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -6,10 +6,13 @@ const electron = require('electron')
|
|||||||
const ipc = electron.ipcRenderer
|
const ipc = electron.ipcRenderer
|
||||||
const remote = electron.remote
|
const remote = electron.remote
|
||||||
|
|
||||||
|
const OSX = global.process.platform === 'darwin'
|
||||||
|
|
||||||
export default class AppSettingTab extends React.Component {
|
export default class AppSettingTab extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
super(props)
|
super(props)
|
||||||
let keymap = remote.getGlobal('keymap')
|
let keymap = Object.assign({}, remote.getGlobal('keymap'))
|
||||||
|
let config = Object.assign({}, remote.getGlobal('config'))
|
||||||
let userName = props.user != null ? props.user.name : null
|
let userName = props.user != null ? props.user.name : null
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
@@ -18,10 +21,10 @@ export default class AppSettingTab extends React.Component {
|
|||||||
alert: null
|
alert: null
|
||||||
},
|
},
|
||||||
userAlert: null,
|
userAlert: null,
|
||||||
keymap: {
|
keymap: keymap,
|
||||||
toggleFinder: keymap.toggleFinder
|
keymapAlert: null,
|
||||||
},
|
config: config,
|
||||||
keymapAlert: null
|
configAlert: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,21 +51,33 @@ export default class AppSettingTab extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
submitHotKey () {
|
submitHotKey () {
|
||||||
ipc.send('hotkeyUpdated', {
|
ipc.send('hotkeyUpdated', this.state.keymap)
|
||||||
toggleFinder: this.state.keymap.toggleFinder
|
}
|
||||||
})
|
|
||||||
|
submitConfig () {
|
||||||
|
ipc.send('configUpdated', this.state.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSaveButtonClick (e) {
|
handleSaveButtonClick (e) {
|
||||||
this.submitHotKey()
|
this.submitHotKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleConfigSaveButtonClick (e) {
|
||||||
|
this.submitConfig()
|
||||||
|
}
|
||||||
|
|
||||||
handleKeyDown (e) {
|
handleKeyDown (e) {
|
||||||
if (e.keyCode === 13) {
|
if (e.keyCode === 13) {
|
||||||
this.submitHotKey()
|
this.submitHotKey()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleConfigKeyDown (e) {
|
||||||
|
if (e.keyCode === 13) {
|
||||||
|
this.submitConfig()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleNameSaveButtonClick (e) {
|
handleNameSaveButtonClick (e) {
|
||||||
let { dispatch } = this.props
|
let { dispatch } = this.props
|
||||||
|
|
||||||
@@ -104,8 +119,61 @@ export default class AppSettingTab extends React.Component {
|
|||||||
{userAlertElement}
|
{userAlertElement}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className='section'>
|
||||||
|
<div className='sectionTitle'>Text</div>
|
||||||
|
<div className='sectionInput'>
|
||||||
|
<label>Editor Font Size</label>
|
||||||
|
<input valueLink={this.linkState('config.editor-font-size')} onKeyDown={e => this.handleConfigKeyDown(e)} type='text'/>
|
||||||
|
</div>
|
||||||
|
<div className='sectionInput'>
|
||||||
|
<label>Editor Font Family</label>
|
||||||
|
<input valueLink={this.linkState('config.editor-font-family')} onKeyDown={e => this.handleConfigKeyDown(e)} type='text'/>
|
||||||
|
</div>
|
||||||
|
<div className='sectionSelect'>
|
||||||
|
<label>Editor Indent Style</label>
|
||||||
|
<div className='sectionSelect-input'>
|
||||||
|
type
|
||||||
|
<select valueLink={this.linkState('config.editor-indent-type')}>
|
||||||
|
<option value='space'>Space</option>
|
||||||
|
<option value='tab'>Tab</option>
|
||||||
|
</select>
|
||||||
|
size
|
||||||
|
<select valueLink={this.linkState('config.editor-indent-size')}>
|
||||||
|
<option value='2'>2</option>
|
||||||
|
<option value='4'>4</option>
|
||||||
|
<option value='8'>8</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='sectionInput'>
|
||||||
|
<label>Preview Font Size</label>
|
||||||
|
<input valueLink={this.linkState('config.preview-font-size')} onKeyDown={e => this.handleConfigKeyDown(e)} type='text'/>
|
||||||
|
</div>
|
||||||
|
<div className='sectionInput'>
|
||||||
|
<label>Preview Font Family</label>
|
||||||
|
<input valueLink={this.linkState('config.preview-font-family')} onKeyDown={e => this.handleConfigKeyDown(e)} type='text'/>
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
true// !OSX
|
||||||
|
? (
|
||||||
|
<div className='sectionInput'>
|
||||||
|
<label>Direct write(Windows only)</label>
|
||||||
|
<input disabled={OSX} onKeyDown={e => this.handleConfigKeyDown(e)} type='checkbox'/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
|
||||||
|
<div className='sectionConfirm'>
|
||||||
|
<button onClick={e => this.handleConfigSaveButtonClick(e)}>Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className='section'>
|
<div className='section'>
|
||||||
<div className='sectionTitle'>Hotkey</div>
|
<div className='sectionTitle'>Hotkey</div>
|
||||||
|
<div className='sectionInput'>
|
||||||
|
<label>Toggle Main</label>
|
||||||
|
<input onKeyDown={e => this.handleKeyDown(e)} valueLink={this.linkState('keymap.toggleMain')} type='text'/>
|
||||||
|
</div>
|
||||||
<div className='sectionInput'>
|
<div className='sectionInput'>
|
||||||
<label>Toggle Finder(popup)</label>
|
<label>Toggle Finder(popup)</label>
|
||||||
<input onKeyDown={e => this.handleKeyDown(e)} valueLink={this.linkState('keymap.toggleFinder')} type='text'/>
|
<input onKeyDown={e => this.handleKeyDown(e)} valueLink={this.linkState('keymap.toggleFinder')} type='text'/>
|
||||||
@@ -142,5 +210,8 @@ export default class AppSettingTab extends React.Component {
|
|||||||
|
|
||||||
AppSettingTab.prototype.linkState = linkState
|
AppSettingTab.prototype.linkState = linkState
|
||||||
AppSettingTab.propTypes = {
|
AppSettingTab.propTypes = {
|
||||||
|
user: PropTypes.shape({
|
||||||
|
name: PropTypes.string
|
||||||
|
}),
|
||||||
dispatch: PropTypes.func
|
dispatch: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export default class Tutorial extends React.Component {
|
|||||||
<div className='title'>Easy to access with Finder</div>
|
<div className='title'>Easy to access with Finder</div>
|
||||||
<div className='content'>
|
<div className='content'>
|
||||||
The Finder helps you organize all of the files and documents.<br/>
|
The Finder helps you organize all of the files and documents.<br/>
|
||||||
There is a short-cut key [control + shift + tab] to open the Finder.<br/>
|
There is a short-cut key [⌘ + alt + s] to open the Finder.<br/>
|
||||||
It is available to save your articles on the Clipboard<br/>
|
It is available to save your articles on the Clipboard<br/>
|
||||||
by selecting your file with pressing Enter key,<br/>
|
by selecting your file with pressing Enter key,<br/>
|
||||||
and to paste the contents of the Clipboard with [{process.platform === 'darwin' ? 'Command' : 'Control'}-V]
|
and to paste the contents of the Clipboard with [{process.platform === 'darwin' ? 'Command' : 'Control'}-V]
|
||||||
|
|||||||
@@ -89,6 +89,31 @@ iptFocusBorderColor = #369DCD
|
|||||||
outline none
|
outline none
|
||||||
&:focus
|
&:focus
|
||||||
border-color iptFocusBorderColor
|
border-color iptFocusBorderColor
|
||||||
|
&>.sectionSelect
|
||||||
|
|
||||||
|
margin-bottom 5px
|
||||||
|
clearfix()
|
||||||
|
height 33px
|
||||||
|
label
|
||||||
|
width 150px
|
||||||
|
padding-left 15px
|
||||||
|
float left
|
||||||
|
line-height 33px
|
||||||
|
.sectionSelect-input
|
||||||
|
float left
|
||||||
|
select
|
||||||
|
width 80px
|
||||||
|
height 25px
|
||||||
|
margin-top 4px
|
||||||
|
border-radius 5px
|
||||||
|
border 1px solid borderColor
|
||||||
|
padding 0 10px
|
||||||
|
font-size 14px
|
||||||
|
outline none
|
||||||
|
margin-left 5px
|
||||||
|
margin-right 15px
|
||||||
|
&:focus
|
||||||
|
border-color iptFocusBorderColor
|
||||||
&>.sectionConfirm
|
&>.sectionConfirm
|
||||||
clearfix()
|
clearfix()
|
||||||
padding 5px 15px
|
padding 5px 15px
|
||||||
@@ -624,10 +649,3 @@ iptFocusBorderColor = #369DCD
|
|||||||
color brandColor
|
color brandColor
|
||||||
&:hover
|
&:hover
|
||||||
color lighten(brandColor, 10%)
|
color lighten(brandColor, 10%)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
53
lib/config.js
Normal file
53
lib/config.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
const electron = require('electron')
|
||||||
|
const app = electron.app
|
||||||
|
const ipc = electron.ipcMain
|
||||||
|
const jetpack = require('fs-jetpack')
|
||||||
|
const mainWindow = require('./main-window')
|
||||||
|
|
||||||
|
const defaultConfig = {
|
||||||
|
'editor-font-size': '14',
|
||||||
|
'editor-font-family': 'Monaco, Ubuntu Mono, Consolas, source-code-pro, monospace',
|
||||||
|
'editor-indent-type': 'space',
|
||||||
|
'editor-indent-size': '4',
|
||||||
|
'preview-font-size': '14',
|
||||||
|
'preview-font-family': 'Lato, helvetica, arial, sans-serif',
|
||||||
|
'disable-direct-write': false
|
||||||
|
}
|
||||||
|
const configFile = 'config.json'
|
||||||
|
|
||||||
|
var userDataPath = app.getPath('userData')
|
||||||
|
|
||||||
|
function getConfig () {
|
||||||
|
var userDataPath = app.getPath('userData')
|
||||||
|
if (jetpack.cwd(userDataPath).exists(configFile)) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(jetpack.cwd(userDataPath).read(configFile, 'utf-8'))
|
||||||
|
} catch (err) {}
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveConfig () {
|
||||||
|
var content
|
||||||
|
try {
|
||||||
|
content = JSON.stringify(global.config)
|
||||||
|
} catch (e) {
|
||||||
|
global.config = {}
|
||||||
|
content = JSON.stringify(global.config)
|
||||||
|
}
|
||||||
|
jetpack.cwd(userDataPath).file(configFile, { content })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init
|
||||||
|
global.config = Object.assign({}, defaultConfig, getConfig())
|
||||||
|
|
||||||
|
function applyConfig () {
|
||||||
|
mainWindow.webContents.send('config-apply')
|
||||||
|
}
|
||||||
|
|
||||||
|
ipc.on('configUpdated', function (event, newConfig) {
|
||||||
|
global.config = Object.assign({}, defaultConfig, global.config, newConfig)
|
||||||
|
saveConfig()
|
||||||
|
applyConfig()
|
||||||
|
})
|
||||||
|
|
||||||
122
lib/hotkey.js
122
lib/hotkey.js
@@ -1,55 +1,41 @@
|
|||||||
const electron = require('electron')
|
const electron = require('electron')
|
||||||
const app = electron.app
|
const app = electron.app
|
||||||
const ipc = electron.ipcMain
|
const ipc = electron.ipcMain
|
||||||
|
const Menu = electron.Menu
|
||||||
const globalShortcut = electron.globalShortcut
|
const globalShortcut = electron.globalShortcut
|
||||||
const jetpack = require('fs-jetpack')
|
const jetpack = require('fs-jetpack')
|
||||||
const mainWindow = require('./main-window')
|
const mainWindow = require('./main-window')
|
||||||
const nodeIpc = require('@rokt33r/node-ipc')
|
const nodeIpc = require('@rokt33r/node-ipc')
|
||||||
|
|
||||||
|
const defaultKeymap = {
|
||||||
|
toggleFinder: 'Cmd + alt + s',
|
||||||
|
toggleMain: 'Cmd + alt + v'
|
||||||
|
}
|
||||||
|
const keymapFilename = 'keymap.json'
|
||||||
|
|
||||||
var userDataPath = app.getPath('userData')
|
var userDataPath = app.getPath('userData')
|
||||||
if (!jetpack.cwd(userDataPath).exists('keymap.json')) {
|
|
||||||
jetpack.cwd(userDataPath).file('keymap.json', {content: '{}'})
|
function getKeymap () {
|
||||||
}
|
var userDataPath = app.getPath('userData')
|
||||||
|
if (jetpack.cwd(userDataPath).exists(keymapFilename)) {
|
||||||
try {
|
try {
|
||||||
global.keymap = JSON.parse(jetpack.cwd(userDataPath).read('keymap.json', 'utf-8'))
|
return JSON.parse(jetpack.cwd(userDataPath).read(keymapFilename, 'utf-8'))
|
||||||
} catch (err) {
|
} catch (err) {}
|
||||||
jetpack.cwd(userDataPath).file('keymap.json', {content: '{}'})
|
}
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveKeymap () {
|
||||||
|
var content
|
||||||
|
try {
|
||||||
|
content = JSON.stringify(global.keymap)
|
||||||
|
} catch (e) {
|
||||||
global.keymap = {}
|
global.keymap = {}
|
||||||
|
content = JSON.stringify(global.keymap)
|
||||||
}
|
}
|
||||||
if (global.keymap.toggleFinder == null) global.keymap.toggleFinder = 'ctrl+tab+shift'
|
jetpack.cwd(userDataPath).file(keymapFilename, { content })
|
||||||
var toggleFinderKey = global.keymap.toggleFinder
|
|
||||||
|
|
||||||
try {
|
|
||||||
globalShortcut.register(toggleFinderKey, function () {
|
|
||||||
emitToFinder('open-finder')
|
|
||||||
mainWindow.webContents.send('open-finder', {})
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err.name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ipc.on('hotkeyUpdated', function (event, newKeymap) {
|
|
||||||
console.log('got new keymap')
|
|
||||||
console.log(newKeymap)
|
|
||||||
globalShortcut.unregisterAll()
|
|
||||||
global.keymap = newKeymap
|
|
||||||
jetpack.cwd(userDataPath).file('keymap.json', {content: JSON.stringify(global.keymap)})
|
|
||||||
|
|
||||||
var toggleFinderKey = global.keymap.toggleFinder != null ? global.keymap.toggleFinder : 'ctrl+tab+shift'
|
|
||||||
try {
|
|
||||||
globalShortcut.register(toggleFinderKey, function () {
|
|
||||||
emitToFinder('open-finder')
|
|
||||||
mainWindow.webContents.send('open-finder', {})
|
|
||||||
})
|
|
||||||
mainWindow.webContents.send('APP_SETTING_DONE', {})
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
mainWindow.webContents.send('APP_SETTING_ERROR', {
|
|
||||||
message: 'Failed to apply hotkey: Invalid format'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function emitToFinder (type, data) {
|
function emitToFinder (type, data) {
|
||||||
var payload = {
|
var payload = {
|
||||||
type: type,
|
type: type,
|
||||||
@@ -58,3 +44,63 @@ function emitToFinder (type, data) {
|
|||||||
|
|
||||||
nodeIpc.server.broadcast('message', payload)
|
nodeIpc.server.broadcast('message', payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleFinder () {
|
||||||
|
emitToFinder('open-finder')
|
||||||
|
mainWindow.webContents.send('open-finder', {})
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleMain () {
|
||||||
|
if (mainWindow.isFocused()) {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
Menu.sendActionToFirstResponder('hide:')
|
||||||
|
} else {
|
||||||
|
mainWindow.minimize()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
mainWindow.show()
|
||||||
|
} else {
|
||||||
|
mainWindow.minimize()
|
||||||
|
mainWindow.restore()
|
||||||
|
}
|
||||||
|
mainWindow.webContents.send('list-focus')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init
|
||||||
|
global.keymap = Object.assign({}, defaultKeymap, getKeymap())
|
||||||
|
|
||||||
|
function registerKey (name, callback, broadcast) {
|
||||||
|
if (broadcast == null) broadcast = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
globalShortcut.register(global.keymap[name], callback)
|
||||||
|
if (broadcast) {
|
||||||
|
mainWindow.webContents.send('APP_SETTING_DONE', {})
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
if (broadcast) {
|
||||||
|
mainWindow.webContents.send('APP_SETTING_ERROR', {
|
||||||
|
message: 'Failed to apply hotkey: Invalid format'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerAllKeys (broadcast) {
|
||||||
|
if (broadcast == null) broadcast = true
|
||||||
|
registerKey('toggleFinder', toggleFinder, broadcast)
|
||||||
|
registerKey('toggleMain', toggleMain, broadcast)
|
||||||
|
}
|
||||||
|
|
||||||
|
registerAllKeys(false)
|
||||||
|
|
||||||
|
ipc.on('hotkeyUpdated', function (event, newKeymap) {
|
||||||
|
global.keymap = Object.assign({}, defaultKeymap, global.keymap, newKeymap)
|
||||||
|
saveKeymap()
|
||||||
|
globalShortcut.unregisterAll()
|
||||||
|
registerAllKeys()
|
||||||
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -295,6 +295,7 @@ app.on('ready', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
require('./hotkey')
|
require('./hotkey')
|
||||||
|
require('./config')
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports = app
|
module.exports = app
|
||||||
|
|||||||
Reference in New Issue
Block a user