diff --git a/browser/finder/FinderDetail.js b/browser/finder/FinderDetail.js
deleted file mode 100644
index 5e4cfc3f..00000000
--- a/browser/finder/FinderDetail.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import React, { PropTypes } from 'react'
-import CodeEditor from 'browser/components/CodeEditor'
-import MarkdownPreview from 'browser/components/MarkdownPreview'
-import ModeIcon from 'browser/components/ModeIcon'
-
-export default class FinderDetail extends React.Component {
- render () {
- let { activeArticle } = this.props
-
- if (activeArticle != null) {
- return (
-
-
-
- {activeArticle.title}
-
-
-
-
-
-
- {activeArticle.mode === 'markdown'
- ?
- :
- }
-
-
- )
- }
- return (
-
- )
- }
-}
-
-FinderDetail.propTypes = {
- activeArticle: PropTypes.shape(),
- saveToClipboard: PropTypes.func
-}
diff --git a/browser/finder/FinderInput.js b/browser/finder/FinderInput.js
deleted file mode 100644
index d23fa98d..00000000
--- a/browser/finder/FinderInput.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import React, { PropTypes } from 'react'
-
-export default class FinderInput extends React.Component {
- render () {
- return (
-
-
-
- )
- }
-}
-
-FinderInput.propTypes = {
- handleSearchChange: PropTypes.func,
- value: PropTypes.string
-}
diff --git a/browser/finder/FinderList.js b/browser/finder/FinderList.js
deleted file mode 100644
index d9f48827..00000000
--- a/browser/finder/FinderList.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import React, { PropTypes } from 'react'
-import ReactDOM from 'react-dom'
-import ModeIcon from 'browser/components/ModeIcon'
-import { selectArticle } from './actions'
-
-export default class FinderList extends React.Component {
- componentDidUpdate () {
- var index = this.props.articles.indexOf(this.props.activeArticle)
- var el = ReactDOM.findDOMNode(this)
- var li = el.querySelectorAll('li')[index]
-
- if (li == null) {
- return
- }
-
- var overflowBelow = el.clientHeight + el.scrollTop < li.offsetTop + li.clientHeight
- if (overflowBelow) {
- el.scrollTop = li.offsetTop + li.clientHeight - el.clientHeight
- }
- var overflowAbove = el.scrollTop > li.offsetTop
- if (overflowAbove) {
- el.scrollTop = li.offsetTop
- }
- }
-
- handleArticleClick (article) {
- return (e) => {
- let { dispatch } = this.props
- dispatch(selectArticle(article.key))
- }
- }
-
- render () {
- let articleElements = this.props.articles.map(function (article) {
- if (article == null) {
- return (
-
- Undefined
-
-
- )
- }
-
- var isActive = this.props.activeArticle != null && (article.key === this.props.activeArticle.key)
- return (
-
-
- {article.title}
-
-
- )
- }.bind(this))
-
- return (
-
- )
- }
-}
-
-FinderList.propTypes = {
- articles: PropTypes.array,
- activeArticle: PropTypes.shape({
- type: PropTypes.string,
- key: PropTypes.string
- }),
- dispatch: PropTypes.func
-}
diff --git a/browser/finder/FinderMain.styl b/browser/finder/FinderMain.styl
new file mode 100644
index 00000000..44d8ef3a
--- /dev/null
+++ b/browser/finder/FinderMain.styl
@@ -0,0 +1,78 @@
+$search-height = 50px
+$nav-width = 175px
+$list-width = 250px
+
+.root
+ absolute top left right bottom
+
+.search
+ height $search-height
+ padding 10px
+ box-sizing border-box
+ border-bottom $ui-border
+ text-align center
+
+.search-input
+ height 30px
+ width 100%
+ margin 0 auto
+ font-size 18px
+ border none
+ outline none
+ text-align center
+.result
+ absolute left right bottom
+ top $search-height
+
+.result-nav
+ user-select none
+ absolute left top bottom
+ width $nav-width
+ background-color $ui-backgroundColor
+.result-nav-filter
+ margin-bottom 5px
+.result-nav-filter-option
+ height 25px
+ line-height 25px
+ padding 0 10px
+ label
+ cursor pointer
+
+.result-nav-menu
+ navButtonColor()
+ height 40px
+ padding 0 10px
+ font-size 14px
+ width 100%
+ outline none
+ text-align left
+ line-height 40px
+ box-sizing border-box
+ cursor pointer
+
+.result-nav-menu--active
+ @extend .result-nav-menu
+ background-color $ui-button--active-backgroundColor
+ color $ui-button--active-color
+ &:hover
+ background-color $ui-button--active-backgroundColor
+
+.result-nav-storageList
+ absolute bottom left right
+ top 80px + 50px + 10px
+ overflow-y auto
+
+.result-list
+ user-select none
+ absolute top bottom
+ left $nav-width
+ width $list-width
+ border-width 0 1px
+ border-style solid
+ border-color $ui-borderColor
+ box-sizing border-box
+ overflow-y auto
+
+.result-detail
+ absolute top bottom right
+ left $nav-width + $list-width
diff --git a/browser/finder/NoteDetail.js b/browser/finder/NoteDetail.js
new file mode 100644
index 00000000..d339265b
--- /dev/null
+++ b/browser/finder/NoteDetail.js
@@ -0,0 +1,198 @@
+import React, { PropTypes } from 'react'
+import CSSModules from 'browser/lib/CSSModules'
+import styles from './NoteDetail.styl'
+import MarkdownPreview from 'browser/components/MarkdownPreview'
+import MarkdownEditor from 'browser/components/MarkdownEditor'
+import CodeEditor from 'browser/components/CodeEditor'
+import modes from 'browser/lib/modes'
+
+const electron = require('electron')
+const { clipboard } = electron
+const path = require('path')
+
+function notify (title, options) {
+ if (process.platform === 'win32') {
+ options.icon = path.join('file://', global.__dirname, '../../resources/app.png')
+ }
+ return new window.Notification(title, options)
+}
+
+class NoteDetail extends React.Component {
+ constructor (props) {
+ super(props)
+
+ this.state = {
+ snippetIndex: 0
+ }
+ }
+
+ componentWillReceiveProps (nextProps) {
+ if (nextProps.note !== this.props.note) {
+ this.setState({
+ snippetIndex: 0
+ }, () => {
+ if (nextProps.note.type === 'SNIPPET_NOTE') {
+ nextProps.note.snippets.forEach((snippet, index) => {
+ this.refs['code-' + index].reload()
+ })
+ }
+ })
+ }
+ }
+
+ selectPriorSnippet () {
+ let { note } = this.props
+ if (note.type === 'SNIPPET_NOTE' && note.snippets.length > 1) {
+ this.setState({
+ snippetIndex: (this.state.snippetIndex + note.snippets.length - 1) % note.snippets.length
+ })
+ }
+ }
+
+ selectNextSnippet () {
+ let { note } = this.props
+ if (note.type === 'SNIPPET_NOTE' && note.snippets.length > 1) {
+ this.setState({
+ snippetIndex: (this.state.snippetIndex + 1) % note.snippets.length
+ })
+ }
+ }
+
+ saveToClipboard () {
+ let { note } = this.props
+
+ if (note.type === 'MARKDOWN_NOTE') {
+ clipboard.writeText(note.content)
+ } else {
+ clipboard.writeText(note.snippets[this.state.snippetIndex].content)
+ }
+
+ notify('Saved to Clipboard!', {
+ body: 'Paste it wherever you want!',
+ silent: true
+ })
+ }
+
+ handleTabButtonClick (e, index) {
+ this.setState({
+ snippetIndex: index
+ })
+ }
+
+ render () {
+ let { note, config } = this.props
+ if (note == null) {
+ return (
+
+
+
+ )
+ }
+
+ let editorFontSize = parseInt(config.editor.fontSize, 10)
+ if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
+ let editorIndentSize = parseInt(config.editor.indentSize, 10)
+ if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4
+
+ if (note.type === 'SNIPPET_NOTE') {
+ let tabList = note.snippets.map((snippet, index) => {
+ let isActive = this.state.snippetIndex === index
+ return
+
+
+ })
+ let viewList = note.snippets.map((snippet, index) => {
+ let isActive = this.state.snippetIndex === index
+ let mode = snippet.mode === 'text'
+ ? null
+ : modes.filter((mode) => mode.name === snippet.mode)[0]
+
+ return
+
+
+
+
+ {snippet.mode === 'markdown'
+ ?
+ :
+ }
+
+ })
+
+ return (
+
+
+
+
+
+ {tabList}
+
+ {viewList}
+
+ )
+ }
+
+ return (
+
+ )
+ }
+}
+
+NoteDetail.propTypes = {
+}
+
+export default CSSModules(NoteDetail, styles)
diff --git a/browser/finder/NoteDetail.styl b/browser/finder/NoteDetail.styl
new file mode 100644
index 00000000..947ea324
--- /dev/null
+++ b/browser/finder/NoteDetail.styl
@@ -0,0 +1,93 @@
+.root
+ absolute top bottom left right
+ width 100%
+ height 100%
+
+.description
+ absolute top left right
+ height 80px
+ border-bottom $ui-border
+ box-sizing border-box
+
+.description-textarea
+ display block
+ height 100%
+ width 100%
+ resize none
+ border none
+ padding 10px
+ line-height 1.6
+ box-sizing border-box
+
+.tabList
+ absolute left right
+ top 80px
+ box-sizing border-box
+ height 30px
+ border-bottom $ui-border
+ display flex
+ background-color $ui-backgroundColor
+.tabList-item
+ position relative
+ flex 1
+ border-right $ui-border
+ &:hover
+ .tabList-item-deleteButton
+ color $ui-inactive-text-color
+ &:hover
+ background-color darken($ui-backgroundColor, 15%)
+ &:active
+ color white
+ background-color $ui-active-color
+.tabList-item--active
+ @extend .tabList-item
+ .tabList-item-button
+ border-color $brand-color
+.tabList-item-button
+ width 100%
+ height 29px
+ navButtonColor()
+ border-left 4px solid transparent
+.tabList-item-deleteButton
+ position absolute
+ top 5px
+ height 20px
+ right 5px
+ width 20px
+ text-align center
+ border none
+ padding 0
+ color transparent
+ background-color transparent
+ border-radius 2px
+.tabList-plusButton
+ navButtonColor()
+ width 30px
+.tabView
+ absolute left right bottom
+ top 110px
+.tabView-top
+ absolute top left right
+ height 30px
+ border-bottom $ui-border
+ display flex
+ box-sizing border-box
+.tabView-top-name
+ flex 1
+ border none
+ padding 0 10px
+ font-size 14px
+.tabView-top-mode
+ width 110px
+ padding 0
+ border none
+ border-left $ui-border
+ colorDefaultButton()
+ color $ui-inactive-text-color
+ pointer-events none
+.tabView-content
+ absolute left right bottom
+ top 30px
+ box-sizing border-box
+ height 100%
+ width 100%
diff --git a/browser/finder/NoteItem.js b/browser/finder/NoteItem.js
new file mode 100644
index 00000000..9dd183ee
--- /dev/null
+++ b/browser/finder/NoteItem.js
@@ -0,0 +1,86 @@
+import React, { PropTypes } from 'react'
+import CSSModules from 'browser/lib/CSSModules'
+import styles from './NoteItem.styl'
+import moment from 'moment'
+import _ from 'lodash'
+
+class NoteItem extends React.Component {
+ constructor (props) {
+ super(props)
+
+ this.state = {
+ }
+ }
+
+ handleClick (e) {
+ this.props.onClick(e)
+ }
+
+ render () {
+ let { note, folder, storage, isActive } = this.props
+
+ let tagList = _.isArray(note.tags)
+ ? note.tags.map((tag) => {
+ return (
+
+ {tag}
+
+ )
+ })
+ : []
+ return (
+ this.handleClick(e)}
+ >
+
+
+
+
+
+ {folder.name}
+ in {storage.name}
+
+
+
+
+ {moment(note.updatedAt).fromNow()}
+
+
+
+
+
+ {note.type === 'SNIPPET_NOTE'
+ ?
+ :
+ }
+ {note.title.trim().length > 0
+ ? note.title
+ : Empty
+ }
+
+
+
+
+ {tagList.length > 0
+ ? tagList
+ : Not tagged yet
+ }
+
+
+ )
+ }
+}
+
+NoteItem.propTypes = {
+}
+
+export default CSSModules(NoteItem, styles)
diff --git a/browser/finder/NoteItem.styl b/browser/finder/NoteItem.styl
new file mode 100644
index 00000000..924fb452
--- /dev/null
+++ b/browser/finder/NoteItem.styl
@@ -0,0 +1,86 @@
+.root
+ position relative
+ height 80px
+ border-bottom $ui-border
+ padding 0 5px
+ user-select none
+ cursor pointer
+ transition background-color 0.15s
+ &:hover
+ background-color alpha($ui-active-color, 10%)
+
+.root--active
+ @extend .root
+ .border
+ border-color $ui-active-color
+
+.border
+ absolute top bottom left right
+ border-style solid
+ border-width 2px
+ border-color transparent
+ transition 0.15s
+
+.info
+ height 30px
+ clearfix()
+ font-size 12px
+ color $ui-inactive-text-color
+ line-height 30px
+ overflow-y hidden
+
+.info-left
+ float left
+ overflow ellipsis
+
+.info-left-folder
+ border-left 4px solid transparent
+ padding 2px 5px
+ color $ui-text-color
+.info-left-folder-surfix
+ font-size 10px
+ margin-left 5px
+ color $ui-inactive-text-color
+.info-right
+ float right
+
+.title
+ height 20px
+ line-height 20px
+ padding 0 5px 0 0
+ font-weight bold
+ overflow ellipsis
+ color $ui-text-color
+.title-icon
+ font-size 12px
+ color $ui-inactive-text-color
+ padding-right 3px
+.title-empty
+ font-weight normal
+ color $ui-inactive-text-color
+
+.tagList
+ height 30px
+ font-size 12px
+ line-height 30px
+ overflow ellipsis
+
+.tagList-icon
+ vertical-align middle
+ color $ui-button-color
+
+.tagList-item
+ margin 0 4px
+ padding 0 4px
+ height 20px
+ border-radius 3px
+ vertical-align middle
+ border-style solid
+ border-color $ui-button--focus-borderColor
+ border-width 0 0 0 3px
+ background-color $ui-backgroundColor
+
+.tagList-empty
+ color $ui-inactive-text-color
+ vertical-align middle
+
diff --git a/browser/finder/NoteList.js b/browser/finder/NoteList.js
new file mode 100644
index 00000000..986efcf7
--- /dev/null
+++ b/browser/finder/NoteList.js
@@ -0,0 +1,89 @@
+import React, { PropTypes } from 'react'
+import NoteItem from './NoteItem'
+import _ from 'lodash'
+
+class NoteList extends React.Component {
+ constructor (props) {
+ super(props)
+
+ this.state = {
+ range: 0
+ }
+ }
+
+ componentWillReceiveProps (nextProps) {
+ if (this.props.search !== nextProps.search) {
+ this.resetScroll()
+ }
+ }
+
+ componentDidUpdate () {
+ let { index } = this.props
+
+ if (index > -1) {
+ let list = this.refs.root
+ let item = list.childNodes[index]
+ if (item == null) return null
+
+ let overflowBelow = item.offsetTop + item.clientHeight - list.clientHeight - list.scrollTop > 0
+ if (overflowBelow) {
+ list.scrollTop = item.offsetTop + item.clientHeight - list.clientHeight
+ }
+ let overflowAbove = list.scrollTop > item.offsetTop
+ if (overflowAbove) {
+ list.scrollTop = item.offsetTop
+ }
+ }
+ }
+
+ resetScroll () {
+ this.refs.root.scrollTop = 0
+ this.setState({
+ range: 0
+ })
+ }
+
+ handleScroll (e) {
+ let { notes } = this.props
+
+ if (e.target.offsetHeight + e.target.scrollTop > e.target.scrollHeight - 100 && notes.length > this.state.range * 10 + 10) {
+ this.setState({
+ range: this.state.range + 1
+ })
+ }
+ }
+
+ render () {
+ let { storages, notes, index } = this.props
+
+ let notesList = notes
+ .slice(0, 10 + 10 * this.state.range)
+ .map((note, _index) => {
+ let storage = _.find(storages, {key: note.storage})
+ let folder = _.find(storage.folders, {key: note.folder})
+ return (
+ this.props.handleNoteClick(e, _index)}
+ />
+ )
+ })
+ return (
+ this.handleScroll(e)}
+ ref='root'
+ >
+ {notesList}
+
+ )
+ }
+}
+
+NoteList.propTypes = {
+}
+
+export default NoteList
diff --git a/browser/finder/StorageSection.js b/browser/finder/StorageSection.js
new file mode 100644
index 00000000..fa34b9db
--- /dev/null
+++ b/browser/finder/StorageSection.js
@@ -0,0 +1,72 @@
+import React, { PropTypes } from 'react'
+import CSSModules from 'browser/lib/CSSModules'
+import styles from './StorageSection.styl'
+
+class StorageSection extends React.Component {
+ constructor (props) {
+ super(props)
+
+ this.state = {
+ isOpen: true
+ }
+ }
+
+ handleToggleButtonClick (e) {
+ this.setState({
+ isOpen: !this.state.isOpen
+ })
+ }
+ handleHeaderClick (e) {
+ let { storage } = this.props
+ this.props.handleStorageButtonClick(e, storage.key)
+ }
+
+ handleFolderClick (e, folder) {
+ let { storage } = this.props
+ this.props.handleFolderButtonClick(e, storage.key, folder.key)
+ }
+
+ render () {
+ let { storage, filter } = this.props
+ let folderList = storage.folders
+ .map((folder) => {
+ return (
+
+ )
+ })
+ return (
+
+
+
+
+
+ {this.state.isOpen &&
+
+ {folderList}
+
+ }
+
+ )
+ }
+}
+
+StorageSection.propTypes = {
+}
+
+export default CSSModules(StorageSection, styles)
diff --git a/browser/finder/StorageSection.styl b/browser/finder/StorageSection.styl
new file mode 100644
index 00000000..ce49af2d
--- /dev/null
+++ b/browser/finder/StorageSection.styl
@@ -0,0 +1,59 @@
+.root
+ position relative
+
+.header
+ height 30px
+.header-toggleButton
+ absolute top left
+ width 25px
+ height 30px
+ navButtonColor()
+ border none
+ outline none
+.header-name
+ display block
+ height 30px
+ navButtonColor()
+ padding 0 10px 0 25px
+ font-size 14px
+ width 100%
+ text-align left
+ line-height 30px
+ box-sizing border-box
+ cursor pointer
+ outline none
+
+.header-name--active
+ @extend .header-name
+ background-color $ui-button--active-backgroundColor
+ color $ui-button--active-color
+ &:hover
+ background-color $ui-button--active-backgroundColor
+
+.folderList-item
+ display block
+ width 100%
+ height 30px
+ navButtonColor()
+ padding 0 10px 0 25px
+ font-size 14px
+ width 100%
+ text-align left
+ line-height 30px
+ box-sizing border-box
+ cursor pointer
+ outline none
+ padding 0 10px
+ margin 2px 0
+ height 30px
+ line-height 30px
+ border-width 0 0 0 6px
+ border-style solid
+ border-color transparent
+
+.folderList-item--active
+ @extend .folderList-item
+ background-color $ui-button--active-backgroundColor
+ color $ui-button--active-color
+ &:hover
+ background-color $ui-button--active-backgroundColor
diff --git a/browser/finder/actions.js b/browser/finder/actions.js
deleted file mode 100644
index 111a10c0..00000000
--- a/browser/finder/actions.js
+++ /dev/null
@@ -1,39 +0,0 @@
-export const SELECT_ARTICLE = 'SELECT_ARTICLE'
-export const SEARCH_ARTICLE = 'SEARCH_ARTICLE'
-export const REFRESH_DATA = 'REFRESH_DATA'
-
-export function selectArticle (key) {
- return {
- type: SELECT_ARTICLE,
- data: { key }
- }
-}
-
-export function searchArticle (input) {
- return {
- type: SEARCH_ARTICLE,
- data: { input }
- }
-}
-
-export function refreshData (data) {
- console.log('refreshing data')
- let { folders, articles } = data
-
- return {
- type: REFRESH_DATA,
- data: {
- articles,
- folders
- }
- }
-}
-
-export default {
- SELECT_ARTICLE,
- SEARCH_ARTICLE,
- REFRESH_DATA,
- selectArticle,
- searchArticle,
- refreshData
-}
diff --git a/browser/finder/index.js b/browser/finder/index.js
index 9ecb2753..99d7ddb2 100644
--- a/browser/finder/index.js
+++ b/browser/finder/index.js
@@ -1,85 +1,86 @@
import React, { PropTypes } from 'react'
import ReactDOM from 'react-dom'
import { connect, Provider } from 'react-redux'
-import reducer from './reducer'
-import { createStore } from 'redux'
-import FinderInput from './FinderInput'
-import FinderList from './FinderList'
-import FinderDetail from './FinderDetail'
-import actions, { selectArticle, searchArticle } from './actions'
import _ from 'lodash'
-import dataStore from 'browser/lib/dataStore'
-import fetchConfig from '../lib/fetchConfig'
+import ipc from './ipcClient'
+import store from './store'
+import CSSModules from 'browser/lib/CSSModules'
+import styles from './FinderMain.styl'
+import StorageSection from './StorageSection'
+import NoteList from './NoteList'
+import NoteDetail from './NoteDetail'
+
const electron = require('electron')
-const { clipboard, ipcRenderer, remote } = electron
-const path = require('path')
-
-let config = fetchConfig()
-applyConfig(config)
-
-ipcRenderer.on('config-apply', function (e, newConfig) {
- config = newConfig
- applyConfig(config)
-})
-
-function applyConfig () {
- let body = document.body
- body.setAttribute('data-theme', config['theme-ui'])
-
- let hljsCss = document.getElementById('hljs-css')
- hljsCss.setAttribute('href', '../node_modules/highlight.js/styles/' + config['theme-code'] + '.css')
-}
-
-if (process.env.NODE_ENV !== 'production') {
- window.addEventListener('keydown', function (e) {
- if (e.keyCode === 73 && e.metaKey && e.altKey) {
- remote.getCurrentWindow().toggleDevTools()
- }
- })
-}
+const { remote } = electron
+const { Menu } = remote
function hideFinder () {
- ipcRenderer.send('hide-finder')
-}
-
-function notify (title, options) {
if (process.platform === 'win32') {
- options.icon = path.join('file://', global.__dirname, '../../resources/app.png')
+ remote.getCurrentWindow().minimize()
+ return
}
- return new window.Notification(title, options)
+ if (process.platform === 'darwin') {
+ Menu.sendActionToFirstResponder('hide:')
+ }
+ remote.getCurrentWindow().hide()
}
require('!!style!css!stylus?sourceMap!../styles/finder/index.styl')
-const FOLDER_FILTER = 'FOLDER_FILTER'
-const FOLDER_EXACT_FILTER = 'FOLDER_EXACT_FILTER'
-const TEXT_FILTER = 'TEXT_FILTER'
-const TAG_FILTER = 'TAG_FILTER'
-
class FinderMain extends React.Component {
constructor (props) {
super(props)
+
+ this.state = {
+ search: '',
+ index: 0,
+ filter: {
+ includeSnippet: true,
+ includeMarkdown: false,
+ type: 'ALL',
+ storage: null,
+ folder: null
+ }
+ }
+
+ this.focusHandler = (e) => this.handleWindowFocus(e)
+ this.blurHandler = (e) => this.handleWindowBlur(e)
}
componentDidMount () {
- this.keyDownHandler = e => this.handleKeyDown(e)
- document.addEventListener('keydown', this.keyDownHandler)
- ReactDOM.findDOMNode(this.refs.finderInput.refs.input).focus()
- this.focusHandler = e => {
- let { dispatch } = this.props
-
- dispatch(searchArticle(''))
- dispatch(selectArticle(null))
- }
window.addEventListener('focus', this.focusHandler)
+ window.addEventListener('blur', this.blurHandler)
}
componentWillUnmount () {
- document.removeEventListener('keydown', this.keyDownHandler)
window.removeEventListener('focus', this.focusHandler)
+ window.removeEventListener('blur', this.blurHandler)
+ }
+
+ handleWindowFocus (e) {
+ this.refs.search.focus()
+ }
+
+ handleWindowBlur (e) {
+ let { filter } = this.state
+ filter.type = 'ALL'
+ this.setState({
+ search: '',
+ filter,
+ index: 0
+ })
}
handleKeyDown (e) {
+ this.refs.search.focus()
+ if (e.keyCode === 9) {
+ if (e.shiftKey) {
+ this.refs.detail.selectPriorSnippet()
+ } else {
+ this.refs.detail.selectNextSnippet()
+ }
+ e.preventDefault()
+ }
if (e.keyCode === 38) {
this.selectPrevious()
e.preventDefault()
@@ -91,7 +92,8 @@ class FinderMain extends React.Component {
}
if (e.keyCode === 13) {
- this.saveToClipboard()
+ this.refs.detail.saveToClipboard()
+ hideFinder()
e.preventDefault()
}
if (e.keyCode === 27) {
@@ -101,26 +103,13 @@ class FinderMain extends React.Component {
if (e.keyCode === 91 || e.metaKey) {
return
}
-
- ReactDOM.findDOMNode(this.refs.finderInput.refs.input).focus()
- }
-
- saveToClipboard () {
- let { activeArticle } = this.props
- clipboard.writeText(activeArticle.content)
-
- ipcRenderer.send('copy-finder')
- notify('Saved to Clipboard!', {
- body: 'Paste it wherever you want!',
- silent: true
- })
- hideFinder()
}
handleSearchChange (e) {
- let { dispatch } = this.props
-
- dispatch(searchArticle(e.target.value))
+ this.setState({
+ search: e.target.value,
+ index: 0
+ })
}
selectArticle (article) {
@@ -128,41 +117,204 @@ class FinderMain extends React.Component {
}
selectPrevious () {
- let { activeArticle, dispatch } = this.props
- let index = this.refs.finderList.props.articles.indexOf(activeArticle)
- let previousArticle = this.refs.finderList.props.articles[index - 1]
- if (previousArticle != null) dispatch(selectArticle(previousArticle.key))
+ if (this.state.index > 0) {
+ this.setState({
+ index: this.state.index - 1
+ })
+ }
}
selectNext () {
- let { activeArticle, dispatch } = this.props
- let index = this.refs.finderList.props.articles.indexOf(activeArticle)
- let previousArticle = this.refs.finderList.props.articles[index + 1]
- if (previousArticle != null) dispatch(selectArticle(previousArticle.key))
+ if (this.state.index < this.noteCount - 1) {
+ this.setState({
+ index: this.state.index + 1
+ })
+ }
+ }
+
+ handleOnlySnippetCheckboxChange (e) {
+ let { filter } = this.state
+ filter.includeSnippet = e.target.checked
+ this.setState({
+ filter: filter,
+ index: 0
+ }, () => {
+ this.refs.search.focus()
+ })
+ }
+
+ handleOnlyMarkdownCheckboxChange (e) {
+ let { filter } = this.state
+ filter.includeMarkdown = e.target.checked
+ this.refs.list.resetScroll()
+ this.setState({
+ filter: filter,
+ index: 0
+ }, () => {
+ this.refs.search.focus()
+ })
+ }
+
+ handleAllNotesButtonClick (e) {
+ let { filter } = this.state
+ filter.type = 'ALL'
+ this.refs.list.resetScroll()
+ this.setState({
+ filter,
+ index: 0
+ }, () => {
+ this.refs.search.focus()
+ })
+ }
+
+ handleStarredButtonClick (e) {
+ let { filter } = this.state
+ filter.type = 'STARRED'
+ this.refs.list.resetScroll()
+ this.setState({
+ filter,
+ index: 0
+ }, () => {
+ this.refs.search.focus()
+ })
+ }
+
+ handleStorageButtonClick (e, storage) {
+ let { filter } = this.state
+ filter.type = 'STORAGE'
+ filter.storage = storage
+ this.refs.list.resetScroll()
+ this.setState({
+ filter,
+ index: 0
+ }, () => {
+ this.refs.search.focus()
+ })
+ }
+
+ handleFolderButtonClick (e, storage, folder) {
+ let { filter } = this.state
+ filter.type = 'FOLDER'
+ filter.storage = storage
+ filter.folder = folder
+ this.refs.list.resetScroll()
+ this.setState({
+ filter,
+ index: 0
+ }, () => {
+ this.refs.search.focus()
+ })
+ }
+
+ handleNoteClick (e, index) {
+ this.setState({
+ index
+ }, () => {
+ this.refs.search.focus()
+ })
}
render () {
- let { articles, activeArticle, status, dispatch } = this.props
- let saveToClipboard = () => this.saveToClipboard()
+ let { storages, notes, config } = this.props
+ let { filter, search } = this.state
+ let storageList = storages
+ .map((storage) => this.handleStorageButtonClick(e, storage)}
+ handleFolderButtonClick={(e, storage, folder) => this.handleFolderButtonClick(e, storage, folder)}
+ />)
+ if (!filter.includeSnippet && filter.includeMarkdown) {
+ notes = notes.filter((note) => note.type === 'MARKDOWN_NOTE')
+ } else if (filter.includeSnippet && !filter.includeMarkdown) {
+ notes = notes.filter((note) => note.type === 'SNIPPET_NOTE')
+ }
+
+ switch (filter.type) {
+ case 'STORAGE':
+ notes = notes.filter((note) => note.storage === filter.storage)
+ break
+ case 'FOLDER':
+ notes = notes.filter((note) => note.storage === filter.storage && note.folder === filter.folder)
+ break
+ case 'STARRED':
+ notes = notes.filter((note) => note.isStarred)
+ }
+
+ if (search.trim().length > 0) {
+ let needle = new RegExp(_.escapeRegExp(search.trim()), 'i')
+ notes = notes.filter((note) => note.title.match(needle))
+ }
+
+ let activeNote = notes[this.state.index]
+ this.noteCount = notes.length
+
return (
-
-
this.handleSearchChange(e)}
- ref='finderInput'
- onChange={this.handleChange}
- value={status.search}
- />
- this.selectArticle(article)}
- />
-
+ this.handleKeyDown(e)}
+ >
+
+ this.handleSearchChange(e)}
+ />
+
+
+
+
+
+
+
+ {storageList}
+
+
+
this.handleNoteClick(e, _index)}
+ />
+
+
+
+
)
}
@@ -180,99 +332,10 @@ FinderMain.propTypes = {
dispatch: PropTypes.func
}
-// Ignore invalid key
-function ignoreInvalidKey (key) {
- return key.length > 0 && !key.match(/^\/\/$/) && !key.match(/^\/$/) && !key.match(/^#$/)
-}
-
-// Build filter object by key
-function buildFilter (key) {
- if (key.match(/^\/\/.+/)) {
- return {type: FOLDER_EXACT_FILTER, value: key.match(/^\/\/(.+)$/)[1]}
- }
- if (key.match(/^\/.+/)) {
- return {type: FOLDER_FILTER, value: key.match(/^\/(.+)$/)[1]}
- }
- if (key.match(/^#(.+)/)) {
- return {type: TAG_FILTER, value: key.match(/^#(.+)$/)[1]}
- }
- return {type: TEXT_FILTER, value: key}
-}
-
-function isContaining (target, needle) {
- return target.match(new RegExp(_.escapeRegExp(needle), 'i'))
-}
-
-function startsWith (target, needle) {
- return target.match(new RegExp('^' + _.escapeRegExp(needle), 'i'))
-}
-
-function remap (state) {
- let { articles, folders, status } = state
-
- let filters = status.search.split(' ')
- .map(key => key.trim())
- .filter(ignoreInvalidKey)
- .map(buildFilter)
-
- let folderExactFilters = filters.filter(filter => filter.type === FOLDER_EXACT_FILTER)
- let folderFilters = filters.filter(filter => filter.type === FOLDER_FILTER)
- let textFilters = filters.filter(filter => filter.type === TEXT_FILTER)
- let tagFilters = filters.filter(filter => filter.type === TAG_FILTER)
-
- let targetFolders
- if (folders != null) {
- let exactTargetFolders = folders.filter(folder => {
- return _.find(folderExactFilters, filter => filter.value.toLowerCase() === folder.name.toLowerCase())
- })
- let fuzzyTargetFolders = folders.filter(folder => {
- return _.find(folderFilters, filter => startsWith(folder.name.replace(/_/g, ''), filter.value.replace(/_/g, '')))
- })
- targetFolders = status.targetFolders = exactTargetFolders.concat(fuzzyTargetFolders)
-
- if (targetFolders.length > 0) {
- articles = articles.filter(article => {
- return _.findWhere(targetFolders, {key: article.FolderKey})
- })
- }
-
- if (textFilters.length > 0) {
- articles = textFilters.reduce((articles, textFilter) => {
- return articles.filter(article => {
- return isContaining(article.title, textFilter.value) || isContaining(article.content, textFilter.value)
- })
- }, articles)
- }
-
- if (tagFilters.length > 0) {
- articles = tagFilters.reduce((articles, tagFilter) => {
- return articles.filter(article => {
- return _.find(article.tags, tag => isContaining(tag, tagFilter.value))
- })
- }, articles)
- }
- }
-
- let activeArticle = _.findWhere(articles, {key: status.articleKey})
- if (activeArticle == null) activeArticle = articles[0]
-
- return {
- articles,
- activeArticle,
- status
- }
-}
-
-var Finder = connect(remap)(FinderMain)
-var store = createStore(reducer)
+var Finder = connect((x) => x)(CSSModules(FinderMain, styles))
function refreshData () {
- let data = dataStore.getData(true)
- store.dispatch(actions.refreshData(data))
-}
-
-window.onfocus = e => {
- refreshData()
+ // let data = dataStore.getData(true)
}
ReactDOM.render((
diff --git a/browser/finder/ipcClient.js b/browser/finder/ipcClient.js
new file mode 100644
index 00000000..55bcf049
--- /dev/null
+++ b/browser/finder/ipcClient.js
@@ -0,0 +1,97 @@
+const nodeIpc = require('node-ipc')
+const { remote, ipcRenderer } = require('electron')
+const { app, Menu } = remote
+const path = require('path')
+const store = require('./store')
+
+nodeIpc.config.id = 'finder'
+nodeIpc.config.retry = 1500
+nodeIpc.config.silent = true
+
+function killFinder () {
+ let finderWindow = remote.getCurrentWindow()
+ finderWindow.removeAllListeners()
+ if (global.process.platform === 'darwin') {
+ // Only OSX has another app process.
+ app.quit()
+ } else {
+ finderWindow.close()
+ }
+}
+
+function toggleFinder () {
+ let finderWindow = remote.getCurrentWindow()
+ if (global.process.platform === 'darwin') {
+ if (finderWindow.isVisible()) {
+ finderWindow.hide()
+ Menu.sendActionToFirstResponder('hide:')
+ } else {
+ nodeIpc.of.node.emit('request-data')
+ finderWindow.show()
+ }
+ } else {
+ if (!finderWindow.isMinimized()) {
+ finderWindow.minimize()
+ } else {
+ nodeIpc.of.node.emit('request-data')
+ finderWindow.restore()
+ }
+ }
+}
+
+nodeIpc.connectTo(
+ 'node',
+ path.join(app.getPath('userData'), 'boostnote.service'),
+ function () {
+ nodeIpc.of.node.on('error', function (err) {
+ console.log(err)
+ })
+ nodeIpc.of.node.on('connect', function () {
+ console.log('Conncted successfully')
+ })
+ nodeIpc.of.node.on('disconnect', function () {
+ console.log('disconnected')
+ })
+
+ nodeIpc.of.node.on('open-finder', function (payload) {
+ toggleFinder()
+ })
+ ipcRenderer.on('open-finder-from-tray', function () {
+ toggleFinder()
+ })
+ ipcRenderer.on('open-main-from-tray', function () {
+ nodeIpc.of.node.emit('open-main-from-finder')
+ })
+
+ ipcRenderer.on('quit-from-tray', function () {
+ nodeIpc.of.node.emit('quit-from-finder')
+ killFinder()
+ })
+
+ nodeIpc.of.node.on('throttle-data', function (payload) {
+ console.log('Received data from Main renderer')
+ store.default.dispatch({
+ type: 'THROTTLE_DATA',
+ storages: payload.storages,
+ notes: payload.notes
+ })
+ })
+
+ nodeIpc.of.node.on('config-renew', function (payload) {
+ console.log('config', payload)
+ store.default.dispatch({
+ type: 'SET_CONFIG',
+ config: payload
+ })
+ })
+
+ nodeIpc.of.node.on('quit-finder-app', function () {
+ nodeIpc.of.node.emit('quit-finder-app-confirm')
+ killFinder()
+ })
+ }
+)
+
+const ipc = {}
+
+module.exports = ipc
diff --git a/browser/finder/reducer.js b/browser/finder/reducer.js
deleted file mode 100644
index 432c9761..00000000
--- a/browser/finder/reducer.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import { combineReducers } from 'redux'
-import { SELECT_ARTICLE, SEARCH_ARTICLE, REFRESH_DATA } from './actions'
-
-let initialArticles = []
-let initialFolders = []
-let initialStatus = {
- articleKey: null,
- search: ''
-}
-
-function status (state = initialStatus, action) {
- switch (action.type) {
- case SELECT_ARTICLE:
- state.articleKey = action.data.key
- return Object.assign({}, state)
- case SEARCH_ARTICLE:
- state.search = action.data.input
- return Object.assign({}, state)
- default:
- return state
- }
-}
-
-function articles (state = initialArticles, action) {
- switch (action.type) {
- case REFRESH_DATA:
- return action.data.articles
- default:
- return state
- }
-}
-
-function folders (state = initialFolders, action) {
- switch (action.type) {
- case REFRESH_DATA:
- console.log(action)
- return action.data.folders
- default:
- return state
- }
-}
-
-export default combineReducers({
- status,
- folders,
- articles
-})
diff --git a/browser/finder/store.js b/browser/finder/store.js
new file mode 100644
index 00000000..c2bf7ac4
--- /dev/null
+++ b/browser/finder/store.js
@@ -0,0 +1,77 @@
+import { combineReducers, createStore } from 'redux'
+import { routerReducer } from 'react-router-redux'
+
+const OSX = global.process.platform === 'darwin'
+const defaultConfig = {
+ zoom: 1,
+ isSideNavFolded: false,
+ listWidth: 250,
+ hotkey: {
+ toggleFinder: OSX ? 'Cmd + Alt + S' : 'Super + Alt + S',
+ toggleMain: OSX ? 'Cmd + Alt + L' : 'Super + Alt + E'
+ },
+ ui: {
+ theme: 'default',
+ disableDirectWrite: false
+ },
+ editor: {
+ theme: 'xcode',
+ fontSize: '14',
+ fontFamily: 'Monaco, Consolas',
+ indentType: 'space',
+ indentSize: '4',
+ switchPreview: 'BLUR' // Available value: RIGHTCLICK, BLUR
+ },
+ preview: {
+ fontSize: '14',
+ fontFamily: 'Lato',
+ codeBlockTheme: 'xcode',
+ lineNumber: true
+ }
+}
+
+function storages (state = [], action) {
+ switch (action.type) {
+ case 'THROTTLE_DATA':
+ state = action.storages
+ }
+ return state
+}
+function notes (state = [], action) {
+ switch (action.type) {
+ case 'THROTTLE_DATA':
+ state = action.notes
+ }
+ return state
+}
+
+function config (state = defaultConfig, action) {
+ switch (action.type) {
+ case 'INIT_CONFIG':
+ case 'SET_CONFIG':
+ return Object.assign({}, state, action.config)
+ case 'SET_IS_SIDENAV_FOLDED':
+ state.isSideNavFolded = action.isFolded
+ return Object.assign({}, state)
+ case 'SET_ZOOM':
+ state.zoom = action.zoom
+ return Object.assign({}, state)
+ case 'SET_LIST_WIDTH':
+ state.listWidth = action.listWidth
+ return Object.assign({}, state)
+ case 'SET_UI':
+ return Object.assign({}, state, action.config)
+ }
+ return state
+}
+
+let reducer = combineReducers({
+ storages,
+ notes,
+ config,
+ routing: routerReducer
+})
+
+let store = createStore(reducer)
+
+export default store
diff --git a/browser/main/Main.js b/browser/main/Main.js
index 8c688833..8725b091 100644
--- a/browser/main/Main.js
+++ b/browser/main/Main.js
@@ -12,6 +12,7 @@ import _ from 'lodash'
import ConfigManager from 'browser/main/lib/ConfigManager'
import modal from 'browser/main/lib/modal'
import InitModal from 'browser/main/modals/InitModal'
+import ipc from 'browser/main/lib/ipc'
class Main extends React.Component {
constructor (props) {
diff --git a/browser/main/SideNav/StorageItem.styl b/browser/main/SideNav/StorageItem.styl
index c5058ee8..47de54b5 100644
--- a/browser/main/SideNav/StorageItem.styl
+++ b/browser/main/SideNav/StorageItem.styl
@@ -58,7 +58,7 @@
.folderList-item
display block
width 100%
- height 3 0px
+ height 30px
background-color transparent
color $ui-inactive-text-color
padding 0
diff --git a/browser/main/StatusBar/StatusBar.styl b/browser/main/StatusBar/StatusBar.styl
index f91d5930..9286ae25 100644
--- a/browser/main/StatusBar/StatusBar.styl
+++ b/browser/main/StatusBar/StatusBar.styl
@@ -27,7 +27,6 @@
position absolute
right 60px
height 24px
- width 125px
border-width 0 0 0 1px
border-style solid
border-color $ui-borderColor
diff --git a/browser/main/StatusBar/index.js b/browser/main/StatusBar/index.js
index 4a1ade40..28486281 100644
--- a/browser/main/StatusBar/index.js
+++ b/browser/main/StatusBar/index.js
@@ -15,13 +15,17 @@ class StatusBar extends React.Component {
super(props)
this.state = {
- updateAvailable: false
+ updateReady: false
}
}
componentDidMount () {
- ipc.on('update-available', function (message) {
- this.setState({updateAvailable: true})
+ ipc.on('update-ready', function (message) {
+ this.setState({
+ updateReady: true
+ }, () => {
+ this.updateApp()
+ })
}.bind(this))
}
@@ -34,7 +38,7 @@ class StatusBar extends React.Component {
})
if (index === 0) {
- ipc.send('update-app', 'Deal with it.')
+ remote.getCurrentWindow().webContents.send('update-app')
}
}
@@ -68,9 +72,9 @@ class StatusBar extends React.Component {
styleName='root'
>
{location.pathname + location.search}
- {this.state.updateAvailable
+ {this.state.updateReady
?
: null
}
diff --git a/browser/main/index.js b/browser/main/index.js
index 015dffed..8298348a 100644
--- a/browser/main/index.js
+++ b/browser/main/index.js
@@ -10,18 +10,9 @@ import { syncHistoryWithStore } from 'react-router-redux'
const electron = require('electron')
const ipc = electron.ipcRenderer
-const path = require('path')
-const remote = electron.remote
-
-if (process.env.NODE_ENV !== 'production') {
- window.addEventListener('keydown', function (e) {
- if (e.keyCode === 73 && e.metaKey && e.altKey) {
- remote.getCurrentWindow().toggleDevTools()
- }
- })
-}
activityRecord.init()
+ipc.send('check-update', 'check-update')
window.addEventListener('online', function () {
ipc.send('check-update', 'check-update')
})
@@ -35,28 +26,6 @@ document.addEventListener('dragover', function (e) {
e.stopPropagation()
})
-function notify (title, options) {
- if (process.platform === 'win32') {
- options.icon = path.join('file://', global.__dirname, '../../resources/app.png')
- options.silent = false
- }
- console.log(options)
- return new window.Notification(title, options)
-}
-
-ipc.on('notify', function (e, payload) {
- notify(payload.title, {
- body: payload.body
- })
-})
-
-ipc.on('copy-finder', function () {
- activityRecord.emit('FINDER_COPY')
-})
-ipc.on('open-finder', function () {
- activityRecord.emit('FINDER_OPEN')
-})
-
let el = document.getElementById('content')
const history = syncHistoryWithStore(hashHistory, store)
diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js
index 075ac8e1..43802707 100644
--- a/browser/main/lib/ConfigManager.js
+++ b/browser/main/lib/ConfigManager.js
@@ -2,7 +2,7 @@ import _ from 'lodash'
const OSX = global.process.platform === 'darwin'
const electron = require('electron')
-const { ipcRenderer } = electron
+const { remote } = electron
const defaultConfig = {
zoom: 1,
@@ -66,17 +66,13 @@ function set (updates) {
let newConfig = Object.assign({}, defaultConfig, currentConfig, updates)
if (!validate(newConfig)) throw new Error('INVALID CONFIG')
_save(newConfig)
- ipcRenderer.send('CONFIG_RENEW', {
+
+ remote.getCurrentWindow().webContents.send('config-renew', {
config: get(),
silent: false
})
}
-ipcRenderer.send('CONFIG_RENEW', {
- config: get(),
- silent: true
-})
-
export default {
get,
set,
diff --git a/browser/main/lib/ipc.js b/browser/main/lib/ipc.js
new file mode 100644
index 00000000..e0d117cf
--- /dev/null
+++ b/browser/main/lib/ipc.js
@@ -0,0 +1,138 @@
+import store from 'browser/main/store'
+import ConfigManager from 'browser/main/lib/ConfigManager'
+
+const nodeIpc = require('node-ipc')
+const { remote, ipcRenderer } = require('electron')
+const { app, Menu } = remote
+const path = require('path')
+
+nodeIpc.config.id = 'node'
+nodeIpc.config.retry = 1500
+nodeIpc.config.silent = true
+console.log('Initializing IPC Server')
+
+// TODO: IPC SERVER WILL BE MOVED TO MAIN PROCESS FROM MAIN WINDOW PROCESS(RENDERER)
+nodeIpc.serve(
+ path.join(app.getPath('userData'), 'boostnote.service'),
+ function () {
+ console.log('IPC Server Started')
+ ipcRenderer.on('open-finder', function () {
+ console.log('Open finder')
+ nodeIpc.server.broadcast('open-finder')
+ })
+
+ /** Quit Sequence
+ 1. `quit-app` Main process -> Main window by Electron IPC
+ 2. `quit-finder-app` Main window -> Finder window by Node IPC(socket)
+ 3. Finder window (and Finder main process: OSX only) killed by remote API
+ 4. `quit-finder-app-confirm` Finder window -> Main window by NodeIPC
+ 5. `quit-app-confirm` Main window -> Main process by Electron IPC
+ 6. Main process discard close preventer and terminate Main window and itself.
+
+ If the platform is a linux without cinnamon, the app will skip 2.-4. because it doesn't launch finder window.
+ `quit-app` will fires directly `quit-app-confirm`.
+ */
+ ipcRenderer.on('quit-app', function () {
+ // Finder app exists only in the linux with cinnamon.
+ if (global.process.env.platform === 'linux' && global.process.env.DESKTOP_SESSION !== 'cinnamon') {
+ ipcRenderer.send('quit-app-confirm')
+ return
+ }
+ let confirmHandler = function () {
+ ipcRenderer.send('quit-app-confirm')
+ }
+ nodeIpc.server.on('quit-finder-app-confirm', confirmHandler)
+ setTimeout(() => {
+ nodeIpc.server.removeListener('quit-finder-app-confirm', confirmHandler)
+ }, 1000)
+ nodeIpc.server.broadcast('quit-finder-app')
+ })
+
+ /** Update Sequence
+ 1. `update-ready` Main process -> Main window by Electron IPC
+ 2. `update-app` Main window -> Main window by Electron IPC
+ 3. `quit-finder-app` Main window -> Finder window by Node IPC
+ 4. Finder window (and Finder main process: OSX only) killed by remote API
+ 5. `quit-finder-app-confirm` Finder window -> Main window by NodeIPC
+ 6. `update-app-confirm` Main window -> Main process by Electron IPC
+ 7. Main process discard close preventer and start updating.
+
+ Handlers of 1. and 2. can be found in StatusBar component.
+ */
+ ipcRenderer.on('update-app', function () {
+ // Linux app doesn't support auto updater
+ if (global.process.env.platform === 'linux') {
+ return
+ }
+ let confirmHandler = function () {
+ ipcRenderer.send('update-app-confirm')
+ }
+ nodeIpc.server.on('quit-finder-app-confirm', confirmHandler)
+ setTimeout(() => {
+ nodeIpc.server.removeListener('quit-finder-app-confirm', confirmHandler)
+ }, 1000)
+ nodeIpc.server.broadcast('quit-finder-app')
+ })
+
+ ipcRenderer.on('update-found', function () {
+ console.log('Update found')
+ })
+
+ let config = ConfigManager.get()
+ nodeIpc.server.broadcast('config-renew', config)
+ ipcRenderer.send('config-renew', {
+ config: config,
+ silent: true
+ })
+ ipcRenderer.on('config-renew', function (e, data) {
+ nodeIpc.server.broadcast('config-renew', data.config)
+ ipcRenderer.send('config-renew', data)
+ })
+
+ nodeIpc.server.on('open-main-from-finder', function () {
+ let mainWindow = remote.getCurrentWindow()
+ console.log('open main from finder')
+ if (mainWindow.isFocused()) {
+ if (global.process.platform === 'darwin') {
+ Menu.sendActionToFirstResponder('hide:')
+ } else {
+ mainWindow.minimize()
+ }
+ } else {
+ if (global.process.platform === 'darwin') {
+ mainWindow.show()
+ } else {
+ mainWindow.minimize()
+ mainWindow.restore()
+ }
+ }
+ })
+
+ nodeIpc.server.on('quit-from-finder', function () {
+ ipcRenderer.send('quit-app-confirm')
+ })
+
+ nodeIpc.server.on('connect', function (socket) {
+ console.log('connected')
+ socket.on('close', function () {
+ console.log('socket dead')
+ })
+ })
+ nodeIpc.server.on('error', function (err) {
+ console.error('Node IPC error', err)
+ })
+ nodeIpc.server.on('request-data', function (data, socket) {
+ let state = store.getState()
+ nodeIpc.server.broadcast('throttle-data', {
+ storages: state.storages,
+ notes: state.notes
+ })
+ })
+ }
+)
+const ipc = {
+
+}
+
+nodeIpc.server.start()
+module.exports = ipc
diff --git a/browser/main/lib/notify.js b/browser/main/lib/notify.js
new file mode 100644
index 00000000..458a784d
--- /dev/null
+++ b/browser/main/lib/notify.js
@@ -0,0 +1,11 @@
+const path = require('path')
+
+function notify (title, options) {
+ if (process.platform === 'win32') {
+ options.icon = path.join('file://', global.__dirname, '../../resources/app.png')
+ options.silent = false
+ }
+ return new window.Notification(title, options)
+}
+
+export default notify
diff --git a/browser/main/modals/PreferencesModal/ConfigTab.js b/browser/main/modals/PreferencesModal/ConfigTab.js
index ade253c4..947ad318 100644
--- a/browser/main/modals/PreferencesModal/ConfigTab.js
+++ b/browser/main/modals/PreferencesModal/ConfigTab.js
@@ -180,7 +180,6 @@ class ConfigTab extends React.Component {
ref='toggleFinder'
value={config.hotkey.toggleFinder}
type='text'
- disabled
/>
diff --git a/lib/finder-app.js b/lib/finder-app.js
index 5ae68823..b158e308 100755
--- a/lib/finder-app.js
+++ b/lib/finder-app.js
@@ -9,9 +9,9 @@ app.on('ready', function () {
app.dock.hide()
}
- var template = require('./finder-menu')
- var menu = Menu.buildFromTemplate(template)
- Menu.setApplicationMenu(menu)
+ // var template = require('./finder-menu')
+ // var menu = Menu.buildFromTemplate(template)
+ // Menu.setApplicationMenu(menu)
finderWindow = require('./finder-window')
})
diff --git a/lib/finder-window.js b/lib/finder-window.js
index f93d9836..e75bab16 100644
--- a/lib/finder-window.js
+++ b/lib/finder-window.js
@@ -2,80 +2,12 @@ const electron = require('electron')
const BrowserWindow = electron.BrowserWindow
const Menu = electron.Menu
const MenuItem = electron.MenuItem
-const app = electron.app
-const ipcMain = electron.ipcMain
const Tray = electron.Tray
const path = require('path')
-const nodeIpc = require('@rokt33r/node-ipc')
-
-var appQuit = false
-var isFinderLoaded = false
-
-nodeIpc.config.id = 'finder'
-nodeIpc.config.retry = 1500
-nodeIpc.config.silent = true
-
-nodeIpc.connectTo(
- 'main',
- path.join(app.getPath('userData'), 'boost.service'),
- function () {
- nodeIpc.of.main.on(
- 'error',
- function (err) {
- nodeIpc.log('<< ## err ##'.rainbow, nodeIpc.config.delay)
- nodeIpc.log(err)
- }
- )
- nodeIpc.of.main.on(
- 'connect',
- function () {
- nodeIpc.log('<< ## connected to world ##'.rainbow, nodeIpc.config.delay)
- }
- )
- nodeIpc.of.main.on(
- 'disconnect',
- function () {
- nodeIpc.log('<< disconnected from main'.notice)
- if (process.platform === 'darwin') {
- appQuit = true
- app.quit()
- }
- }
- )
- nodeIpc.of.main.on(
- 'message',
- function (payload) {
- if (isFinderLoaded) {
- switch (payload.type) {
- case 'open-finder':
- if (finderWindow.isFocused()) {
- hideFinder()
- } else {
- openFinder()
- }
- break
- case 'config-apply': {
- finderWindow.webContents.send('config-apply', payload.data)
- break
- }
- }
- }
- }
- )
- }
-)
-
-function emit (type, data) {
- var payload = {
- type: type,
- data: data
- }
- nodeIpc.of.main.emit('message', payload)
-}
var config = {
- width: 640,
- height: 400,
+ width: 840,
+ height: 540,
show: false,
frame: false,
resizable: false,
@@ -107,11 +39,8 @@ finderWindow.on('blur', function () {
})
finderWindow.on('close', function (e) {
- if (process.platform === 'darwin') {
- if (appQuit) return true
- e.preventDefault()
- finderWindow.hide()
- }
+ e.preventDefault()
+ finderWindow.hide()
})
var appIcon = new Tray(path.join(__dirname, '../resources/tray-icon.png'))
@@ -121,19 +50,21 @@ var trayMenu = new Menu()
trayMenu.append(new MenuItem({
label: 'Open Main window',
click: function () {
- emit('show-main-window')
- }
-}))
-trayMenu.append(new MenuItem({
- label: 'Open Finder window',
- click: function () {
- openFinder()
+ finderWindow.webContents.send('open-main-from-tray')
}
}))
+if (process.env.platform !== 'linux' || process.env.DESKTOP_SESSION === 'cinnamon') {
+ trayMenu.append(new MenuItem({
+ label: 'Open Finder window',
+ click: function () {
+ finderWindow.webContents.send('open-finder-from-tray')
+ }
+ }))
+}
trayMenu.append(new MenuItem({
label: 'Quit',
click: function () {
- emit('quit-app')
+ finderWindow.webContents.send('quit-from-tray')
}
}))
@@ -143,21 +74,6 @@ appIcon.on('click', function (e) {
appIcon.popUpContextMenu(trayMenu)
})
-ipcMain.on('copy-finder', function () {
- emit('copy-finder')
-})
-
-ipcMain.on('hide-finder', function () {
- hideFinder()
-})
-
-finderWindow.webContents.on('did-finish-load', function () {
- isFinderLoaded = true
-})
-
-function openFinder () {
- if (isFinderLoaded) finderWindow.show()
-}
function hideFinder () {
if (process.platform === 'win32') {
finderWindow.minimize()
diff --git a/lib/finder.html b/lib/finder.html
index 8f4d74db..413d1aa1 100644
--- a/lib/finder.html
+++ b/lib/finder.html
@@ -28,11 +28,12 @@
+
-
-
-
-
+
+
+
+