1
0
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:
Rokt33r
2016-01-08 14:38:29 +09:00
parent ee280d5c7b
commit 09735b7f47
14 changed files with 461 additions and 194 deletions

View File

@@ -3,10 +3,35 @@ import ReactDOM from 'react-dom'
import modes from '../lib/modes'
import _ from 'lodash'
const remote = require('electron').remote
const electron = require('electron')
const remote = electron.remote
const ipc = electron.ipcRenderer
const ace = window.ace
function getConfig () {
return Object.assign({}, remote.getGlobal('config'))
}
let config = getConfig()
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) {
if (nextProps.readOnly !== this.props.readOnly) {
this.editor.setReadOnly(!!nextProps.readOnly)
@@ -14,6 +39,7 @@ export default class CodeEditor extends React.Component {
}
componentDidMount () {
let { article } = this.props
var el = ReactDOM.findDOMNode(this)
var editor = this.editor = ace.edit(el)
editor.$blockScrolling = Infinity
@@ -21,6 +47,7 @@ export default class CodeEditor extends React.Component {
editor.setTheme('ace/theme/xcode')
editor.moveCursorTo(0, 0)
editor.setReadOnly(!!this.props.readOnly)
editor.setFontSize(this.state.fontSize)
editor.commands.addCommand({
name: 'Emacs cursor up',
@@ -45,36 +72,62 @@ export default class CodeEditor extends React.Component {
})
var session = editor.getSession()
let mode = _.findWhere(modes, {name: this.props.mode})
let mode = _.findWhere(modes, {name: article.mode})
let syntaxMode = mode != null
? mode.mode
: 'text'
session.setMode('ace/mode/' + syntaxMode)
session.setUseSoftTabs(true)
session.setOption('useWorker', false)
session.setUseSoftTabs(this.state.indentType === 'space')
session.setTabSize(!isNaN(this.state.indentSize) ? parseInt(this.state.indentSize, 10) : 4)
session.setOption('useWorker', true)
session.setUseWrapMode(true)
session.setValue(this.props.code)
session.setValue(this.props.article.content)
session.on('change', e => {
if (this.props.onChange != null) {
var value = editor.getValue()
this.props.onChange(e, value)
session.on('change', this.changeHandler)
ipc.on('config-apply', this.configApplyHandler)
}
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)
})
}
componentDidUpdate (prevProps) {
var session = this.editor.getSession()
if (this.editor.getValue() !== this.props.code) {
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)
handleChange (e) {
if (this.props.onChange) {
var value = this.editor.getValue()
this.props.onChange(value)
}
}
@@ -96,14 +149,23 @@ export default class CodeEditor extends React.Component {
render () {
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 = {
code: PropTypes.string,
article: PropTypes.shape({
content: PropTypes.string,
mode: PropTypes.string,
key: PropTypes.string
}),
className: PropTypes.string,
onChange: PropTypes.func,
onBlur: PropTypes.func,

View File

@@ -2,11 +2,12 @@ import React, { PropTypes } from 'react'
import markdown from '../lib/markdown'
import ReactDOM from 'react-dom'
import sanitizeHtml from '@rokt33r/sanitize-html'
import hljs from 'highlight.js'
import _ from 'lodash'
const electron = require('electron')
const shell = electron.shell
const remote = electron.remote
const ipc = electron.ipcRenderer
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 {
constructor (props) {
super(props)
this.configApplyHandler = e => this.handleConfigApply(e)
this.state = {
fontSize: config['preview-font-size'],
fontFamily: config['preview-font-family']
}
}
componentDidMount () {
this.addListener()
// this.renderCode()
this.renderMath()
ipc.on('config-apply', this.configApplyHandler)
}
componentDidUpdate () {
this.addListener()
// this.renderCode()
this.renderMath()
}
componentWillUnmount () {
this.removeListener()
ipc.removeListener('config-apply', this.configApplyHandler)
}
componentWillUpdate () {
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 () {
let inline = ReactDOM.findDOMNode(this).querySelectorAll('span.math')
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 () {
let isEmpty = this.props.content.trim().length === 0
let content = isEmpty
@@ -174,6 +185,7 @@ export default class MarkdownPreview extends React.Component {
onMouseMove={e => this.handleMouseMove(e)}
onMouseUp={e => this.handleMouseUp(e)}
dangerouslySetInnerHTML={{__html: ' ' + content}}
style={{fontSize: this.state.fontSize, fontFamily: this.state.fontFamily}}
/>
)
}

View File

@@ -24,7 +24,7 @@ export default class FinderDetail extends React.Component {
<div className='content'>
{activeArticle.mode === 'markdown'
? <MarkdownPreview content={activeArticle.content}/>
: <CodeEditor readOnly mode={activeArticle.mode} code={activeArticle.content}/>
: <CodeEditor readOnly article={activeArticle}/>
}
</div>
</div>

View File

@@ -105,7 +105,7 @@ export function init () {
folders: [defaultFolder],
version: '0.4'
}
jetpack.write(getLocalPath(), data)
queueSave()
}
}
@@ -114,18 +114,44 @@ export function getData () {
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) {
if (!_.isArray(articles)) throw new Error('Articles must be an array')
let data = getData()
data.articles = articles
jetpack.write(getLocalPath(), data)
queueSave()
}
export function setFolders (folders) {
if (!_.isArray(folders)) throw new Error('Folders must be an array')
let data = getData()
data.folders = folders
jetpack.write(getLocalPath(), data)
queueSave()
}
function isFinderCalled () {

View File

@@ -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 () {
this.setState({
cursorPosition: null,
@@ -77,13 +85,19 @@ export default class ArticleEditor extends React.Component {
}
handleBlurCodeEditor () {
if (this.props.mode === 'markdown') {
let { article } = this.props
if (article.mode === 'markdown') {
this.switchPreviewMode()
}
}
handleCodeEditorChange (value) {
this.props.onChange(value)
}
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) {
return (
<div className='ArticleEditor'>
@@ -92,7 +106,7 @@ export default class ArticleEditor extends React.Component {
onMouseUp={e => this.handlePreviewMouseUp(e)}
onMouseDown={e => this.handlePreviewMouseDown(e)}
onMouseMove={e => this.handlePreviewMouseMove(e)}
content={this.props.content}
content={article.content}
/>
<div className='ArticleDetail-panel-content-tooltip'>Click to Edit</div>
</div>
@@ -104,11 +118,10 @@ export default class ArticleEditor extends React.Component {
<CodeEditor
ref='editor'
onBlur={e => this.handleBlurCodeEditor(e)}
onChange={this.props.onChange}
mode={this.props.mode}
code={this.props.content}
onChange={value => this.handleCodeEditorChange(value)}
article={article}
/>
{this.props.mode === 'markdown'
{article.mode === 'markdown'
? (
<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 = {
article: PropTypes.shape({
content: PropTypes.string,
mode: PropTypes.string,
key: PropTypes.string,
mode: PropTypes.string
}),
onChange: PropTypes.func
}

View File

@@ -18,9 +18,9 @@ import DeleteArticleModal from '../../modal/DeleteArticleModal'
import ArticleEditor from './ArticleEditor'
const electron = require('electron')
const ipc = electron.ipcRenderer
const remote = electron.remote
const { Menu, MenuItem } = remote
let count = 0
// const remote = electron.remote
// const { Menu, MenuItem } = remote
// const othersMenu = new Menu()
// othersMenu.append(new MenuItem({
// label: 'Delete Post',
@@ -152,18 +152,6 @@ export default class ArticleDetail extends React.Component {
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) {
if (this.props.activeArticle == null || prevProps.activeArticle == null || this.props.activeArticle.key !== prevProps.activeArticle.key) {
this.refs.editor.resetCursorPosition()
@@ -175,20 +163,6 @@ export default class ArticleDetail extends React.Component {
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 () {
return (
<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) {
this.deleteHandler()
}
handleFolderKeyChange (e) {
let article = this.state.article
article.FolderKey = e.target.value
let { dispatch, activeArticle, status, folders } = this.props
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) {
let { article } = this.state
article.title = e.target.value
this.setState({
article
}, () => this.cacheArticle())
let { dispatch, activeArticle } = this.props
let article = Object.assign({}, activeArticle, {
title: e.target.value,
updatedAt: new Date()
})
dispatch(updateArticle(article))
}
handleTagsChange (newTag, tags) {
let article = this.state.article
article.tags = tags
let { dispatch, activeArticle } = this.props
let article = Object.assign({}, activeArticle, {
tags: tags,
updatedAt: new Date()
})
this.setState({
article
}, () => this.cacheArticle())
dispatch(updateArticle(article))
}
handleModeChange (value) {
let { article } = this.state
article.mode = value
this.setState({
article
}, () => 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
let { dispatch, activeArticle } = this.props
let article = Object.assign({}, activeArticle, {
mode: value,
updatedAt: new Date()
})
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) {
if (e.keyCode === 9 && !e.shiftKey) {
e.preventDefault()
@@ -312,7 +275,7 @@ export default class ArticleDetail extends React.Component {
<div className='ArticleDetail-info-row'>
<select
className='ArticleDetail-info-folder'
value={this.state.article.FolderKey}
value={activeArticle.FolderKey}
onChange={e => this.handleFolderKeyChange(e)}
>
{folderOptions}
@@ -321,7 +284,7 @@ export default class ArticleDetail extends React.Component {
children={
isUnsaved
? <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}
<div>
<TagSelect
tags={this.state.article.tags}
tags={activeArticle.tags}
onChange={(tags, tag) => this.handleTagsChange(tags, tag)}
suggestTags={tags}
/>
@@ -367,7 +330,7 @@ export default class ArticleDetail extends React.Component {
onKeyDown={e => this.handleTitleKeyDown(e)}
placeholder='(Untitled)'
ref='title'
value={this.state.article.title}
value={activeArticle.title}
onChange={e => this.handleTitleChange(e)}
/>
</div>
@@ -375,16 +338,15 @@ export default class ArticleDetail extends React.Component {
ref='mode'
onChange={e => this.handleModeChange(e)}
onKeyDown={e => this.handleModeSelectKeyDown(e)}
value={this.state.article.mode}
value={activeArticle.mode}
className='ArticleDetail-panel-header-mode'
/>
</div>
{status.isTutorialOpen ? modeSelectTutorialElement : null}
<ArticleEditor
ref='editor'
content={this.state.article.content}
mode={this.state.article.mode}
onChange={(e, content) => this.handleContentChange(e, content)}
article={activeArticle}
onChange={content => this.handleContentChange(content)}
/>
</div>
</div>

View File

@@ -130,7 +130,7 @@ export default class ArticleList extends React.Component {
let modifiedArticle = _.findWhere(modified, {key: article.key})
let originalArticle = article
if (modifiedArticle) {
article = Object.assign({}, article, modifiedArticle)
article = Object.assign({}, article)
}
let tagElements = Array.isArray(article.tags) && article.tags.length > 0
? article.tags.slice().map(tag => {

View File

@@ -33,7 +33,7 @@ export default class CreateNewFolder extends React.Component {
name,
color
}
console.log(input)
try {
store.dispatch(createFolder(input))
} catch (e) {

View File

@@ -6,10 +6,13 @@ const electron = require('electron')
const ipc = electron.ipcRenderer
const remote = electron.remote
const OSX = global.process.platform === 'darwin'
export default class AppSettingTab extends React.Component {
constructor (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
this.state = {
@@ -18,10 +21,10 @@ export default class AppSettingTab extends React.Component {
alert: null
},
userAlert: null,
keymap: {
toggleFinder: keymap.toggleFinder
},
keymapAlert: null
keymap: keymap,
keymapAlert: null,
config: config,
configAlert: null
}
}
@@ -48,21 +51,33 @@ export default class AppSettingTab extends React.Component {
}
submitHotKey () {
ipc.send('hotkeyUpdated', {
toggleFinder: this.state.keymap.toggleFinder
})
ipc.send('hotkeyUpdated', this.state.keymap)
}
submitConfig () {
ipc.send('configUpdated', this.state.config)
}
handleSaveButtonClick (e) {
this.submitHotKey()
}
handleConfigSaveButtonClick (e) {
this.submitConfig()
}
handleKeyDown (e) {
if (e.keyCode === 13) {
this.submitHotKey()
}
}
handleConfigKeyDown (e) {
if (e.keyCode === 13) {
this.submitConfig()
}
}
handleNameSaveButtonClick (e) {
let { dispatch } = this.props
@@ -104,8 +119,61 @@ export default class AppSettingTab extends React.Component {
{userAlertElement}
</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='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'>
<label>Toggle Finder(popup)</label>
<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.propTypes = {
user: PropTypes.shape({
name: PropTypes.string
}),
dispatch: PropTypes.func
}

View File

@@ -89,7 +89,7 @@ export default class Tutorial extends React.Component {
<div className='title'>Easy to access with Finder</div>
<div className='content'>
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/>
by selecting your file with pressing Enter key,<br/>
and to paste the contents of the Clipboard with [{process.platform === 'darwin' ? 'Command' : 'Control'}-V]

View File

@@ -89,6 +89,31 @@ iptFocusBorderColor = #369DCD
outline none
&:focus
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
clearfix()
padding 5px 15px
@@ -624,10 +649,3 @@ iptFocusBorderColor = #369DCD
color brandColor
&:hover
color lighten(brandColor, 10%)

53
lib/config.js Normal file
View 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()
})

View File

@@ -1,55 +1,41 @@
const electron = require('electron')
const app = electron.app
const ipc = electron.ipcMain
const Menu = electron.Menu
const globalShortcut = electron.globalShortcut
const jetpack = require('fs-jetpack')
const mainWindow = require('./main-window')
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')
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 {
global.keymap = JSON.parse(jetpack.cwd(userDataPath).read('keymap.json', 'utf-8'))
} catch (err) {
jetpack.cwd(userDataPath).file('keymap.json', {content: '{}'})
return JSON.parse(jetpack.cwd(userDataPath).read(keymapFilename, 'utf-8'))
} catch (err) {}
}
return {}
}
function saveKeymap () {
var content
try {
content = JSON.stringify(global.keymap)
} catch (e) {
global.keymap = {}
content = JSON.stringify(global.keymap)
}
if (global.keymap.toggleFinder == null) global.keymap.toggleFinder = 'ctrl+tab+shift'
var toggleFinderKey = global.keymap.toggleFinder
try {
globalShortcut.register(toggleFinderKey, function () {
emitToFinder('open-finder')
mainWindow.webContents.send('open-finder', {})
})
} catch (err) {
console.log(err.name)
jetpack.cwd(userDataPath).file(keymapFilename, { content })
}
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) {
var payload = {
type: type,
@@ -58,3 +44,63 @@ function emitToFinder (type, data) {
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()
})

View File

@@ -295,6 +295,7 @@ app.on('ready', function () {
})
require('./hotkey')
require('./config')
})
module.exports = app