mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-14 10:16:26 +00:00
Compare commits
49 Commits
0.4.0-beta
...
v0.4.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e36478b9ac | ||
|
|
e1fe4dd693 | ||
|
|
b1ee949b1c | ||
|
|
a0e5f8e97e | ||
|
|
80a0c59f87 | ||
|
|
823fdec705 | ||
|
|
fe87dcced7 | ||
|
|
137eb44516 | ||
|
|
f60d957102 | ||
|
|
8f0b04504f | ||
|
|
2c39d8b1c8 | ||
|
|
d4d1c32288 | ||
|
|
e4f39d2b6a | ||
|
|
e5a2bfbcbd | ||
|
|
de3b76b31d | ||
|
|
53455496bf | ||
|
|
cc2a2f6dfb | ||
|
|
ee4ac7371c | ||
|
|
d5265407b9 | ||
|
|
954b3e9fc5 | ||
|
|
7d9894bef7 | ||
|
|
3b34698e8b | ||
|
|
263cb581c4 | ||
|
|
1c9cb4516c | ||
|
|
ac4ceccb4f | ||
|
|
e731b7882d | ||
|
|
84e0728ff3 | ||
|
|
666bc18e91 | ||
|
|
8f83124a0d | ||
|
|
ee91daad7e | ||
|
|
ee78c0d33b | ||
|
|
09482ebcf3 | ||
|
|
67424f2d3a | ||
|
|
51f530ffbe | ||
|
|
013f96a754 | ||
|
|
df6a018fb6 | ||
|
|
409eaf54c1 | ||
|
|
7e04fd342c | ||
|
|
1fe15bc6a5 | ||
|
|
ff1bffbb55 | ||
|
|
b28b18a19a | ||
|
|
bbc3c85212 | ||
|
|
26a08fac06 | ||
|
|
da9d7a4336 | ||
|
|
46c6555f94 | ||
|
|
3e980fd2d4 | ||
|
|
fb1462f669 | ||
|
|
41e1630aac | ||
|
|
ef84c4e3da |
@@ -1,5 +1,6 @@
|
||||
var BrowserWindow = require('browser-window')
|
||||
var path = require('path')
|
||||
const electron = require('electron')
|
||||
const BrowserWindow = electron.BrowserWindow
|
||||
const path = require('path')
|
||||
|
||||
var finderWindow = new BrowserWindow({
|
||||
width: 640,
|
||||
@@ -18,7 +19,7 @@ var finderWindow = new BrowserWindow({
|
||||
|
||||
var url = path.resolve(__dirname, '../browser/finder/index.html')
|
||||
|
||||
finderWindow.loadUrl('file://' + url)
|
||||
finderWindow.loadURL('file://' + url)
|
||||
|
||||
finderWindow.on('blur', function () {
|
||||
finderWindow.hide()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
var BrowserWindow = require('browser-window')
|
||||
var path = require('path')
|
||||
const electron = require('electron')
|
||||
const BrowserWindow = electron.BrowserWindow
|
||||
const path = require('path')
|
||||
|
||||
var mainWindow = new BrowserWindow({
|
||||
width: 1080,
|
||||
@@ -11,9 +12,9 @@ var mainWindow = new BrowserWindow({
|
||||
'standard-window': false
|
||||
})
|
||||
|
||||
var url = path.resolve(__dirname, '../browser/main/index.html')
|
||||
const url = path.resolve(__dirname, '../browser/main/index.html')
|
||||
|
||||
mainWindow.loadUrl('file://' + url)
|
||||
mainWindow.loadURL('file://' + url)
|
||||
|
||||
mainWindow.setVisibleOnAllWorkspaces(true)
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
var BrowserWindow = require('browser-window')
|
||||
const electron = require('electron')
|
||||
const BrowserWindow = electron.BrowserWindow
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
@@ -89,13 +90,6 @@ module.exports = [
|
||||
click: function () {
|
||||
BrowserWindow.getFocusedWindow().reload()
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Toggle DevTools',
|
||||
accelerator: 'Alt+Command+I',
|
||||
click: function () {
|
||||
BrowserWindow.getFocusedWindow().toggleDevTools()
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
var autoUpdater = require('auto-updater')
|
||||
var nn = require('node-notifier')
|
||||
var app = require('app')
|
||||
var path = require('path')
|
||||
|
||||
var version = app.getVersion()
|
||||
var versionText = (version == null || version.length === 0) ? 'DEV version' : 'v' + version
|
||||
|
||||
autoUpdater
|
||||
.on('error', function (err, message) {
|
||||
console.error(err)
|
||||
console.error(message)
|
||||
nn.notify({
|
||||
title: 'Error! ' + versionText,
|
||||
icon: path.join(__dirname, 'browser/main/resources/favicon-230x230.png'),
|
||||
message: message
|
||||
})
|
||||
})
|
||||
// .on('checking-for-update', function () {
|
||||
// // Connecting
|
||||
// })
|
||||
.on('update-available', function () {
|
||||
nn.notify({
|
||||
title: 'Update is available!! ' + versionText,
|
||||
icon: path.join(__dirname, 'browser/main/resources/favicon-230x230.png'),
|
||||
message: 'Download started.. wait for the update ready.'
|
||||
})
|
||||
})
|
||||
.on('update-not-available', function () {
|
||||
nn.notify({
|
||||
title: 'Latest Build!! ' + versionText,
|
||||
icon: path.join(__dirname, 'browser/main/resources/favicon-230x230.png'),
|
||||
message: 'Hope you to enjoy our app :D'
|
||||
})
|
||||
})
|
||||
|
||||
module.exports = autoUpdater
|
||||
@@ -11,7 +11,16 @@ export default class FinderDetail extends React.Component {
|
||||
return (
|
||||
<div className='FinderDetail'>
|
||||
<div className='header'>
|
||||
<ModeIcon mode={activeArticle.mode}/> {activeArticle.title}</div>
|
||||
<div className='left'>
|
||||
<ModeIcon mode={activeArticle.mode}/> {activeArticle.title}
|
||||
</div>
|
||||
<div className='right'>
|
||||
<button onClick={this.props.saveToClipboard} className='clipboardBtn'>
|
||||
<i className='fa fa-clipboard fa-fw'/>
|
||||
<span className='tooltip'>Copy to clipboard (Enter)</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='content'>
|
||||
{activeArticle.mode === 'markdown'
|
||||
? <MarkdownPreview content={activeArticle.content}/>
|
||||
@@ -30,5 +39,6 @@ export default class FinderDetail extends React.Component {
|
||||
}
|
||||
|
||||
FinderDetail.propTypes = {
|
||||
activeArticle: PropTypes.shape()
|
||||
activeArticle: PropTypes.shape(),
|
||||
saveToClipboard: PropTypes.func
|
||||
}
|
||||
|
||||
@@ -16,11 +16,8 @@ export function searchArticle (input) {
|
||||
}
|
||||
}
|
||||
|
||||
export function refreshData () {
|
||||
export function refreshData (data) {
|
||||
console.log('refreshing data')
|
||||
let data = JSON.parse(localStorage.getItem('local'))
|
||||
if (data == null) return null
|
||||
|
||||
let { folders, articles } = data
|
||||
|
||||
return {
|
||||
@@ -31,3 +28,12 @@ export function refreshData () {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
SELECT_ARTICLE,
|
||||
SEARCH_ARTICLE,
|
||||
REFRESH_DATA,
|
||||
selectArticle,
|
||||
searchArticle,
|
||||
refreshData
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<title>CodeXen Popup</title>
|
||||
<title>Boost Finder</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../../node_modules/font-awesome/css/font-awesome.min.css" media="screen" charset="utf-8">
|
||||
<link rel="stylesheet" href="../../node_modules/devicon/devicon.min.css">
|
||||
<link rel="stylesheet" href="../../node_modules/highlight.js/styles/xcode.css">
|
||||
<link rel="shortcut icon" href="favicon.ico">
|
||||
|
||||
<style>
|
||||
@@ -27,7 +28,8 @@
|
||||
<div id="content"></div>
|
||||
<script src="../../submodules/ace/src-min/ace.js"></script>
|
||||
<script>
|
||||
require('web-frame').setZoomLevelLimits(1, 1)
|
||||
const electron = require('electron')
|
||||
electron.webFrame.setZoomLevelLimits(1, 1)
|
||||
var scriptUrl = process.env.BOOST_ENV === 'development'
|
||||
? 'http://localhost:8080/assets/finder.js'
|
||||
: '../../compiled/finder.js'
|
||||
|
||||
@@ -6,17 +6,23 @@ import { createStore } from 'redux'
|
||||
import FinderInput from './FinderInput'
|
||||
import FinderList from './FinderList'
|
||||
import FinderDetail from './FinderDetail'
|
||||
import { selectArticle, searchArticle, refreshData } from './actions'
|
||||
import actions, { selectArticle, searchArticle } from './actions'
|
||||
import _ from 'lodash'
|
||||
import activityRecord from 'boost/activityRecord'
|
||||
import dataStore from 'boost/dataStore'
|
||||
|
||||
const electron = require('electron')
|
||||
const { remote, clipboard } = electron
|
||||
|
||||
import remote from 'remote'
|
||||
var hideFinder = remote.getGlobal('hideFinder')
|
||||
import clipboard from 'clipboard'
|
||||
|
||||
function notify (...args) {
|
||||
return new window.Notification(...args)
|
||||
}
|
||||
|
||||
require('../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'
|
||||
|
||||
@@ -45,10 +51,7 @@ class FinderMain extends React.Component {
|
||||
}
|
||||
|
||||
if (e.keyCode === 13) {
|
||||
let { activeArticle } = this.props
|
||||
clipboard.writeText(activeArticle.content)
|
||||
activityRecord.emit('FINDER_COPY')
|
||||
hideFinder()
|
||||
this.saveToClipboard()
|
||||
e.preventDefault()
|
||||
}
|
||||
if (e.keyCode === 27) {
|
||||
@@ -57,6 +60,16 @@ class FinderMain extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
saveToClipboard () {
|
||||
let { activeArticle } = this.props
|
||||
clipboard.writeText(activeArticle.content)
|
||||
|
||||
notify('Saved to Clipboard!', {
|
||||
body: 'Paste it wherever you want!'
|
||||
})
|
||||
hideFinder()
|
||||
}
|
||||
|
||||
handleSearchChange (e) {
|
||||
let { dispatch } = this.props
|
||||
|
||||
@@ -83,6 +96,7 @@ class FinderMain extends React.Component {
|
||||
|
||||
render () {
|
||||
let { articles, activeArticle, status, dispatch } = this.props
|
||||
let saveToClipboard = () => this.saveToClipboard()
|
||||
return (
|
||||
<div onClick={e => this.handleClick(e)} onKeyDown={e => this.handleKeyDown(e)} className='Finder'>
|
||||
<FinderInput
|
||||
@@ -98,7 +112,10 @@ class FinderMain extends React.Component {
|
||||
dispatch={dispatch}
|
||||
selectArticle={article => this.selectArticle(article)}
|
||||
/>
|
||||
<FinderDetail activeArticle={activeArticle}/>
|
||||
<FinderDetail
|
||||
activeArticle={activeArticle}
|
||||
saveToClipboard={saveToClipboard}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -116,27 +133,47 @@ FinderMain.propTypes = {
|
||||
dispatch: PropTypes.func
|
||||
}
|
||||
|
||||
function remap (state) {
|
||||
let { articles, folders, status } = state
|
||||
// Ignore invalid key
|
||||
function ignoreInvalidKey (key) {
|
||||
return key.length > 0 && !key.match(/^\/\/$/) && !key.match(/^\/$/) && !key.match(/^#$/)
|
||||
}
|
||||
|
||||
let filters = status.search.split(' ').map(key => key.trim()).filter(key => key.length > 0 && !key.match(/^#$/)).map(key => {
|
||||
if (key.match(/^in:.+$/)) {
|
||||
return {type: FOLDER_FILTER, value: key.match(/^in:(.+)$/)[1]}
|
||||
// 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 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 targetFolders = folders.filter(folder => {
|
||||
return _.findWhere(folderFilters, {value: folder.name})
|
||||
let exactTargetFolders = folders.filter(folder => {
|
||||
return _.find(folderExactFilters, filter => folder.name.match(new RegExp(`^${filter.value}$`)))
|
||||
})
|
||||
status.targetFolders = targetFolders
|
||||
let fuzzyTargetFolders = folders.filter(folder => {
|
||||
return _.find(folderFilters, filter => folder.name.match(new RegExp(`^${filter.value}`)))
|
||||
})
|
||||
targetFolders = status.targetFolders = exactTargetFolders.concat(fuzzyTargetFolders)
|
||||
|
||||
if (targetFolders.length > 0) {
|
||||
articles = articles.filter(article => {
|
||||
@@ -164,6 +201,7 @@ function remap (state) {
|
||||
let activeArticle = _.findWhere(articles, {key: status.articleKey})
|
||||
if (activeArticle == null) activeArticle = articles[0]
|
||||
|
||||
console.log(status.search)
|
||||
return {
|
||||
articles,
|
||||
activeArticle,
|
||||
@@ -174,13 +212,19 @@ function remap (state) {
|
||||
var Finder = connect(remap)(FinderMain)
|
||||
var store = createStore(reducer)
|
||||
|
||||
function refreshData () {
|
||||
let data = dataStore.getData()
|
||||
store.dispatch(actions.refreshData(data))
|
||||
}
|
||||
|
||||
window.onfocus = e => {
|
||||
store.dispatch(refreshData())
|
||||
activityRecord.emit('FINDER_OPEN')
|
||||
refreshData()
|
||||
}
|
||||
|
||||
ReactDOM.render((
|
||||
<Provider store={store}>
|
||||
<Finder/>
|
||||
</Provider>
|
||||
), document.getElementById('content'))
|
||||
), document.getElementById('content'), function () {
|
||||
refreshData()
|
||||
})
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { combineReducers } from 'redux'
|
||||
import { SELECT_ARTICLE, SEARCH_ARTICLE, REFRESH_DATA } from './actions'
|
||||
|
||||
let data = JSON.parse(localStorage.getItem('local'))
|
||||
|
||||
let initialArticles = data != null ? data.articles : []
|
||||
let initialFolders = data != null ? data.folders : []
|
||||
let initialArticles = []
|
||||
let initialFolders = []
|
||||
let initialStatus = {
|
||||
articleKey: null,
|
||||
search: ''
|
||||
@@ -14,10 +12,10 @@ function status (state = initialStatus, action) {
|
||||
switch (action.type) {
|
||||
case SELECT_ARTICLE:
|
||||
state.articleKey = action.data.key
|
||||
return state
|
||||
return Object.assign({}, state)
|
||||
case SEARCH_ARTICLE:
|
||||
state.search = action.data.input
|
||||
return state
|
||||
return Object.assign({}, state)
|
||||
default:
|
||||
return state
|
||||
}
|
||||
|
||||
@@ -7,11 +7,13 @@ import ArticleTopBar from './HomePage/ArticleTopBar'
|
||||
import ArticleList from './HomePage/ArticleList'
|
||||
import ArticleDetail from './HomePage/ArticleDetail'
|
||||
import _ from 'lodash'
|
||||
import keygen from 'boost/keygen'
|
||||
import { isModalOpen, closeModal } from 'boost/modal'
|
||||
const electron = require('electron')
|
||||
const BrowserWindow = electron.remote.BrowserWindow
|
||||
|
||||
const TEXT_FILTER = 'TEXT_FILTER'
|
||||
const FOLDER_FILTER = 'FOLDER_FILTER'
|
||||
const FOLDER_EXACT_FILTER = 'FOLDER_EXACT_FILTER'
|
||||
const TAG_FILTER = 'TAG_FILTER'
|
||||
|
||||
class HomePage extends React.Component {
|
||||
@@ -27,6 +29,13 @@ class HomePage extends React.Component {
|
||||
}
|
||||
|
||||
handleKeyDown (e) {
|
||||
if (process.env.BOOST_ENV === 'development' && e.keyCode === 73 && e.metaKey && e.altKey) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
BrowserWindow.getFocusedWindow().toggleDevTools()
|
||||
return
|
||||
}
|
||||
|
||||
if (isModalOpen()) {
|
||||
if (e.keyCode === 27) closeModal()
|
||||
return
|
||||
@@ -98,7 +107,7 @@ class HomePage extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { dispatch, status, articles, activeArticle, folders, filters } = this.props
|
||||
let { dispatch, status, articles, allArticles, activeArticle, folders, tags, filters } = this.props
|
||||
|
||||
return (
|
||||
<div className='HomePage'>
|
||||
@@ -107,6 +116,7 @@ class HomePage extends React.Component {
|
||||
dispatch={dispatch}
|
||||
folders={folders}
|
||||
status={status}
|
||||
allArticles={allArticles}
|
||||
/>
|
||||
<ArticleTopBar
|
||||
ref='top'
|
||||
@@ -127,6 +137,7 @@ class HomePage extends React.Component {
|
||||
activeArticle={activeArticle}
|
||||
folders={folders}
|
||||
status={status}
|
||||
tags={tags}
|
||||
filters={filters}
|
||||
/>
|
||||
</div>
|
||||
@@ -134,6 +145,25 @@ class HomePage extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
// 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 remap (state) {
|
||||
let { folders, articles, status } = state
|
||||
|
||||
@@ -141,26 +171,33 @@ function remap (state) {
|
||||
articles.sort((a, b) => {
|
||||
return new Date(b.updatedAt) - new Date(a.updatedAt)
|
||||
})
|
||||
let allArticles = articles.slice()
|
||||
|
||||
let tags = _.uniq(allArticles.reduce((sum, article) => {
|
||||
if (!_.isArray(article.tags)) return sum
|
||||
return sum.concat(article.tags)
|
||||
}, []))
|
||||
|
||||
// Filter articles
|
||||
let filters = status.search.split(' ').map(key => key.trim()).filter(key => key.length > 0 && !key.match(/^#$/)).map(key => {
|
||||
if (key.match(/^in:.+$/)) {
|
||||
return {type: FOLDER_FILTER, value: key.match(/^in:(.+)$/)[1]}
|
||||
}
|
||||
if (key.match(/^#(.+)/)) {
|
||||
return {type: TAG_FILTER, value: key.match(/^#(.+)$/)[1]}
|
||||
}
|
||||
return {type: TEXT_FILTER, value: key}
|
||||
})
|
||||
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 targetFolders = folders.filter(folder => {
|
||||
return _.findWhere(folderFilters, {value: folder.name})
|
||||
let exactTargetFolders = folders.filter(folder => {
|
||||
return _.find(folderExactFilters, filter => folder.name.match(new RegExp(`^${filter.value}$`)))
|
||||
})
|
||||
status.targetFolders = targetFolders
|
||||
let fuzzyTargetFolders = folders.filter(folder => {
|
||||
return _.find(folderFilters, filter => folder.name.match(new RegExp(`^${filter.value}`)))
|
||||
})
|
||||
targetFolders = status.targetFolders = exactTargetFolders.concat(fuzzyTargetFolders)
|
||||
|
||||
if (targetFolders.length > 0) {
|
||||
articles = articles.filter(article => {
|
||||
@@ -189,48 +226,13 @@ function remap (state) {
|
||||
let activeArticle = _.findWhere(articles, {key: status.articleKey})
|
||||
if (activeArticle == null) activeArticle = articles[0]
|
||||
|
||||
// remove Unsaved new article if user is not CREATE_MODE
|
||||
if (status.mode !== CREATE_MODE) {
|
||||
let targetIndex = _.findIndex(articles, article => article.status === NEW)
|
||||
|
||||
if (targetIndex >= 0) articles.splice(targetIndex, 1)
|
||||
}
|
||||
|
||||
// switching CREATE_MODE
|
||||
// restrict
|
||||
// 1. team have one folder at least
|
||||
// or Change IDLE MODE
|
||||
if (status.mode === CREATE_MODE) {
|
||||
let newArticle = _.findWhere(articles, {status: 'NEW'})
|
||||
let FolderKey = folders[0].key
|
||||
if (folderFilters.length > 0) {
|
||||
let targetFolder = _.findWhere(folders, {name: folderFilters[0].value})
|
||||
if (targetFolder != null) FolderKey = targetFolder.key
|
||||
}
|
||||
|
||||
if (newArticle == null) {
|
||||
newArticle = {
|
||||
id: null,
|
||||
key: keygen(),
|
||||
title: '',
|
||||
content: '',
|
||||
mode: 'markdown',
|
||||
tags: [],
|
||||
FolderKey: FolderKey,
|
||||
status: NEW
|
||||
}
|
||||
articles.unshift(newArticle)
|
||||
}
|
||||
activeArticle = newArticle
|
||||
} else if (status.mode === CREATE_MODE) {
|
||||
status.mode = IDLE_MODE
|
||||
}
|
||||
|
||||
return {
|
||||
folders,
|
||||
status,
|
||||
allArticles,
|
||||
articles,
|
||||
activeArticle,
|
||||
tags,
|
||||
filters: {
|
||||
folder: folderFilters,
|
||||
tag: tagFilters,
|
||||
@@ -247,6 +249,7 @@ HomePage.propTypes = {
|
||||
userId: PropTypes.string
|
||||
}),
|
||||
articles: PropTypes.array,
|
||||
allArticles: PropTypes.array,
|
||||
activeArticle: PropTypes.shape(),
|
||||
dispatch: PropTypes.func,
|
||||
folders: PropTypes.array,
|
||||
|
||||
@@ -5,7 +5,19 @@ import _ from 'lodash'
|
||||
import ModeIcon from 'boost/components/ModeIcon'
|
||||
import MarkdownPreview from 'boost/components/MarkdownPreview'
|
||||
import CodeEditor from 'boost/components/CodeEditor'
|
||||
import { IDLE_MODE, CREATE_MODE, EDIT_MODE, switchMode, switchArticle, switchFolder, clearSearch, updateArticle, destroyArticle, NEW } from 'boost/actions'
|
||||
import {
|
||||
IDLE_MODE,
|
||||
EDIT_MODE,
|
||||
switchMode,
|
||||
switchArticle,
|
||||
switchFolder,
|
||||
clearSearch,
|
||||
lockStatus,
|
||||
unlockStatus,
|
||||
updateArticle,
|
||||
destroyArticle,
|
||||
NEW
|
||||
} from 'boost/actions'
|
||||
import linkState from 'boost/linkState'
|
||||
import FolderMark from 'boost/components/FolderMark'
|
||||
import TagLink from 'boost/components/TagLink'
|
||||
@@ -82,7 +94,12 @@ export default class ArticleDetail extends React.Component {
|
||||
|
||||
this.state = {
|
||||
article: makeInstantArticle(props.activeArticle),
|
||||
previewMode: false
|
||||
previewMode: false,
|
||||
isArticleEdited: false,
|
||||
isTagChanged: false,
|
||||
isTitleChanged: false,
|
||||
isContentChanged: false,
|
||||
isModeChanged: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +113,7 @@ export default class ArticleDetail extends React.Component {
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
let isModeChanged = prevProps.status.mode !== this.props.status.mode
|
||||
if (isModeChanged && this.props.status.mode !== IDLE_MODE) {
|
||||
if (isModeChanged && this.props.status.mode === EDIT_MODE) {
|
||||
ReactDOM.findDOMNode(this.refs.title).focus()
|
||||
}
|
||||
}
|
||||
@@ -106,6 +123,7 @@ export default class ArticleDetail extends React.Component {
|
||||
|
||||
let isArticleChanged = nextProps.activeArticle != null && (nextProps.activeArticle.key !== this.state.article.key)
|
||||
let isModeChanged = nextProps.status.mode !== this.props.status.mode
|
||||
|
||||
// Reset article input
|
||||
if (isArticleChanged || (isModeChanged && nextProps.status.mode !== IDLE_MODE)) {
|
||||
Object.assign(nextState, {
|
||||
@@ -117,7 +135,11 @@ export default class ArticleDetail extends React.Component {
|
||||
if (isModeChanged) {
|
||||
Object.assign(nextState, {
|
||||
openDeleteConfirmMenu: false,
|
||||
previewMode: false
|
||||
previewMode: false,
|
||||
isArticleEdited: false,
|
||||
isTagChanged: false,
|
||||
isTitleChanged: false,
|
||||
isContentChanged: false
|
||||
})
|
||||
}
|
||||
|
||||
@@ -224,18 +246,23 @@ export default class ArticleDetail extends React.Component {
|
||||
|
||||
handleCancelButtonClick (e) {
|
||||
let { activeArticle, dispatch } = this.props
|
||||
if (activeArticle.status === NEW) dispatch(switchArticle(null))
|
||||
|
||||
if (activeArticle.status === NEW) {
|
||||
dispatch(switchArticle(null))
|
||||
}
|
||||
dispatch(switchMode(IDLE_MODE))
|
||||
}
|
||||
|
||||
handleSaveButtonClick (e) {
|
||||
let { dispatch, folders, filters } = this.props
|
||||
let { dispatch, folders, status } = this.props
|
||||
let article = this.state.article
|
||||
let newArticle = Object.assign({}, article)
|
||||
|
||||
let folder = _.findWhere(folders, {key: article.FolderKey})
|
||||
if (folder == null) return false
|
||||
|
||||
dispatch(unlockStatus())
|
||||
|
||||
delete newArticle.status
|
||||
newArticle.updatedAt = new Date()
|
||||
if (newArticle.createdAt == null) {
|
||||
@@ -251,7 +278,7 @@ export default class ArticleDetail extends React.Component {
|
||||
// Searchを初期化し、更新先のFolder filterをかける
|
||||
// かかれていない時に
|
||||
// Searchを初期化する
|
||||
if (filters.folder.length !== 0) dispatch(switchFolder(folder.name))
|
||||
if (status.targetFolders.length > 0) dispatch(switchFolder(folder.name))
|
||||
else dispatch(clearSearch())
|
||||
dispatch(switchArticle(newArticle.key))
|
||||
}
|
||||
@@ -263,19 +290,83 @@ export default class ArticleDetail extends React.Component {
|
||||
this.setState({article: article})
|
||||
}
|
||||
|
||||
handleTitleChange (e) {
|
||||
let { article } = this.state
|
||||
article.title = e.target.value
|
||||
let _isTitleChanged = article.title !== this.props.activeArticle.title
|
||||
|
||||
let { isTagChanged, isContentChanged, isArticleEdited, isModeChanged } = this.state
|
||||
let _isArticleEdited = _isTitleChanged || isTagChanged || isContentChanged || isModeChanged
|
||||
|
||||
this.setState({
|
||||
article,
|
||||
isTitleChanged: _isTitleChanged,
|
||||
isArticleEdited: _isArticleEdited
|
||||
}, () => {
|
||||
if (isArticleEdited !== _isArticleEdited) {
|
||||
let { dispatch } = this.props
|
||||
if (_isArticleEdited) {
|
||||
console.log('lockit')
|
||||
dispatch(lockStatus())
|
||||
} else {
|
||||
console.log('unlockit')
|
||||
dispatch(unlockStatus())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
handleTagsChange (newTag, tags) {
|
||||
let article = this.state.article
|
||||
article.tags = tags
|
||||
|
||||
this.setState({article: article})
|
||||
let _isTagChanged = _.difference(article.tags, this.props.activeArticle.tags).length > 0 || _.difference(this.props.activeArticle.tags, article.tags).length > 0
|
||||
|
||||
let { isTitleChanged, isContentChanged, isArticleEdited, isModeChanged } = this.state
|
||||
let _isArticleEdited = _isTagChanged || isTitleChanged || isContentChanged || isModeChanged
|
||||
|
||||
this.setState({
|
||||
article,
|
||||
isTagChanged: _isTagChanged,
|
||||
isArticleEdited: _isArticleEdited
|
||||
}, () => {
|
||||
if (isArticleEdited !== _isArticleEdited) {
|
||||
let { dispatch } = this.props
|
||||
if (_isArticleEdited) {
|
||||
console.log('lockit')
|
||||
dispatch(lockStatus())
|
||||
} else {
|
||||
console.log('unlockit')
|
||||
dispatch(unlockStatus())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
handleModeChange (value) {
|
||||
let article = this.state.article
|
||||
let { article } = this.state
|
||||
article.mode = value
|
||||
let _isModeChanged = article.mode !== this.props.activeArticle.mode
|
||||
|
||||
let { isTagChanged, isContentChanged, isArticleEdited, isTitleChanged } = this.state
|
||||
let _isArticleEdited = _isModeChanged || isTagChanged || isContentChanged || isTitleChanged
|
||||
|
||||
this.setState({
|
||||
article: article,
|
||||
previewMode: false
|
||||
article,
|
||||
previewMode: false,
|
||||
isModeChanged: _isModeChanged,
|
||||
isArticleEdited: _isArticleEdited
|
||||
}, () => {
|
||||
if (isArticleEdited !== _isArticleEdited) {
|
||||
let { dispatch } = this.props
|
||||
if (_isArticleEdited) {
|
||||
console.log('lockit')
|
||||
dispatch(lockStatus())
|
||||
} else {
|
||||
console.log('unlockit')
|
||||
dispatch(unlockStatus())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -286,9 +377,34 @@ export default class ArticleDetail extends React.Component {
|
||||
}
|
||||
|
||||
handleContentChange (e, value) {
|
||||
let article = this.state.article
|
||||
let { status } = this.props
|
||||
if (status.mode === IDLE_MODE) {
|
||||
return
|
||||
}
|
||||
|
||||
let { article } = this.state
|
||||
article.content = value
|
||||
this.setState({article: article})
|
||||
let _isContentChanged = article.content !== this.props.activeArticle.content
|
||||
|
||||
let { isTagChanged, isModeChanged, isArticleEdited, isTitleChanged } = this.state
|
||||
let _isArticleEdited = _isContentChanged || isTagChanged || isModeChanged || isTitleChanged
|
||||
|
||||
this.setState({
|
||||
article,
|
||||
isContentChanged: _isContentChanged,
|
||||
isArticleEdited: _isArticleEdited
|
||||
}, () => {
|
||||
if (isArticleEdited !== _isArticleEdited) {
|
||||
let { dispatch } = this.props
|
||||
if (_isArticleEdited) {
|
||||
console.log('lockit')
|
||||
dispatch(lockStatus())
|
||||
} else {
|
||||
console.log('unlockit')
|
||||
dispatch(unlockStatus())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
handleTogglePreviewButtonClick (e) {
|
||||
@@ -303,7 +419,7 @@ export default class ArticleDetail extends React.Component {
|
||||
}
|
||||
|
||||
renderEdit () {
|
||||
let { folders, status } = this.props
|
||||
let { folders, status, tags } = this.props
|
||||
|
||||
let folderOptions = folders.map(folder => {
|
||||
return (
|
||||
@@ -322,10 +438,12 @@ export default class ArticleDetail extends React.Component {
|
||||
>
|
||||
{folderOptions}
|
||||
</select>
|
||||
{this.state.isArticleEdited ? ' (edited)' : ''}
|
||||
|
||||
<TagSelect
|
||||
tags={this.state.article.tags}
|
||||
onChange={(tags, tag) => this.handleTagsChange(tags, tag)}
|
||||
suggestTags={tags}
|
||||
/>
|
||||
|
||||
{status.isTutorialOpen ? tagSelectTutorialElement : null}
|
||||
@@ -346,7 +464,7 @@ export default class ArticleDetail extends React.Component {
|
||||
<div className='detailPanel'>
|
||||
<div className='header'>
|
||||
<div className='title'>
|
||||
<input onKeyDown={e => this.handleTitleKeyDown(e)} placeholder='Title' ref='title' valueLink={this.linkState('article.title')}/>
|
||||
<input onKeyDown={e => this.handleTitleKeyDown(e)} placeholder='Title' ref='title' value={this.state.article.title} onChange={e => this.handleTitleChange(e)}/>
|
||||
</div>
|
||||
<ModeSelect
|
||||
ref='mode'
|
||||
@@ -381,7 +499,6 @@ export default class ArticleDetail extends React.Component {
|
||||
if (activeArticle == null) return this.renderEmpty()
|
||||
|
||||
switch (status.mode) {
|
||||
case CREATE_MODE:
|
||||
case EDIT_MODE:
|
||||
return this.renderEdit()
|
||||
case IDLE_MODE:
|
||||
@@ -395,6 +512,7 @@ export default class ArticleDetail extends React.Component {
|
||||
ArticleDetail.propTypes = {
|
||||
status: PropTypes.shape(),
|
||||
activeArticle: PropTypes.shape(),
|
||||
activeUser: PropTypes.shape()
|
||||
activeUser: PropTypes.shape(),
|
||||
dispatch: PropTypes.func
|
||||
}
|
||||
ArticleDetail.prototype.linkState = linkState
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import { findWhere } from 'lodash'
|
||||
import { setSearchFilter, switchFolder, switchMode, CREATE_MODE } from 'boost/actions'
|
||||
import { setSearchFilter, switchFolder, switchMode, switchArticle, updateArticle, EDIT_MODE } from 'boost/actions'
|
||||
import { openModal } from 'boost/modal'
|
||||
import FolderMark from 'boost/components/FolderMark'
|
||||
import Preferences from 'boost/components/modal/Preferences'
|
||||
import CreateNewFolder from 'boost/components/modal/CreateNewFolder'
|
||||
import keygen from 'boost/keygen'
|
||||
|
||||
import remote from 'remote'
|
||||
const electron = require('electron')
|
||||
const remote = electron.remote
|
||||
let userName = remote.getGlobal('process').env.USER
|
||||
|
||||
const BRAND_COLOR = '#18AF90'
|
||||
@@ -65,9 +67,27 @@ export default class ArticleNavigator extends React.Component {
|
||||
}
|
||||
|
||||
handleNewPostButtonClick (e) {
|
||||
let { dispatch } = this.props
|
||||
let { dispatch, folders, status } = this.props
|
||||
let { targetFolders } = status
|
||||
|
||||
dispatch(switchMode(CREATE_MODE))
|
||||
let FolderKey = targetFolders.length > 0
|
||||
? targetFolders[0].key
|
||||
: folders[0].key
|
||||
|
||||
let newArticle = {
|
||||
id: null,
|
||||
key: keygen(),
|
||||
title: '',
|
||||
content: '',
|
||||
mode: 'markdown',
|
||||
tags: [],
|
||||
FolderKey: FolderKey,
|
||||
status: 'NEW'
|
||||
}
|
||||
|
||||
dispatch(updateArticle(newArticle))
|
||||
dispatch(switchArticle(newArticle.key, true))
|
||||
dispatch(switchMode(EDIT_MODE))
|
||||
}
|
||||
|
||||
handleNewFolderButton (e) {
|
||||
@@ -88,16 +108,17 @@ export default class ArticleNavigator extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { status, folders } = this.props
|
||||
let { status, folders, allArticles } = this.props
|
||||
let { targetFolders } = status
|
||||
if (targetFolders == null) targetFolders = []
|
||||
|
||||
let folderElememts = folders.map((folder, index) => {
|
||||
let isActive = findWhere(targetFolders, {key: folder.key})
|
||||
let articleCount = allArticles.filter(article => article.FolderKey === folder.key && article.status !== 'NEW').length
|
||||
|
||||
return (
|
||||
<button onClick={e => this.handleFolderButtonClick(folder.name)(e)} key={'folder-' + folder.key} className={isActive ? 'active' : ''}>
|
||||
<FolderMark color={folder.color}/> {folder.name}
|
||||
<FolderMark color={folder.color}/> {folder.name} <span className='articleCount'>{articleCount}</span>
|
||||
</button>
|
||||
)
|
||||
})
|
||||
@@ -150,6 +171,7 @@ export default class ArticleNavigator extends React.Component {
|
||||
ArticleNavigator.propTypes = {
|
||||
activeUser: PropTypes.object,
|
||||
folders: PropTypes.array,
|
||||
allArticles: PropTypes.array,
|
||||
status: PropTypes.shape({
|
||||
folderId: PropTypes.number
|
||||
}),
|
||||
|
||||
@@ -10,7 +10,9 @@ const searchTutorialElement = (
|
||||
<text x='450' y='33' fill={BRAND_COLOR} fontSize='24'>Search some posts!!</text>
|
||||
<text x='450' y='60' fill={BRAND_COLOR} fontSize='18'>{'- Search by tag : #{string}'}</text>
|
||||
<text x='450' y='85' fill={BRAND_COLOR} fontSize='18'>
|
||||
{'- Search by folder : in:{folder_name}\n'}</text>
|
||||
{'- Search by folder : /{folder_name}\n'}</text>
|
||||
<text x='465' y='105' fill={BRAND_COLOR} fontSize='14'>
|
||||
{'exact match : //{folder_name}'}</text>
|
||||
|
||||
<svg width='500' height='300'>
|
||||
<path fill='white' d='M54.5,51.5c-12.4,3.3-27.3-1.4-38.4-7C11.2,42,5,38.1,5.6,31.8c0.7-6.9,8.1-11.2,13.8-13.7
|
||||
@@ -117,7 +119,7 @@ export default class ArticleTopBar extends React.Component {
|
||||
}
|
||||
<div className={'tooltip' + (this.state.isTooltipHidden ? ' hide' : '')}>
|
||||
- Search by tag : #{'{string}'}<br/>
|
||||
- Search by folder : in:{'{folder_name}'}
|
||||
- Search by folder : /{'{folder_name}'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import ipc from 'ipc'
|
||||
const electron = require('electron')
|
||||
const ipc = electron.ipcRenderer
|
||||
import React, { PropTypes } from 'react'
|
||||
|
||||
var ContactModal = require('boost/components/modal/ContactModal')
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
<link rel="stylesheet" href="../../node_modules/font-awesome/css/font-awesome.min.css" media="screen" charset="utf-8">
|
||||
<link rel="stylesheet" href="../../node_modules/devicon/devicon.min.css">
|
||||
<link rel="stylesheet" href="../../node_modules/highlight.js/styles/xcode.css">
|
||||
<link rel="shortcut icon" href="favicon.ico">
|
||||
|
||||
<style>
|
||||
@@ -53,8 +54,9 @@
|
||||
|
||||
<script src="../../submodules/ace/src-min/ace.js"></script>
|
||||
<script type='text/javascript'>
|
||||
require('web-frame').setZoomLevelLimits(1, 1)
|
||||
var version = require('remote').require('app').getVersion()
|
||||
const electron = require('electron')
|
||||
electron.webFrame.setZoomLevelLimits(1, 1)
|
||||
var version = electron.remote.app.getVersion()
|
||||
document.title = 'Boost' + ((version == null || version.length === 0) ? ' DEV' : '')
|
||||
var scriptUrl = process.env.BOOST_ENV === 'development'
|
||||
? 'http://localhost:8080/assets/main.js'
|
||||
|
||||
@@ -11,8 +11,23 @@ require('../styles/main/index.styl')
|
||||
import { openModal } from 'boost/modal'
|
||||
import Tutorial from 'boost/components/modal/Tutorial'
|
||||
import activityRecord from 'boost/activityRecord'
|
||||
const electron = require('electron')
|
||||
const ipc = electron.ipcRenderer
|
||||
|
||||
activityRecord.init()
|
||||
window.addEventListener('online', function () {
|
||||
ipc.send('check-update', 'check-update')
|
||||
})
|
||||
|
||||
function notify (...args) {
|
||||
return new window.Notification(...args)
|
||||
}
|
||||
|
||||
ipc.on('notify', function (e, payload) {
|
||||
notify(payload.title, {
|
||||
body: payload.body
|
||||
})
|
||||
})
|
||||
|
||||
let routes = (
|
||||
<Route path='/' component={MainPage}>
|
||||
|
||||
@@ -79,6 +79,31 @@ body
|
||||
white-space nowrap
|
||||
text-overflow ellipsis
|
||||
overflow-x hidden
|
||||
clearfix()
|
||||
.left
|
||||
float left
|
||||
.right
|
||||
float right
|
||||
button
|
||||
border-radius 16.5px
|
||||
cursor pointer
|
||||
height 33px
|
||||
width 33px
|
||||
border none
|
||||
margin-right 5px
|
||||
font-size 18px
|
||||
color inactiveTextColor
|
||||
background-color transparent
|
||||
padding 0
|
||||
.tooltip
|
||||
tooltip()
|
||||
&.clipboardBtn .tooltip
|
||||
margin-left -160px
|
||||
margin-top 25px
|
||||
&:hover
|
||||
color textColor
|
||||
.tooltip
|
||||
opacity 1
|
||||
.content
|
||||
position absolute
|
||||
top 55px
|
||||
|
||||
@@ -98,9 +98,11 @@ iptFocusBorderColor = #369DCD
|
||||
&:hover
|
||||
background-color white
|
||||
.TagSelect
|
||||
.tags
|
||||
white-space nowrap
|
||||
overflow-x auto
|
||||
position relative
|
||||
max-width 350px
|
||||
margin-top 5px
|
||||
noSelect()
|
||||
z-index 30
|
||||
@@ -136,6 +138,26 @@ iptFocusBorderColor = #369DCD
|
||||
border none
|
||||
transition 0.1s
|
||||
height 18px
|
||||
.suggestTags
|
||||
position fixed
|
||||
width 150px
|
||||
max-height 150px
|
||||
background-color white
|
||||
z-index 5
|
||||
border 1px solid borderColor
|
||||
border-radius 5px
|
||||
button
|
||||
width 100%
|
||||
display block
|
||||
padding 0 15px
|
||||
height 33px
|
||||
line-height 33px
|
||||
background-color transparent
|
||||
border none
|
||||
text-align left
|
||||
font-size 14px
|
||||
&:hover
|
||||
background-color darken(white, 10%)
|
||||
.right
|
||||
button
|
||||
cursor pointer
|
||||
@@ -222,9 +244,6 @@ iptFocusBorderColor = #369DCD
|
||||
display inline-block
|
||||
&:hover
|
||||
background-color darken(white, 10%)
|
||||
|
||||
|
||||
|
||||
.title
|
||||
absolute left top bottom
|
||||
right 150px
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
articleNavBgColor = #353535
|
||||
articleCount = #999
|
||||
|
||||
.ArticleNavigator
|
||||
background-color articleNavBgColor
|
||||
@@ -149,7 +150,10 @@ articleNavBgColor = #353535
|
||||
&:hover
|
||||
background-color transparentify(white, 5%)
|
||||
&.active, &:active
|
||||
background-color brandColor
|
||||
background-color transparentify(lighten(brandColor, 25%), 70%)
|
||||
.articleCount
|
||||
color articleCount
|
||||
font-size 12px
|
||||
.members
|
||||
.memberList>div
|
||||
height 33px
|
||||
|
||||
@@ -9,3 +9,4 @@
|
||||
@require './lib/CreateNewFolder'
|
||||
@require './lib/Preferences'
|
||||
@require './lib/Tutorial'
|
||||
@require './lib/EditedAlert'
|
||||
|
||||
@@ -34,9 +34,30 @@ iptFocusBorderColor = #369DCD
|
||||
border-radius 5px
|
||||
border solid 1px borderColor
|
||||
outline none
|
||||
margin 100px auto 25px
|
||||
margin 75px auto 20px
|
||||
&:focus
|
||||
border-color iptFocusBorderColor
|
||||
.colorSelect
|
||||
text-align center
|
||||
.option
|
||||
cursor pointer
|
||||
font-size 22px
|
||||
height 48px
|
||||
width 48px
|
||||
margin 0 2px
|
||||
border 1px solid transparent
|
||||
border-radius 5px
|
||||
overflow hidden
|
||||
line-height 45px
|
||||
text-align center
|
||||
transition 0.1s
|
||||
display inline-block
|
||||
&:hover
|
||||
border-color borderColor
|
||||
font-size 28px
|
||||
&.active
|
||||
font-size 28px
|
||||
border-color iptFocusBorderColor
|
||||
.alert
|
||||
color infoTextColor
|
||||
background-color infoBackgroundColor
|
||||
@@ -44,7 +65,7 @@ iptFocusBorderColor = #369DCD
|
||||
padding 15px 15px
|
||||
width 330px
|
||||
border-radius 5px
|
||||
margin 0 auto
|
||||
margin 15px auto 0
|
||||
&.error
|
||||
color errorTextColor
|
||||
background-color errorBackgroundColor
|
||||
|
||||
28
browser/styles/main/HomeContainer/lib/EditedAlert.styl
Normal file
28
browser/styles/main/HomeContainer/lib/EditedAlert.styl
Normal file
@@ -0,0 +1,28 @@
|
||||
.EditedAlert.modal
|
||||
width 350px
|
||||
top 100px
|
||||
.title
|
||||
font-size 24px
|
||||
margin-bottom 15px
|
||||
.message
|
||||
font-size 14px
|
||||
margin-bottom 15px
|
||||
.control
|
||||
text-align right
|
||||
button
|
||||
border-radius 5px
|
||||
height 33px
|
||||
padding 0 15px
|
||||
font-size 14px
|
||||
background-color white
|
||||
border 1px solid borderColor
|
||||
border-radius 5px
|
||||
margin-left 5px
|
||||
&:hover
|
||||
background-color darken(white, 10%)
|
||||
&.primary
|
||||
border-color brandColor
|
||||
background-color brandColor
|
||||
color white
|
||||
&:hover
|
||||
background-color lighten(brandColor, 10%)
|
||||
@@ -440,19 +440,22 @@ iptFocusBorderColor = #369DCD
|
||||
padding 5px 0
|
||||
&:last-child
|
||||
border-color transparent
|
||||
.folderColor
|
||||
float left
|
||||
margin-left 10px
|
||||
text-align center
|
||||
width 44px
|
||||
.folderName
|
||||
float left
|
||||
width 175px
|
||||
overflow ellipsis
|
||||
padding-left 15px
|
||||
.folderPublic
|
||||
float left
|
||||
text-align center
|
||||
width 100px
|
||||
.folderControl
|
||||
float right
|
||||
width 145px
|
||||
width 125px
|
||||
text-align center
|
||||
&.folderHeader
|
||||
.folderName
|
||||
padding-left 25px
|
||||
&.newFolder
|
||||
.alert
|
||||
display block
|
||||
@@ -502,6 +505,30 @@ iptFocusBorderColor = #369DCD
|
||||
&:hover
|
||||
color lighten(brandColor, 10%)
|
||||
&.FolderRow
|
||||
.sortBtns
|
||||
float left
|
||||
display block
|
||||
height 30px
|
||||
width 30px
|
||||
margin-top 1.5px
|
||||
position absolute
|
||||
button
|
||||
absolute left
|
||||
background-color transparent
|
||||
border none
|
||||
height 15px
|
||||
padding 0
|
||||
margin 0
|
||||
color stripBtnColor
|
||||
&:first-child
|
||||
top 0
|
||||
&:last-child
|
||||
top 15px
|
||||
&:hover
|
||||
color stripHoverBtnColor
|
||||
&:disabled
|
||||
color lighten(stripBtnColor, 10%)
|
||||
cursor not-allowed
|
||||
.folderName input
|
||||
height 33px
|
||||
border 1px solid borderColor
|
||||
@@ -512,16 +539,52 @@ iptFocusBorderColor = #369DCD
|
||||
width 150px
|
||||
&:focus
|
||||
border-color iptFocusBorderColor
|
||||
.folderPublic select
|
||||
.folderColor
|
||||
.select
|
||||
height 33px
|
||||
width 33px
|
||||
border 1px solid borderColor
|
||||
background-color white
|
||||
outline none
|
||||
display block
|
||||
margin 0 auto
|
||||
font-size 14px
|
||||
border-radius 5px
|
||||
&:focus
|
||||
border-color iptFocusBorderColor
|
||||
.options
|
||||
position absolute
|
||||
background-color white
|
||||
text-align left
|
||||
border 1px solid borderColor
|
||||
border-radius 5px
|
||||
padding 0 5px 5px
|
||||
margin-left 5px
|
||||
margin-top -34px
|
||||
clearfix()
|
||||
.label
|
||||
margin-left 5px
|
||||
line-height 22px
|
||||
font-size 12px
|
||||
button
|
||||
float left
|
||||
border none
|
||||
width 33px
|
||||
height 33px
|
||||
margin-right 5px
|
||||
border 1px solid transparent
|
||||
line-height 29px
|
||||
overflow hidden
|
||||
border-radius 5px
|
||||
background-color transparent
|
||||
outline none
|
||||
transition 0.1s
|
||||
&:hover
|
||||
border-color borderColor
|
||||
&.active
|
||||
border-color iptFocusBorderColor
|
||||
.FolderMark
|
||||
transform scale(1.4)
|
||||
.folderControl
|
||||
button
|
||||
border none
|
||||
|
||||
@@ -108,6 +108,8 @@ slideBgColor4 = #00B493
|
||||
height 140px
|
||||
.slide3
|
||||
background-color slideBgColor3
|
||||
.title
|
||||
margin-bottom 15px
|
||||
.content
|
||||
font-size 18px
|
||||
&>img
|
||||
|
||||
@@ -62,7 +62,7 @@ marked()
|
||||
display list-item
|
||||
line-height 1.8em
|
||||
code
|
||||
font-family monospace
|
||||
font-family Monaco, Menlo, 'Ubuntu Mono', Consolas, source-code-pro, monospace;
|
||||
padding 2px 4px
|
||||
border solid 1px borderColor
|
||||
border-radius 4px
|
||||
@@ -77,6 +77,7 @@ marked()
|
||||
overflow-x auto
|
||||
margin 15px 0 25px
|
||||
background-color #F6F6F6
|
||||
line-height 1.35em
|
||||
&>code
|
||||
padding 0
|
||||
border none
|
||||
|
||||
72
finder.js
Executable file
72
finder.js
Executable file
@@ -0,0 +1,72 @@
|
||||
const electron = require('electron')
|
||||
const app = electron.app
|
||||
const Tray = electron.Tray
|
||||
const Menu = electron.Menu
|
||||
const MenuItem = electron.MenuItem
|
||||
|
||||
process.stdin.setEncoding('utf8')
|
||||
|
||||
console.log = function () {
|
||||
process.stdout.write(JSON.stringify({
|
||||
type: 'log',
|
||||
data: JSON.stringify(Array.prototype.slice.call(arguments).join(' '))
|
||||
}), 'utf-8')
|
||||
}
|
||||
|
||||
function emit (type, data) {
|
||||
process.stdout.write(JSON.stringify({
|
||||
type: type,
|
||||
data: JSON.stringify(data)
|
||||
}), 'utf-8')
|
||||
}
|
||||
|
||||
var finderWindow
|
||||
app.on('ready', function () {
|
||||
app.dock.hide()
|
||||
var appIcon = new Tray(__dirname + '/resources/tray-icon.png')
|
||||
appIcon.setToolTip('Boost')
|
||||
|
||||
finderWindow = require('./atom-lib/finder-window')
|
||||
finderWindow.webContents.on('did-finish-load', function () {
|
||||
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',
|
||||
click: function () {
|
||||
finderWindow.show()
|
||||
}
|
||||
}))
|
||||
trayMenu.append(new MenuItem({
|
||||
label: 'Quit',
|
||||
click: function () {
|
||||
emit('quit-app')
|
||||
}
|
||||
}))
|
||||
appIcon.setContextMenu(trayMenu)
|
||||
|
||||
process.stdin.on('data', function (payload) {
|
||||
try {
|
||||
payload = JSON.parse(payload)
|
||||
} catch (e) {
|
||||
console.log('Not parsable payload : ', payload)
|
||||
return
|
||||
}
|
||||
console.log('from main >> ', payload.type)
|
||||
switch (payload.type) {
|
||||
case 'open-finder':
|
||||
finderWindow.show()
|
||||
break
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
global.hideFinder = function () {
|
||||
Menu.sendActionToFirstResponder('hide:')
|
||||
}
|
||||
})
|
||||
|
||||
10
index.js
Normal file
10
index.js
Normal file
@@ -0,0 +1,10 @@
|
||||
function isFinderCalled () {
|
||||
var argv = process.argv.slice(1)
|
||||
return argv.some(arg => arg.match(/--finder/))
|
||||
}
|
||||
|
||||
if (isFinderCalled()) {
|
||||
require('./finder.js')
|
||||
} else {
|
||||
require('./main.js')
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
// Action types
|
||||
export const CLEAR_NEW_ARTICLE = 'CLEAR_NEW_ARTICLE'
|
||||
export const ARTICLE_UPDATE = 'ARTICLE_UPDATE'
|
||||
export const ARTICLE_DESTROY = 'ARTICLE_DESTROY'
|
||||
export const FOLDER_CREATE = 'FOLDER_CREATE'
|
||||
export const FOLDER_UPDATE = 'FOLDER_UPDATE'
|
||||
export const FOLDER_DESTROY = 'FOLDER_DESTROY'
|
||||
export const FOLDER_REPLACE = 'FOLDER_REPLACE'
|
||||
|
||||
export const SWITCH_FOLDER = 'SWITCH_FOLDER'
|
||||
export const SWITCH_MODE = 'SWITCH_MODE'
|
||||
@@ -11,17 +13,24 @@ export const SWITCH_ARTICLE = 'SWITCH_ARTICLE'
|
||||
export const SET_SEARCH_FILTER = 'SET_SEARCH_FILTER'
|
||||
export const SET_TAG_FILTER = 'SET_TAG_FILTER'
|
||||
export const CLEAR_SEARCH = 'CLEAR_SEARCH'
|
||||
export const LOCK_STATUS = 'LOCK_STATUS'
|
||||
export const UNLOCK_STATUS = 'UNLOCK_STATUS'
|
||||
export const TOGGLE_TUTORIAL = 'TOGGLE_TUTORIAL'
|
||||
|
||||
// Status - mode
|
||||
export const IDLE_MODE = 'IDLE_MODE'
|
||||
export const CREATE_MODE = 'CREATE_MODE'
|
||||
export const EDIT_MODE = 'EDIT_MODE'
|
||||
|
||||
// Article status
|
||||
export const NEW = 'NEW'
|
||||
|
||||
// DB
|
||||
export function clearNewArticle () {
|
||||
return {
|
||||
type: CLEAR_NEW_ARTICLE
|
||||
}
|
||||
}
|
||||
|
||||
export function updateArticle (article) {
|
||||
return {
|
||||
type: ARTICLE_UPDATE,
|
||||
@@ -57,6 +66,16 @@ export function destroyFolder (key) {
|
||||
}
|
||||
}
|
||||
|
||||
export function replaceFolder (a, b) {
|
||||
return {
|
||||
type: FOLDER_REPLACE,
|
||||
data: {
|
||||
a,
|
||||
b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function switchFolder (folderName) {
|
||||
return {
|
||||
type: SWITCH_FOLDER,
|
||||
@@ -71,10 +90,13 @@ export function switchMode (mode) {
|
||||
}
|
||||
}
|
||||
|
||||
export function switchArticle (articleKey) {
|
||||
export function switchArticle (articleKey, isNew) {
|
||||
return {
|
||||
type: SWITCH_ARTICLE,
|
||||
data: articleKey
|
||||
data: {
|
||||
key: articleKey,
|
||||
isNew: isNew
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +120,18 @@ export function clearSearch () {
|
||||
}
|
||||
}
|
||||
|
||||
export function lockStatus () {
|
||||
return {
|
||||
type: LOCK_STATUS
|
||||
}
|
||||
}
|
||||
|
||||
export function unlockStatus () {
|
||||
return {
|
||||
type: UNLOCK_STATUS
|
||||
}
|
||||
}
|
||||
|
||||
export function toggleTutorial () {
|
||||
return {
|
||||
type: TOGGLE_TUTORIAL
|
||||
|
||||
@@ -47,6 +47,10 @@ Post all records(except today)
|
||||
and remove all posted records
|
||||
*/
|
||||
export function postRecords (data) {
|
||||
if (process.env.BOOST_ENV === 'development') {
|
||||
console.log('post failed - on development')
|
||||
return
|
||||
}
|
||||
let records = getAllRecords()
|
||||
records = records.filter(record => {
|
||||
return !isSameDate(new Date(), record.date)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import shell from 'shell'
|
||||
const electron = require('electron')
|
||||
const shell = electron.shell
|
||||
|
||||
export default class ExternalLink extends React.Component {
|
||||
handleClick (e) {
|
||||
|
||||
@@ -3,45 +3,50 @@ import React, { PropTypes } from 'react'
|
||||
const BLUE = '#3460C7'
|
||||
const LIGHTBLUE = '#2BA5F7'
|
||||
const ORANGE = '#FF8E00'
|
||||
const YELLOW = '#EAEF31'
|
||||
const GREEN = '#02FF26'
|
||||
const DARKGREEN = '#008A59'
|
||||
const YELLOW = '#E8D252'
|
||||
const GREEN = '#3FD941'
|
||||
const DARKGREEN = '#1FAD85'
|
||||
const RED = '#E10051'
|
||||
const PURPLE = '#B013A4'
|
||||
const BRAND_COLOR = '#2BAC8F'
|
||||
|
||||
function getColorByIndex (index) {
|
||||
switch (index % 8) {
|
||||
case 0:
|
||||
return LIGHTBLUE
|
||||
return RED
|
||||
case 1:
|
||||
return ORANGE
|
||||
case 2:
|
||||
return RED
|
||||
return YELLOW
|
||||
case 3:
|
||||
return GREEN
|
||||
case 4:
|
||||
return DARKGREEN
|
||||
case 5:
|
||||
return YELLOW
|
||||
return LIGHTBLUE
|
||||
case 6:
|
||||
return BLUE
|
||||
case 7:
|
||||
return PURPLE
|
||||
default:
|
||||
return BRAND_COLOR
|
||||
return DARKGREEN
|
||||
}
|
||||
}
|
||||
|
||||
export default class FolderMark extends React.Component {
|
||||
render () {
|
||||
let color = getColorByIndex(this.props.color)
|
||||
let className = 'FolderMark fa fa-square fa-fw'
|
||||
if (this.props.className != null) {
|
||||
className += ' active'
|
||||
}
|
||||
|
||||
return (
|
||||
<i className='fa fa-square fa-fw' style={{color: color}}/>
|
||||
<i className={className} style={{color: color}}/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
FolderMark.propTypes = {
|
||||
color: PropTypes.number
|
||||
color: PropTypes.number,
|
||||
className: PropTypes.string
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import shell from 'shell'
|
||||
var React = require('react')
|
||||
var { PropTypes } = React
|
||||
import markdown from 'boost/markdown'
|
||||
var ReactDOM = require('react-dom')
|
||||
|
||||
const electron = require('electron')
|
||||
const shell = electron.shell
|
||||
|
||||
function handleAnchorClick (e) {
|
||||
shell.openExternal(e.target.href)
|
||||
e.preventDefault()
|
||||
|
||||
@@ -18,7 +18,7 @@ export default class ModeSelect extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount (e) {
|
||||
componentDidMount () {
|
||||
this.blurHandler = e => {
|
||||
let searchElement = ReactDOM.findDOMNode(this.refs.search)
|
||||
if (this.state.mode === EDIT_MODE && document.activeElement !== searchElement) {
|
||||
@@ -28,7 +28,7 @@ export default class ModeSelect extends React.Component {
|
||||
window.addEventListener('click', this.blurHandler)
|
||||
}
|
||||
|
||||
componentWillUnmount (e) {
|
||||
componentWillUnmount () {
|
||||
window.removeEventListener('click', this.blurHandler)
|
||||
let searchElement = ReactDOM.findDOMNode(this.refs.search)
|
||||
if (searchElement != null && this.searchKeyDownListener != null) {
|
||||
|
||||
@@ -3,23 +3,54 @@ import ReactDOM from 'react-dom'
|
||||
import _ from 'lodash'
|
||||
import linkState from 'boost/linkState'
|
||||
|
||||
function isNotEmptyString (str) {
|
||||
return _.isString(str) && str.length > 0
|
||||
}
|
||||
|
||||
export default class TagSelect extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
input: ''
|
||||
input: '',
|
||||
isInputFocused: false
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyDown (e) {
|
||||
if (e.keyCode !== 13) return false
|
||||
e.preventDefault()
|
||||
componentDidMount () {
|
||||
this.blurInputBlurHandler = e => {
|
||||
if (ReactDOM.findDOMNode(this.refs.tagInput) !== document.activeElement) {
|
||||
this.setState({isInputFocused: false})
|
||||
}
|
||||
}
|
||||
window.addEventListener('click', this.blurInputBlurHandler)
|
||||
}
|
||||
|
||||
componentWillUnmount (e) {
|
||||
window.removeEventListener('click', this.blurInputBlurHandler)
|
||||
}
|
||||
|
||||
// Suggestは必ずInputの下に位置するようにする
|
||||
componentDidUpdate () {
|
||||
if (this.shouldShowSuggest()) {
|
||||
let inputRect = ReactDOM.findDOMNode(this.refs.tagInput).getBoundingClientRect()
|
||||
let suggestElement = ReactDOM.findDOMNode(this.refs.suggestTags)
|
||||
if (suggestElement != null) {
|
||||
suggestElement.style.top = inputRect.top + 20 + 'px'
|
||||
suggestElement.style.left = inputRect.left + 'px'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shouldShowSuggest () {
|
||||
return this.state.isInputFocused && isNotEmptyString(this.state.input)
|
||||
}
|
||||
|
||||
addTag (tag, clearInput = true) {
|
||||
let tags = this.props.tags.slice(0)
|
||||
let newTag = this.state.input.trim()
|
||||
let newTag = tag.trim()
|
||||
|
||||
if (newTag.length === 0) {
|
||||
if (newTag.length === 0 && clearInput) {
|
||||
this.setState({input: ''})
|
||||
return
|
||||
}
|
||||
@@ -30,13 +61,38 @@ export default class TagSelect extends React.Component {
|
||||
if (_.isFunction(this.props.onChange)) {
|
||||
this.props.onChange(newTag, tags)
|
||||
}
|
||||
this.setState({input: ''})
|
||||
if (clearInput) this.setState({input: ''})
|
||||
}
|
||||
|
||||
handleKeyDown (e) {
|
||||
switch (e.keyCode) {
|
||||
case 8:
|
||||
{
|
||||
if (this.state.input.length > 0) break
|
||||
e.preventDefault()
|
||||
|
||||
let tags = this.props.tags.slice(0)
|
||||
tags.pop()
|
||||
|
||||
this.props.onChange(null, tags)
|
||||
}
|
||||
break
|
||||
case 13:
|
||||
{
|
||||
e.preventDefault()
|
||||
this.addTag(this.state.input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleThisClick (e) {
|
||||
ReactDOM.findDOMNode(this.refs.tagInput).focus()
|
||||
}
|
||||
|
||||
handleInputFocus (e) {
|
||||
this.setState({isInputFocused: true})
|
||||
}
|
||||
|
||||
handleItemRemoveButton (tag) {
|
||||
return e => {
|
||||
e.stopPropagation()
|
||||
@@ -50,8 +106,16 @@ export default class TagSelect extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleSuggestClick (tag) {
|
||||
return e => {
|
||||
this.addTag(tag)
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
var tagElements = _.isArray(this.props.tags)
|
||||
let { tags, suggestTags } = this.props
|
||||
|
||||
let tagElements = _.isArray(tags)
|
||||
? this.props.tags.map(tag => (
|
||||
<span key={tag} className='tagItem'>
|
||||
<button onClick={e => this.handleItemRemoveButton(tag)(e)} className='tagRemoveBtn'><i className='fa fa-fw fa-times'/></button>
|
||||
@@ -59,8 +123,18 @@ export default class TagSelect extends React.Component {
|
||||
</span>))
|
||||
: null
|
||||
|
||||
let suggestElements = this.shouldShowSuggest() ? suggestTags
|
||||
.filter(tag => {
|
||||
return tag.match(this.state.input)
|
||||
})
|
||||
.map(tag => {
|
||||
return <button onClick={e => this.handleSuggestClick(tag)(e)} key={tag}>{tag}</button>
|
||||
})
|
||||
: null
|
||||
|
||||
return (
|
||||
<div className='TagSelect' onClick={e => this.handleThisClick(e)}>
|
||||
<div className='tags'>
|
||||
{tagElements}
|
||||
<input
|
||||
type='text'
|
||||
@@ -68,7 +142,18 @@ export default class TagSelect extends React.Component {
|
||||
ref='tagInput'
|
||||
valueLink={this.linkState('input')}
|
||||
placeholder='Click here to add tags'
|
||||
className='tagInput'/>
|
||||
className='tagInput'
|
||||
onFocus={e => this.handleInputFocus(e)}
|
||||
/>
|
||||
</div>
|
||||
{suggestElements != null && suggestElements.length > 0
|
||||
? (
|
||||
<div ref='suggestTags' className='suggestTags'>
|
||||
{suggestElements}
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -76,7 +161,8 @@ export default class TagSelect extends React.Component {
|
||||
|
||||
TagSelect.propTypes = {
|
||||
tags: PropTypes.arrayOf(PropTypes.string),
|
||||
onChange: PropTypes.func
|
||||
onChange: PropTypes.func,
|
||||
suggestTags: PropTypes.array
|
||||
}
|
||||
|
||||
TagSelect.prototype.linkState = linkState
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { PropTypes } from 'react'
|
||||
import linkState from 'boost/linkState'
|
||||
import { createFolder } from 'boost/actions'
|
||||
import store from 'boost/store'
|
||||
import FolderMark from 'boost/components/FolderMark'
|
||||
|
||||
export default class CreateNewFolder extends React.Component {
|
||||
constructor (props) {
|
||||
@@ -9,6 +10,7 @@ export default class CreateNewFolder extends React.Component {
|
||||
|
||||
this.state = {
|
||||
name: '',
|
||||
color: Math.round(Math.random() * 7),
|
||||
alert: null
|
||||
}
|
||||
}
|
||||
@@ -20,9 +22,11 @@ export default class CreateNewFolder extends React.Component {
|
||||
handleConfirmButton (e) {
|
||||
this.setState({alert: null}, () => {
|
||||
let { close } = this.props
|
||||
let name = this.state.name
|
||||
let { name, color } = this.state
|
||||
|
||||
let input = {
|
||||
name
|
||||
name,
|
||||
color
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -38,6 +42,20 @@ export default class CreateNewFolder extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
handleColorClick (colorIndex) {
|
||||
return e => {
|
||||
this.setState({
|
||||
color: colorIndex
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyDown (e) {
|
||||
if (e.keyCode === 13) {
|
||||
this.handleConfirmButton()
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
let alert = this.state.alert
|
||||
let alertElement = alert != null ? (
|
||||
@@ -45,6 +63,20 @@ export default class CreateNewFolder extends React.Component {
|
||||
{alert.message}
|
||||
</p>
|
||||
) : null
|
||||
let colorIndexes = []
|
||||
for (let i = 0; i < 8; i++) {
|
||||
colorIndexes.push(i)
|
||||
}
|
||||
let colorElements = colorIndexes.map(index => {
|
||||
let className = 'option'
|
||||
if (index === this.state.color) className += ' active'
|
||||
|
||||
return (
|
||||
<span className={className} key={index} onClick={e => this.handleColorClick(index)(e)}>
|
||||
<FolderMark color={index}/>
|
||||
</span>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<div className='CreateNewFolder modal'>
|
||||
@@ -52,7 +84,10 @@ export default class CreateNewFolder extends React.Component {
|
||||
|
||||
<div className='title'>Create new folder</div>
|
||||
|
||||
<input className='ipt' type='text' valueLink={this.linkState('name')} placeholder='Enter folder name'/>
|
||||
<input onKeyDown={e => this.handleKeyDown(e)} className='ipt' type='text' valueLink={this.linkState('name')} placeholder='Enter folder name'/>
|
||||
<div className='colorSelect'>
|
||||
{colorElements}
|
||||
</div>
|
||||
{alertElement}
|
||||
|
||||
<button onClick={e => this.handleConfirmButton(e)} className='confirmBtn'>Create</button>
|
||||
|
||||
36
lib/components/modal/EditedAlert.js
Normal file
36
lib/components/modal/EditedAlert.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import store from 'boost/store'
|
||||
import { unlockStatus, clearNewArticle } from 'boost/actions'
|
||||
|
||||
export default class EditedAlert extends React.Component {
|
||||
handleNoButtonClick (e) {
|
||||
this.props.close()
|
||||
}
|
||||
|
||||
handleYesButtonClick (e) {
|
||||
store.dispatch(unlockStatus())
|
||||
store.dispatch(this.props.action)
|
||||
store.dispatch(clearNewArticle())
|
||||
this.props.close()
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className='EditedAlert modal'>
|
||||
<div className='title'>Your article is still editing!</div>
|
||||
|
||||
<div className='message'>Do you really want to leave without finishing?</div>
|
||||
|
||||
<div className='control'>
|
||||
<button onClick={e => this.handleNoButtonClick(e)}><i className='fa fa-fw fa-close'/> No</button>
|
||||
<button onClick={e => this.handleYesButtonClick(e)} className='primary'><i className='fa fa-fw fa-check'/> Yes</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
EditedAlert.propTypes = {
|
||||
action: PropTypes.object,
|
||||
close: PropTypes.func
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import React from 'react'
|
||||
import linkState from 'boost/linkState'
|
||||
import remote from 'remote'
|
||||
import ipc from 'ipc'
|
||||
|
||||
const electron = require('electron')
|
||||
const ipc = electron.ipcRenderer
|
||||
const remote = electron.remote
|
||||
|
||||
export default class AppSettingTab extends React.Component {
|
||||
constructor (props) {
|
||||
@@ -36,12 +38,22 @@ export default class AppSettingTab extends React.Component {
|
||||
ipc.removeListener('APP_SETTING_ERROR', this.handleSettingError)
|
||||
}
|
||||
|
||||
handleSaveButtonClick (e) {
|
||||
submitHotKey () {
|
||||
ipc.send('hotkeyUpdated', {
|
||||
toggleFinder: this.state.toggleFinder
|
||||
})
|
||||
}
|
||||
|
||||
handleSaveButtonClick (e) {
|
||||
this.submitHotKey()
|
||||
}
|
||||
|
||||
handleKeyDown (e) {
|
||||
if (e.keyCode === 13) {
|
||||
this.submitHotKey()
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
let alert = this.state.alert
|
||||
let alertElement = alert != null ? (
|
||||
@@ -56,7 +68,7 @@ export default class AppSettingTab extends React.Component {
|
||||
<div className='sectionTitle'>Hotkey</div>
|
||||
<div className='sectionInput'>
|
||||
<label>Toggle Finder(popup)</label>
|
||||
<input valueLink={this.linkState('toggleFinder')} type='text'/>
|
||||
<input onKeyDown={e => this.handleKeyDown(e)} valueLink={this.linkState('toggleFinder')} type='text'/>
|
||||
</div>
|
||||
<div className='sectionConfirm'>
|
||||
<button onClick={e => this.handleSaveButtonClick(e)}>Save</button>
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { PropTypes } from 'react'
|
||||
import linkState from 'boost/linkState'
|
||||
import FolderMark from 'boost/components/FolderMark'
|
||||
import store from 'boost/store'
|
||||
import { updateFolder, destroyFolder } from 'boost/actions'
|
||||
import { updateFolder, destroyFolder, replaceFolder } from 'boost/actions'
|
||||
|
||||
const IDLE = 'IDLE'
|
||||
const EDIT = 'EDIT'
|
||||
@@ -17,6 +17,20 @@ export default class FolderRow extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleUpClick (e) {
|
||||
let { index } = this.props
|
||||
if (index > 0) {
|
||||
store.dispatch(replaceFolder(index, index - 1))
|
||||
}
|
||||
}
|
||||
|
||||
handleDownClick (e) {
|
||||
let { index, count } = this.props
|
||||
if (index < count - 1) {
|
||||
store.dispatch(replaceFolder(index, index + 1))
|
||||
}
|
||||
}
|
||||
|
||||
handleCancelButtonClick (e) {
|
||||
this.setState({
|
||||
mode: IDLE
|
||||
@@ -26,7 +40,9 @@ export default class FolderRow extends React.Component {
|
||||
handleEditButtonClick (e) {
|
||||
this.setState({
|
||||
mode: EDIT,
|
||||
name: this.props.folder.name
|
||||
name: this.props.folder.name,
|
||||
color: this.props.folder.color,
|
||||
isColorEditing: false
|
||||
})
|
||||
}
|
||||
|
||||
@@ -34,12 +50,34 @@ export default class FolderRow extends React.Component {
|
||||
this.setState({mode: DELETE})
|
||||
}
|
||||
|
||||
handleNameInputKeyDown (e) {
|
||||
if (e.keyCode === 13) {
|
||||
this.handleSaveButtonClick()
|
||||
}
|
||||
}
|
||||
|
||||
handleColorSelectClick (e) {
|
||||
this.setState({
|
||||
isColorEditing: true
|
||||
})
|
||||
}
|
||||
|
||||
handleColorButtonClick (index) {
|
||||
return e => {
|
||||
this.setState({
|
||||
color: index,
|
||||
isColorEditing: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleSaveButtonClick (e) {
|
||||
let { folder, setAlert } = this.props
|
||||
|
||||
setAlert(null, () => {
|
||||
let input = {
|
||||
name: this.state.name
|
||||
name: this.state.name,
|
||||
color: this.state.color
|
||||
}
|
||||
folder = Object.assign({}, folder, input)
|
||||
|
||||
@@ -68,10 +106,40 @@ export default class FolderRow extends React.Component {
|
||||
|
||||
switch (this.state.mode) {
|
||||
case EDIT:
|
||||
let colorIndexes = []
|
||||
for (let i = 0; i < 8; i++) {
|
||||
colorIndexes.push(i)
|
||||
}
|
||||
|
||||
let colorOptions = colorIndexes.map(index => {
|
||||
let className = this.state.color === index
|
||||
? 'active'
|
||||
: null
|
||||
return (
|
||||
<button onClick={e => this.handleColorButtonClick(index)(e)} className={className} key={index}>
|
||||
<FolderMark color={index}/>
|
||||
</button>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<div className='FolderRow edit'>
|
||||
<div className='folderColor'>
|
||||
<button onClick={e => this.handleColorSelectClick(e)} className='select'>
|
||||
<FolderMark color={this.state.color}/>
|
||||
</button>
|
||||
{this.state.isColorEditing
|
||||
? (
|
||||
<div className='options'>
|
||||
<div className='label'>Color select</div>
|
||||
{colorOptions}
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
<div className='folderName'>
|
||||
<input valueLink={this.linkState('name')} type='text'/>
|
||||
<input onKeyDown={e => this.handleNameInputKeyDown(e)} valueLink={this.linkState('name')} type='text'/>
|
||||
</div>
|
||||
<div className='folderControl'>
|
||||
<button onClick={e => this.handleSaveButtonClick(e)} className='primary'>Save</button>
|
||||
@@ -93,7 +161,12 @@ export default class FolderRow extends React.Component {
|
||||
default:
|
||||
return (
|
||||
<div className='FolderRow'>
|
||||
<div className='folderName'><FolderMark color={folder.color}/> {folder.name}</div>
|
||||
<div className='sortBtns'>
|
||||
<button onClick={e => this.handleUpClick(e)}><i className='fa fa-sort-up fa-fw'/></button>
|
||||
<button onClick={e => this.handleDownClick(e)}><i className='fa fa-sort-down fa-fw'/></button>
|
||||
</div>
|
||||
<div className='folderColor'><FolderMark color={folder.color}/></div>
|
||||
<div className='folderName'>{folder.name}</div>
|
||||
<div className='folderControl'>
|
||||
<button onClick={e => this.handleEditButtonClick(e)}><i className='fa fa-fw fa-edit'/></button>
|
||||
<button onClick={e => this.handleDeleteButtonClick(e)}><i className='fa fa-fw fa-close'/></button>
|
||||
@@ -106,6 +179,8 @@ export default class FolderRow extends React.Component {
|
||||
|
||||
FolderRow.propTypes = {
|
||||
folder: PropTypes.shape(),
|
||||
index: PropTypes.number,
|
||||
count: PropTypes.number,
|
||||
setAlert: PropTypes.func
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,12 @@ export default class FolderSettingTab extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleNewFolderNameKeyDown (e) {
|
||||
if (e.keyCode === 13) {
|
||||
this.handleSaveButtonClick()
|
||||
}
|
||||
}
|
||||
|
||||
handleSaveButtonClick (e) {
|
||||
this.setState({alert: null}, () => {
|
||||
if (this.state.name.trim().length === 0) return false
|
||||
@@ -40,9 +46,15 @@ export default class FolderSettingTab extends React.Component {
|
||||
|
||||
render () {
|
||||
let { folders } = this.props
|
||||
let folderElements = folders.map(folder => {
|
||||
let folderElements = folders.map((folder, index) => {
|
||||
return (
|
||||
<FolderRow key={'folder-' + folder.key} folder={folder} setAlert={(alert, cb) => this.setAlert(alert, cb)}/>
|
||||
<FolderRow
|
||||
key={'folder-' + folder.key}
|
||||
folder={folder}
|
||||
index={index}
|
||||
count={folders.length}
|
||||
setAlert={(alert, cb) => this.setAlert(alert, cb)}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -59,13 +71,13 @@ export default class FolderSettingTab extends React.Component {
|
||||
<div className='sectionTitle'>Manage folder</div>
|
||||
<div className='folderTable'>
|
||||
<div className='folderHeader'>
|
||||
<div className='folderName'>Folder name</div>
|
||||
<div className='folderName'>Folder</div>
|
||||
<div className='folderControl'>Edit/Delete</div>
|
||||
</div>
|
||||
{folderElements}
|
||||
<div className='newFolder'>
|
||||
<div className='folderName'>
|
||||
<input valueLink={this.linkState('name')} type='text' placeholder='New Folder'/>
|
||||
<input onKeyDown={e => this.handleNewFolderNameKeyDown(e)} valueLink={this.linkState('name')} type='text' placeholder='New Folder'/>
|
||||
</div>
|
||||
<div className='folderControl'>
|
||||
<button onClick={e => this.handleSaveButtonClick(e)} className='primary'>Add</button>
|
||||
|
||||
@@ -83,158 +83,6 @@ class Preferences extends React.Component {
|
||||
return (<AppSettingTab/>)
|
||||
}
|
||||
}
|
||||
|
||||
// handleProfileSaveButtonClick (e) {
|
||||
// let profileState = this.state.profile
|
||||
// profileState.userInfo.alert = {
|
||||
// type: 'info',
|
||||
// message: 'Sending...'
|
||||
// }
|
||||
// this.setState({profile: profileState}, () => {
|
||||
// let input = {
|
||||
// profileName: profileState.userInfo.profileName,
|
||||
// email: profileState.userInfo.email
|
||||
// }
|
||||
// api.updateUserInfo(input)
|
||||
// .then(res => {
|
||||
// let profileState = this.state.profile
|
||||
// profileState.userInfo.alert = {
|
||||
// type: 'success',
|
||||
// message: 'Successfully done!'
|
||||
// }
|
||||
// this.setState({profile: profileState})
|
||||
// })
|
||||
// .catch(err => {
|
||||
// var message
|
||||
// if (err.status != null) {
|
||||
// message = err.response.body.message
|
||||
// } else if (err.code === 'ECONNREFUSED') {
|
||||
// message = 'Can\'t connect to API server.'
|
||||
// } else throw err
|
||||
|
||||
// let profileState = this.state.profile
|
||||
// profileState.userInfo.alert = {
|
||||
// type: 'error',
|
||||
// message: message
|
||||
// }
|
||||
|
||||
// this.setState({profile: profileState})
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
|
||||
// handlePasswordSaveButton (e) {
|
||||
// let profileState = this.state.profile
|
||||
|
||||
// if (profileState.password.newPassword !== profileState.password.confirmation) {
|
||||
// profileState.password.alert = {
|
||||
// type: 'error',
|
||||
// message: 'Confirmation doesn\'t match'
|
||||
// }
|
||||
// this.setState({profile: profileState})
|
||||
// return
|
||||
// }
|
||||
|
||||
// profileState.password.alert = {
|
||||
// type: 'info',
|
||||
// message: 'Sending...'
|
||||
// }
|
||||
|
||||
// this.setState({profile: profileState}, () => {
|
||||
// let input = {
|
||||
// password: profileState.password.currentPassword,
|
||||
// newPassword: profileState.password.newPassword
|
||||
// }
|
||||
// api.updatePassword(input)
|
||||
// .then(res => {
|
||||
// let profileState = this.state.profile
|
||||
// profileState.password.alert = {
|
||||
// type: 'success',
|
||||
// message: 'Successfully done!'
|
||||
// }
|
||||
// profileState.password.currentPassword = ''
|
||||
// profileState.password.newPassword = ''
|
||||
// profileState.password.confirmation = ''
|
||||
|
||||
// this.setState({profile: profileState})
|
||||
// })
|
||||
// .catch(err => {
|
||||
// var message
|
||||
// if (err.status != null) {
|
||||
// message = err.response.body.message
|
||||
// } else if (err.code === 'ECONNREFUSED') {
|
||||
// message = 'Can\'t connect to API server.'
|
||||
// } else throw err
|
||||
|
||||
// let profileState = this.state.profile
|
||||
// profileState.password.alert = {
|
||||
// type: 'error',
|
||||
// message: message
|
||||
// }
|
||||
// profileState.password.currentPassword = ''
|
||||
// profileState.password.newPassword = ''
|
||||
// profileState.password.confirmation = ''
|
||||
|
||||
// this.setState({profile: profileState}, () => {
|
||||
// if (this.refs.currentPassword != null) findDOMNode(this.refs.currentPassword).focus()
|
||||
// })
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
|
||||
// renderProfile () {
|
||||
// let profileState = this.state.profile
|
||||
// return (
|
||||
// <div className='content profile'>
|
||||
// <div className='section userSection'>
|
||||
// <div className='sectionTitle'>User Info</div>
|
||||
// <div className='sectionInput'>
|
||||
// <label>Profile Name</label>
|
||||
// <input valueLink={this.linkState('profile.userInfo.profileName')} type='text'/>
|
||||
// </div>
|
||||
// <div className='sectionInput'>
|
||||
// <label>E-mail</label>
|
||||
// <input valueLink={this.linkState('profile.userInfo.email')} type='text'/>
|
||||
// </div>
|
||||
// <div className='sectionConfirm'>
|
||||
// <button onClick={e => this.handleProfileSaveButtonClick(e)}>Save</button>
|
||||
|
||||
// {this.state.profile.userInfo.alert != null
|
||||
// ? (
|
||||
// <div className={'alert ' + profileState.userInfo.alert.type}>{profileState.userInfo.alert.message}</div>
|
||||
// )
|
||||
// : null}
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// <div className='section passwordSection'>
|
||||
// <div className='sectionTitle'>Password</div>
|
||||
// <div className='sectionInput'>
|
||||
// <label>Current Password</label>
|
||||
// <input ref='currentPassword' valueLink={this.linkState('profile.password.currentPassword')} type='password' placeholder='Current Password'/>
|
||||
// </div>
|
||||
// <div className='sectionInput'>
|
||||
// <label>New Password</label>
|
||||
// <input valueLink={this.linkState('profile.password.newPassword')} type='password' placeholder='New Password'/>
|
||||
// </div>
|
||||
// <div className='sectionInput'>
|
||||
// <label>Confirmation</label>
|
||||
// <input valueLink={this.linkState('profile.password.confirmation')} type='password' placeholder='Confirmation'/>
|
||||
// </div>
|
||||
// <div className='sectionConfirm'>
|
||||
// <button onClick={e => this.handlePasswordSaveButton(e)}>Save</button>
|
||||
|
||||
// {profileState.password.alert != null
|
||||
// ? (
|
||||
// <div className={'alert ' + profileState.password.alert.type}>{profileState.password.alert.message}</div>
|
||||
// )
|
||||
// : null}
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// )
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
Preferences.propTypes = {
|
||||
|
||||
@@ -88,10 +88,11 @@ export default class Tutorial extends React.Component {
|
||||
return (<div className='slide slide3'>
|
||||
<div className='title'>Easy to access with Finder</div>
|
||||
<div className='content'>
|
||||
With Finder, You can search your articles faster.<br/>
|
||||
You can open Finder by pressing Control + shift + tab<br/>
|
||||
To put the content of an article in the clipboard, press Enter.<br/>
|
||||
So you can paste it with Cmd(⌘) + V
|
||||
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/>
|
||||
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 [Command-V]
|
||||
|
||||
<img width='480' src='../../resources/finder.png'/>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
import keygen from 'boost/keygen'
|
||||
const electron = require('electron')
|
||||
const remote = electron.remote
|
||||
const jetpack = require('fs-jetpack')
|
||||
const path = require('path')
|
||||
|
||||
let defaultContent = 'Boost is a brand new note App for programmers.\n\n> 下に日本語版があります。\n\n# \u25CEfeature\n\nBoost has some preponderant functions for efficient engineer\'s task.See some part of it.\n\n1. classify information by\u300CFolders\u300D\n2. deal with great variety of syntax\n3. Finder function\n\n\uFF0A\u3000\uFF0A\u3000\uFF0A\u3000\uFF0A\n\n# 1. classify information by \u300CFolders\u300D- access the information you needed easily.\n\n\u300CFolders\u300D which on the left side bar. Press plus button now. flexible way of classification.\n- Create Folder every language or flamework\n- Make Folder for your own casual memos\n\n# 2. Deal with a great variety of syntax \u2013 instead of your brain\nSave handy all information related with programming\n- Use markdown and gather api specification\n- Well using module and snippet\n\nSave them on Boost, you don\'t need to rewrite or re-search same code again.\n\n# 3. Load Finder function \u2013 now you don\'t need to spell command by hand typing.\n\n**Shift +cmd+tab** press buttons at same time.\nThen, the window will show up for search Boost contents that instant.\n\nUsing cursor key to chose, press enter, cmd+v to paste and\u2026 please check it out by your own eye.\n\n- Such command spl or linux which programmers often use but troublesome to hand type\n\n- (Phrases commonly used for e-mail or customer support)\n\nWe support preponderant efficiency\n\n\uFF0A\u3000\uFF0A\u3000\uFF0A\u3000\uFF0A\n\n## \u25CEfor more information\nFrequently updated with this blog ( http:\/\/blog-jp.b00st.io )\n\nHave wonderful programmer life!\n\n## Hack your memory**\n\n\n\n# 日本語版\n\n**Boost**は全く新しいエンジニアライクのノートアプリです。\n\n# ◎特徴\nBoostはエンジニアの仕事を圧倒的に効率化するいくつかの機能を備えています。\nその一部をご紹介します。\n1. Folderで情報を分類\n2. 豊富なsyantaxに対応\n3. Finder機能\n4. チーム機能(リアルタイム搭載)\n\n* * * *\n\n# 1. Folderで情報を分類、欲しい情報にすぐアクセス。\n左側のバーに存在する「Folders」。\n今すぐプラスボタンを押しましょう。\n分類の仕方も自由自在です。\n- 言語やフレームワークごとにFolderを作成\n- 自分用のカジュアルなメモをまとめる場としてFolderを作成\n\n\n# 2. 豊富なsyntaxに対応、自分の脳の代わりに。\nプログラミングに関する情報を全て、手軽に保存しましょう。\n- mdで、apiの仕様をまとめる\n- よく使うモジュールやスニペット\n\nBoostに保存しておくことで、何度も同じコードを書いたり調べたりする必要がなくなります。\n\n# 3. Finder機能を搭載、もうコマンドを手打ちする必要はありません。\n**「shift+cmd+tab」** を同時に押してみてください。\nここでは、一瞬でBoostの中身を検索するウィンドウを表示させることができます。\n\n矢印キーで選択、Enterを押し、cmd+vでペーストすると…続きはご自身の目でお確かめください。\n- sqlやlinux等の、よく使うが手打ちが面倒なコマンド\n- (メールやカスタマーサポート等でよく使うフレーズ)\n\n私たちは、圧倒的な効率性を支援します。\n\* * * *\n\n\n## ◎詳しくは\nこちらのブログ( http://blog-jp.b00st.io )にて随時更新しています。\n\nそれでは素晴らしいエンジニアライフを!\n\n## Hack your memory**'
|
||||
let defaultContent = 'Boost is a brand new note App for programmers.\n\n> 下に日本語版があります。\n\n# \u25CEfeature\n\nBoost has some preponderant functions for efficient engineer\'s task.See some part of it.\n\n1. classify information by\u300CFolders\u300D\n2. deal with great variety of syntax\n3. Finder function\n\n\uFF0A\u3000\uFF0A\u3000\uFF0A\u3000\uFF0A\n\n# 1. classify information by \u300CFolders\u300D- access the information you needed easily.\n\n\u300CFolders\u300D which on the left side bar. Press plus button now. flexible way of classification.\n- Create Folder every language or flamework\n- Make Folder for your own casual memos\n\n# 2. Deal with a great variety of syntax \u2013 instead of your brain\nSave handy all information related with programming\n- Use markdown and gather api specification\n- Well using module and snippet\n\nSave them on Boost, you don\'t need to rewrite or re-search same code again.\n\n# 3. Load Finder function \u2013 now you don\'t need to spell command by hand typing.\n\n**Shift +ctrl+tab** press buttons at same time.\nThen, the window will show up for search Boost contents that instant.\n\nUsing cursor key to chose, press enter, cmd+v to paste and\u2026 please check it out by your own eye.\n\n- Such command spl or linux which programmers often use but troublesome to hand type\n\n- (Phrases commonly used for e-mail or customer support)\n\nWe support preponderant efficiency\n\n\uFF0A\u3000\uFF0A\u3000\uFF0A\u3000\uFF0A\n\n## \u25CEfor more information\nFrequently updated with this blog ( http:\/\/blog-jp.b00st.io )\n\nHave wonderful programmer life!\n\n## Hack your memory**\n\n\n\n# 日本語版\n\n**Boost**は全く新しいエンジニアライクのノートアプリです。\n\n# ◎特徴\nBoostはエンジニアの仕事を圧倒的に効率化するいくつかの機能を備えています。\nその一部をご紹介します。\n1. Folderで情報を分類\n2. 豊富なsyantaxに対応\n3. Finder機能\n\n\n* * * *\n\n# 1. Folderで情報を分類、欲しい情報にすぐアクセス。\n左側のバーに存在する「Folders」。\n今すぐプラスボタンを押しましょう。\n分類の仕方も自由自在です。\n- 言語やフレームワークごとにFolderを作成\n- 自分用のカジュアルなメモをまとめる場としてFolderを作成\n\n\n# 2. 豊富なsyntaxに対応、自分の脳の代わりに。\nプログラミングに関する情報を全て、手軽に保存しましょう。\n- mdで、apiの仕様をまとめる\n- よく使うモジュールやスニペット\n\nBoostに保存しておくことで、何度も同じコードを書いたり調べたりする必要がなくなります。\n\n# 3. Finder機能を搭載、もうコマンドを手打ちする必要はありません。\n**「shift+ctrl+tab」** を同時に押してみてください。\nここでは、一瞬でBoostの中身を検索するウィンドウを表示させることができます。\n\n矢印キーで選択、Enterを押し、cmd+vでペーストすると…続きはご自身の目でお確かめください。\n- sqlやlinux等の、よく使うが手打ちが面倒なコマンド\n- (メールやカスタマーサポート等でよく使うフレーズ)\n\n私たちは、圧倒的な効率性を支援します。\n\* * * *\n\n\n## ◎詳しくは\nこちらのブログ( http://blog-jp.b00st.io )にて随時更新しています。\n\nそれでは素晴らしいエンジニアライフを!\n\n## Hack your memory**'
|
||||
|
||||
function getLocalPath () {
|
||||
return path.join(remote.app.getPath('userData'), 'local.json')
|
||||
}
|
||||
|
||||
export function init () {
|
||||
console.log('initialize data store')
|
||||
let data = JSON.parse(localStorage.getItem('local'))
|
||||
let data = jetpack.read(getLocalPath(), 'json')
|
||||
|
||||
if (data == null) {
|
||||
if (localStorage.getItem('local') != null) {
|
||||
data = JSON.parse(localStorage.getItem('local'))
|
||||
jetpack.write(getLocalPath(), data)
|
||||
localStorage.removeItem('local')
|
||||
return
|
||||
}
|
||||
|
||||
let defaultFolder = {
|
||||
name: 'default',
|
||||
key: keygen()
|
||||
@@ -24,37 +40,35 @@ export function init () {
|
||||
folders: [defaultFolder],
|
||||
version: '0.4'
|
||||
}
|
||||
localStorage.setItem('local', JSON.stringify(data))
|
||||
jetpack.write(getLocalPath(), data)
|
||||
}
|
||||
}
|
||||
|
||||
function getKey (teamId) {
|
||||
return teamId == null
|
||||
? 'local'
|
||||
: `team-${teamId}`
|
||||
export function getData () {
|
||||
return jetpack.read(getLocalPath(), 'json')
|
||||
}
|
||||
|
||||
export function getData (teamId) {
|
||||
let key = getKey(teamId)
|
||||
return JSON.parse(localStorage.getItem(key))
|
||||
}
|
||||
|
||||
export function setArticles (teamId, articles) {
|
||||
let key = getKey(teamId)
|
||||
let data = JSON.parse(localStorage.getItem(key))
|
||||
export function setArticles (articles) {
|
||||
let data = getData()
|
||||
data.articles = articles
|
||||
localStorage.setItem(key, JSON.stringify(data))
|
||||
jetpack.write(getLocalPath(), data)
|
||||
}
|
||||
|
||||
export function setFolders (teamId, folders) {
|
||||
let key = getKey(teamId)
|
||||
let data = JSON.parse(localStorage.getItem(key))
|
||||
export function setFolders (folders) {
|
||||
let data = getData()
|
||||
data.folders = folders
|
||||
localStorage.setItem(key, JSON.stringify(data))
|
||||
jetpack.write(getLocalPath(), data)
|
||||
}
|
||||
|
||||
function isFinderCalled () {
|
||||
var argv = process.argv.slice(1)
|
||||
return argv.some(arg => arg.match(/--finder/))
|
||||
}
|
||||
|
||||
export default (function () {
|
||||
if (!isFinderCalled()) {
|
||||
init()
|
||||
}
|
||||
return {
|
||||
init,
|
||||
getData,
|
||||
|
||||
@@ -1,9 +1,25 @@
|
||||
import markdownit from 'markdown-it'
|
||||
import hljs from 'highlight.js'
|
||||
import emoji from 'markdown-it-emoji'
|
||||
|
||||
var md = markdownit({
|
||||
typographer: true,
|
||||
linkify: true
|
||||
linkify: true,
|
||||
highlight: function (str, lang) {
|
||||
if (lang && hljs.getLanguage(lang)) {
|
||||
try {
|
||||
return hljs.highlight(lang, str).value;
|
||||
} catch (__) {}
|
||||
}
|
||||
|
||||
try {
|
||||
return hljs.highlightAuto(str).value;
|
||||
} catch (__) {}
|
||||
|
||||
return ''; // use external default escaping
|
||||
}
|
||||
})
|
||||
md.use(emoji)
|
||||
|
||||
export default function markdown (content) {
|
||||
if (content == null) content = ''
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
var shell = require('shell')
|
||||
const electron = require('electron')
|
||||
const shell = electron.shell
|
||||
|
||||
export default function (e) {
|
||||
shell.openExternal(e.currentTarget.href)
|
||||
|
||||
119
lib/reducer.js
119
lib/reducer.js
@@ -1,20 +1,51 @@
|
||||
import { combineReducers } from 'redux'
|
||||
import _ from 'lodash'
|
||||
import { SWITCH_FOLDER, SWITCH_MODE, SWITCH_ARTICLE, SET_SEARCH_FILTER, SET_TAG_FILTER, CLEAR_SEARCH, TOGGLE_TUTORIAL, ARTICLE_UPDATE, ARTICLE_DESTROY, FOLDER_CREATE, FOLDER_UPDATE, FOLDER_DESTROY, IDLE_MODE, CREATE_MODE } from './actions'
|
||||
import {
|
||||
// Status action type
|
||||
SWITCH_FOLDER,
|
||||
SWITCH_MODE,
|
||||
SWITCH_ARTICLE,
|
||||
SET_SEARCH_FILTER,
|
||||
SET_TAG_FILTER,
|
||||
CLEAR_SEARCH,
|
||||
LOCK_STATUS,
|
||||
UNLOCK_STATUS,
|
||||
TOGGLE_TUTORIAL,
|
||||
|
||||
// Article action type
|
||||
ARTICLE_UPDATE,
|
||||
ARTICLE_DESTROY,
|
||||
CLEAR_NEW_ARTICLE,
|
||||
|
||||
// Folder action type
|
||||
FOLDER_CREATE,
|
||||
FOLDER_UPDATE,
|
||||
FOLDER_DESTROY,
|
||||
FOLDER_REPLACE,
|
||||
|
||||
// view mode
|
||||
IDLE_MODE
|
||||
} from './actions'
|
||||
import dataStore from 'boost/dataStore'
|
||||
import keygen from 'boost/keygen'
|
||||
import activityRecord from 'boost/activityRecord'
|
||||
import { openModal } from 'boost/modal'
|
||||
import EditedAlert from 'boost/components/modal/EditedAlert'
|
||||
|
||||
const initialStatus = {
|
||||
mode: IDLE_MODE,
|
||||
search: '',
|
||||
isTutorialOpen: false
|
||||
isTutorialOpen: false,
|
||||
isStatusLocked: false
|
||||
}
|
||||
|
||||
let data = dataStore.getData()
|
||||
let initialArticles = data.articles
|
||||
let initialFolders = data.folders
|
||||
|
||||
let isStatusLocked = false
|
||||
let isCreatingNew = false
|
||||
|
||||
function folders (state = initialFolders, action) {
|
||||
state = state.slice()
|
||||
switch (action.type) {
|
||||
@@ -26,18 +57,17 @@ function folders (state = initialFolders, action) {
|
||||
Object.assign(newFolder, {
|
||||
key: keygen(),
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
// random number (0-7)
|
||||
color: Math.round(Math.random() * 7)
|
||||
updatedAt: new Date()
|
||||
})
|
||||
|
||||
if (newFolder.length === 0) throw new Error('Folder name is required')
|
||||
if (newFolder.name == null && newFolder.name.length === 0) throw new Error('Folder name is required')
|
||||
if (newFolder.name.match(/\//)) throw new Error('`/` is not available for folder name')
|
||||
|
||||
let conflictFolder = _.findWhere(state, {name: newFolder.name})
|
||||
if (conflictFolder != null) throw new Error(`${newFolder.name} already exists!`)
|
||||
state.push(newFolder)
|
||||
|
||||
dataStore.setFolders(null, state)
|
||||
dataStore.setFolders(state)
|
||||
activityRecord.emit('FOLDER_CREATE')
|
||||
return state
|
||||
}
|
||||
@@ -48,7 +78,8 @@ function folders (state = initialFolders, action) {
|
||||
|
||||
if (!_.isString(folder.name)) throw new Error('Folder name must be a string')
|
||||
folder.name = folder.name.trim().replace(/\s/, '_')
|
||||
if (folder.length === 0) throw new Error('Folder name is required')
|
||||
if (folder.name.length === 0) throw new Error('Folder name is required')
|
||||
if (folder.name.match(/\//)) throw new Error('`/` is not available for folder name')
|
||||
|
||||
// Folder existence check
|
||||
if (targetFolder == null) throw new Error('Folder doesnt exist')
|
||||
@@ -63,7 +94,7 @@ function folders (state = initialFolders, action) {
|
||||
updatedAt: new Date()
|
||||
})
|
||||
|
||||
dataStore.setFolders(null, state)
|
||||
dataStore.setFolders(state)
|
||||
activityRecord.emit('FOLDER_UPDATE')
|
||||
return state
|
||||
}
|
||||
@@ -76,18 +107,58 @@ function folders (state = initialFolders, action) {
|
||||
if (targetIndex >= 0) {
|
||||
state.splice(targetIndex, 1)
|
||||
}
|
||||
dataStore.setFolders(null, state)
|
||||
dataStore.setFolders(state)
|
||||
activityRecord.emit('FOLDER_DESTROY')
|
||||
return state
|
||||
}
|
||||
case FOLDER_REPLACE:
|
||||
{
|
||||
let { a, b } = action.data
|
||||
let folderA = state[a]
|
||||
let folderB = state[b]
|
||||
state.splice(a, 1, folderB)
|
||||
state.splice(b, 1, folderA)
|
||||
}
|
||||
return state
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
let isCleaned = true
|
||||
function articles (state = initialArticles, action) {
|
||||
state = state.slice()
|
||||
|
||||
if (!isCreatingNew && !isCleaned) {
|
||||
state = state.filter(article => article.status !== 'NEW')
|
||||
isCleaned = true
|
||||
}
|
||||
switch (action.type) {
|
||||
case SWITCH_ARTICLE:
|
||||
if (action.data.isNew) {
|
||||
isCleaned = false
|
||||
}
|
||||
if (!isStatusLocked && !action.data.isNew) {
|
||||
isCreatingNew = false
|
||||
if (!isCleaned) {
|
||||
state = state.filter(article => article.status !== 'NEW')
|
||||
isCleaned = true
|
||||
}
|
||||
}
|
||||
return state
|
||||
case SWITCH_FOLDER:
|
||||
case SET_SEARCH_FILTER:
|
||||
case SET_TAG_FILTER:
|
||||
case CLEAR_SEARCH:
|
||||
if (!isStatusLocked) {
|
||||
isCreatingNew = false
|
||||
if (!isCleaned) {
|
||||
state = state.filter(article => article.status !== 'NEW')
|
||||
isCleaned = true
|
||||
}
|
||||
}
|
||||
return state
|
||||
case CLEAR_NEW_ARTICLE:
|
||||
return state.filter(article => article.status !== 'NEW')
|
||||
case ARTICLE_UPDATE:
|
||||
{
|
||||
let article = action.data.article
|
||||
@@ -96,7 +167,8 @@ function articles (state = initialArticles, action) {
|
||||
if (targetIndex < 0) state.unshift(article)
|
||||
else state.splice(targetIndex, 1, article)
|
||||
|
||||
dataStore.setArticles(null, state)
|
||||
if (article.status !== 'NEW') dataStore.setArticles(state)
|
||||
else isCreatingNew = true
|
||||
return state
|
||||
}
|
||||
case ARTICLE_DESTROY:
|
||||
@@ -106,7 +178,7 @@ function articles (state = initialArticles, action) {
|
||||
let targetIndex = _.findIndex(state, _article => articleKey === _article.key)
|
||||
if (targetIndex >= 0) state.splice(targetIndex, 1)
|
||||
|
||||
dataStore.setArticles(null, state)
|
||||
dataStore.setArticles(state)
|
||||
return state
|
||||
}
|
||||
case FOLDER_DESTROY:
|
||||
@@ -115,7 +187,7 @@ function articles (state = initialArticles, action) {
|
||||
|
||||
state = state.filter(article => article.FolderKey !== folderKey)
|
||||
|
||||
dataStore.setArticles(null, state)
|
||||
dataStore.setArticles(state)
|
||||
return state
|
||||
}
|
||||
default:
|
||||
@@ -129,18 +201,31 @@ function status (state = initialStatus, action) {
|
||||
case TOGGLE_TUTORIAL:
|
||||
state.isTutorialOpen = !state.isTutorialOpen
|
||||
return state
|
||||
case LOCK_STATUS:
|
||||
isStatusLocked = state.isStatusLocked = true
|
||||
return state
|
||||
case UNLOCK_STATUS:
|
||||
isStatusLocked = state.isStatusLocked = false
|
||||
return state
|
||||
}
|
||||
|
||||
// if status locked, status become unmutable
|
||||
if (state.isStatusLocked) {
|
||||
openModal(EditedAlert, {action})
|
||||
return state
|
||||
}
|
||||
switch (action.type) {
|
||||
case SWITCH_FOLDER:
|
||||
state.mode = IDLE_MODE
|
||||
state.search = `in:${action.data} `
|
||||
state.search = `//${action.data} `
|
||||
|
||||
return state
|
||||
case SWITCH_MODE:
|
||||
state.mode = action.data
|
||||
if (state.mode === CREATE_MODE) state.articleKey = null
|
||||
|
||||
return state
|
||||
case SWITCH_ARTICLE:
|
||||
state.articleKey = action.data
|
||||
state.articleKey = action.data.key
|
||||
state.mode = IDLE_MODE
|
||||
|
||||
return state
|
||||
|
||||
190
main.js
190
main.js
@@ -1,54 +1,86 @@
|
||||
var app = require('app')
|
||||
var Menu = require('menu')
|
||||
var MenuItem = require('menu-item')
|
||||
var Tray = require('tray')
|
||||
var ipc = require('ipc')
|
||||
var jetpack = require('fs-jetpack')
|
||||
|
||||
require('crash-reporter').start()
|
||||
const electron = require('electron')
|
||||
const app = electron.app
|
||||
const Menu = electron.Menu
|
||||
const ipc = electron.ipcMain
|
||||
const globalShortcut = electron.globalShortcut
|
||||
const autoUpdater = electron.autoUpdater
|
||||
const jetpack = require('fs-jetpack')
|
||||
const path = require('path')
|
||||
const ChildProcess = require('child_process')
|
||||
electron.crashReporter.start()
|
||||
|
||||
var mainWindow = null
|
||||
var appIcon = null
|
||||
var menu = null
|
||||
var finderWindow = null
|
||||
|
||||
var finderProcess
|
||||
var update = null
|
||||
|
||||
// app.on('window-all-closed', function () {
|
||||
// if (process.platform !== 'darwin') app.quit()
|
||||
// })
|
||||
|
||||
var version = app.getVersion()
|
||||
var versionText = (version == null || version.length === 0) ? 'DEV version' : 'v' + version
|
||||
var nn = require('node-notifier')
|
||||
var updater = require('./atom-lib/updater')
|
||||
var path = require('path')
|
||||
|
||||
var appQuit = false
|
||||
|
||||
updater
|
||||
.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
|
||||
nn.notify({
|
||||
title: 'Ready to Update!! ' + versionText,
|
||||
icon: path.join(__dirname, 'browser/main/resources/favicon-230x230.png'),
|
||||
message: 'Click tray icon to update app: ' + releaseName
|
||||
var version = app.getVersion()
|
||||
var versionText = (version == null || version.length === 0) ? 'DEV version' : 'v' + version
|
||||
var versionNotified = false
|
||||
|
||||
function notify (title, body) {
|
||||
if (mainWindow != null) {
|
||||
mainWindow.webContents.send('notify', {
|
||||
title: title,
|
||||
body: body
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
autoUpdater
|
||||
.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
|
||||
update = quitAndUpdate
|
||||
|
||||
if (mainWindow != null && !mainWindow.webContents.isLoading()) {
|
||||
if (mainWindow != null) {
|
||||
notify('Ready to Update! ' + releaseName, 'Click update button on Main window.')
|
||||
mainWindow.webContents.send('update-available', 'Update available!')
|
||||
}
|
||||
})
|
||||
.on('error', function (err, message) {
|
||||
console.error(err)
|
||||
if (!versionNotified) {
|
||||
notify('Updater error!', message)
|
||||
}
|
||||
})
|
||||
// .on('checking-for-update', function () {
|
||||
// // Connecting
|
||||
// console.log('checking...')
|
||||
// })
|
||||
.on('update-available', function () {
|
||||
notify('Update is available!', 'Download started.. wait for the update ready.')
|
||||
})
|
||||
.on('update-not-available', function () {
|
||||
if (mainWindow != null && !versionNotified) {
|
||||
versionNotified = true
|
||||
notify('Latest Build!! ' + versionText, 'Hope you to enjoy our app :D')
|
||||
}
|
||||
})
|
||||
|
||||
app.on('ready', function () {
|
||||
app.on('before-quit', function () {
|
||||
if (finderProcess) finderProcess.kill()
|
||||
appQuit = true
|
||||
})
|
||||
console.log('Version ' + version)
|
||||
updater.setFeedUrl('http://orbital.b00st.io/rokt33r/boost-app/latest?version=' + version)
|
||||
updater.checkForUpdates()
|
||||
// menu start
|
||||
autoUpdater.setFeedURL('https://orbital.b00st.io/rokt33r/boost-app/latest?version=' + version)
|
||||
autoUpdater.checkForUpdates()
|
||||
|
||||
var template = require('./atom-lib/menu-template')
|
||||
var menu = Menu.buildFromTemplate(template)
|
||||
Menu.setApplicationMenu(menu)
|
||||
|
||||
setInterval(function () {
|
||||
if (update == null) autoUpdater.checkForUpdates()
|
||||
}, 1000 * 60 * 60 * 24)
|
||||
|
||||
ipc.on('check-update', function (event, msg) {
|
||||
if (update == null) autoUpdater.checkForUpdates()
|
||||
})
|
||||
|
||||
ipc.on('update-app', function (event, msg) {
|
||||
if (update != null) {
|
||||
@@ -57,48 +89,76 @@ app.on('ready', function () {
|
||||
}
|
||||
})
|
||||
|
||||
menu = Menu.buildFromTemplate(template)
|
||||
|
||||
Menu.setApplicationMenu(menu)
|
||||
// menu end
|
||||
appIcon = new Tray(__dirname + '/resources/tray-icon.png')
|
||||
appIcon.setToolTip('Boost')
|
||||
|
||||
var trayMenu = new Menu()
|
||||
trayMenu.append(new MenuItem({
|
||||
label: 'Open main window',
|
||||
click: function () {
|
||||
if (mainWindow != null) mainWindow.show()
|
||||
}
|
||||
}))
|
||||
trayMenu.append(new MenuItem({
|
||||
label: 'Quit',
|
||||
click: function () {
|
||||
app.quit()
|
||||
}
|
||||
}))
|
||||
appIcon.setContextMenu(trayMenu)
|
||||
|
||||
mainWindow = require('./atom-lib/main-window')
|
||||
mainWindow.on('close', function (e) {
|
||||
if (appQuit) return true
|
||||
e.preventDefault()
|
||||
mainWindow.hide()
|
||||
})
|
||||
if (update != null) {
|
||||
mainWindow.webContents.on('did-finish-load', function () {
|
||||
mainWindow.webContents.send('update-available', 'whoooooooh!')
|
||||
})
|
||||
if (finderProcess == null) {
|
||||
finderProcess = ChildProcess
|
||||
.execFile(process.execPath, [path.resolve(__dirname, 'finder.js'), '--finder'])
|
||||
finderProcess.stdout.setEncoding('utf8')
|
||||
finderProcess.stderr.setEncoding('utf8')
|
||||
finderProcess.stdout.on('data', format)
|
||||
finderProcess.stderr.on('data', errorFormat)
|
||||
}
|
||||
|
||||
app.on('activate-with-no-open-windows', function () {
|
||||
if (update != null) {
|
||||
mainWindow.webContents.send('update-available', 'whoooooooh!')
|
||||
} else {
|
||||
autoUpdater.checkForUpdates()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
if (mainWindow == null) return null
|
||||
mainWindow.show()
|
||||
})
|
||||
|
||||
finderWindow = require('./atom-lib/finder-window')
|
||||
function format (payload) {
|
||||
// console.log('from finder >> ', payload)
|
||||
try {
|
||||
payload = JSON.parse(payload)
|
||||
} catch (e) {
|
||||
console.log('Not parsable payload : ', payload)
|
||||
return
|
||||
}
|
||||
switch (payload.type) {
|
||||
case 'log':
|
||||
console.log('FINDER(stdout): ' + payload.data)
|
||||
break
|
||||
case 'show-main-window':
|
||||
if (mainWindow != null) {
|
||||
mainWindow.show()
|
||||
}
|
||||
break
|
||||
case 'request-data':
|
||||
mainWindow.webContents.send('request-data')
|
||||
break
|
||||
case 'quit-app':
|
||||
appQuit = true
|
||||
app.quit()
|
||||
break
|
||||
}
|
||||
}
|
||||
function errorFormat (output) {
|
||||
console.error('FINDER(stderr):' + output)
|
||||
}
|
||||
|
||||
function emitToFinder (type, data) {
|
||||
if (!finderProcess) {
|
||||
console.log('finder process is not ready')
|
||||
return
|
||||
}
|
||||
var payload = {
|
||||
type: type,
|
||||
data: data
|
||||
}
|
||||
finderProcess.stdin.write(JSON.stringify(payload), 'utf-8')
|
||||
}
|
||||
|
||||
var globalShortcut = require('global-shortcut')
|
||||
var userDataPath = app.getPath('userData')
|
||||
if (!jetpack.cwd(userDataPath).exists('keymap.json')) {
|
||||
jetpack.cwd(userDataPath).file('keymap.json', {content: '{}'})
|
||||
@@ -114,10 +174,7 @@ app.on('ready', function () {
|
||||
|
||||
try {
|
||||
globalShortcut.register(toggleFinderKey, function () {
|
||||
if (mainWindow != null && !mainWindow.isFocused()) {
|
||||
mainWindow.hide()
|
||||
}
|
||||
finderWindow.show()
|
||||
emitToFinder('open-finder')
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err.name)
|
||||
@@ -133,10 +190,7 @@ app.on('ready', function () {
|
||||
var toggleFinderKey = global.keymap.toggleFinder != null ? global.keymap.toggleFinder : 'ctrl+tab+shift'
|
||||
try {
|
||||
globalShortcut.register(toggleFinderKey, function () {
|
||||
if (mainWindow != null && !mainWindow.isFocused()) {
|
||||
mainWindow.hide()
|
||||
}
|
||||
finderWindow.show()
|
||||
emitToFinder('open-finder')
|
||||
})
|
||||
mainWindow.webContents.send('APP_SETTING_DONE', {})
|
||||
} catch (err) {
|
||||
@@ -146,12 +200,4 @@ app.on('ready', function () {
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
global.hideFinder = function () {
|
||||
if (!mainWindow.isVisible()) {
|
||||
Menu.sendActionToFirstResponder('hide:')
|
||||
} else {
|
||||
mainWindow.focus()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
17
package.json
17
package.json
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"name": "boost",
|
||||
"version": "0.4.0-beta.2",
|
||||
"version": "0.4.2",
|
||||
"description": "Boost App",
|
||||
"main": "main.js",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "BOOST_ENV=development electron ./main.js",
|
||||
"webpack": "webpack-dev-server --hot --inline --config webpack.config.js",
|
||||
"compile": "NODE_ENV=production webpack --config webpack.config.production.js",
|
||||
"build": "electron-packager ./ Boost --app-version=$npm_package_version $npm_package_config_platform $npm_package_config_version $npm_package_config_ignore --overwrite",
|
||||
"build": "electron-packager ./ Boost --app-version=$npm_package_version $npm_package_config_platform $npm_package_config_version $npm_package_config_ignore --overwrite --asar",
|
||||
"codesign": "codesign --verbose --deep --force --sign \"MAISIN solutions Inc.\" Boost-darwin-x64/Boost.app"
|
||||
},
|
||||
"config": {
|
||||
"version": "--version=0.34.0 --app-bundle-id=com.maisin.boost",
|
||||
"version": "--version=0.35.1 --app-bundle-id=com.maisin.boost",
|
||||
"platform": "--platform=darwin --arch=x64 --prune --icon=resources/app.icns",
|
||||
"ignore": "--ignore=Boost-darwin-x64 --ignore=node_modules/devicon/icons --ignore=submodules/ace/(?!src-min)|submodules/ace/(?=src-min-noconflict)"
|
||||
},
|
||||
@@ -38,14 +38,14 @@
|
||||
"homepage": "https://github.com/Rokt33r/codexen-app#readme",
|
||||
"dependencies": {
|
||||
"devicon": "^2.0.0",
|
||||
"electron-packager": "^5.1.1",
|
||||
"font-awesome": "^4.3.0",
|
||||
"fs-jetpack": "^0.7.0",
|
||||
"highlight.js": "^8.9.1",
|
||||
"lodash": "^3.10.1",
|
||||
"markdown-it": "^4.3.1",
|
||||
"markdown-it-emoji": "^1.1.0",
|
||||
"md5": "^2.0.0",
|
||||
"moment": "^2.10.3",
|
||||
"node-notifier": "^4.2.3",
|
||||
"socket.io-client": "^1.3.6",
|
||||
"superagent": "^1.2.0",
|
||||
"superagent-promise": "^1.0.3"
|
||||
@@ -55,16 +55,15 @@
|
||||
"babel-plugin-react-transform": "^1.1.1",
|
||||
"css-loader": "^0.19.0",
|
||||
"electron-packager": "^5.1.0",
|
||||
"electron-prebuilt": "^0.33.6",
|
||||
"electron-prebuilt": "^0.35.1",
|
||||
"nib": "^1.1.0",
|
||||
"react": "^0.14.0",
|
||||
"react-dom": "^0.14.0",
|
||||
"react-redux": "^4.0.0",
|
||||
"react-router": "^1.0.0-rc1",
|
||||
"react-select": "^0.8.1",
|
||||
"react-transform-catch-errors": "^1.0.0",
|
||||
"react-transform-hmr": "^1.0.1",
|
||||
"redbox-react": "^1.1.1",
|
||||
"redbox-react": "^1.2.0",
|
||||
"redux": "^3.0.2",
|
||||
"standard": "^5.3.1",
|
||||
"style-loader": "^0.12.4",
|
||||
|
||||
@@ -3,7 +3,6 @@ var path = require('path')
|
||||
var JsonpTemplatePlugin = webpack.JsonpTemplatePlugin
|
||||
var FunctionModulePlugin = require('webpack/lib/FunctionModulePlugin')
|
||||
var NodeTargetPlugin = require('webpack/lib/node/NodeTargetPlugin')
|
||||
var ExternalsPlugin = webpack.ExternalsPlugin
|
||||
var opt = {
|
||||
path: path.join(__dirname, 'compiled'),
|
||||
filename: '[name].js',
|
||||
@@ -39,36 +38,20 @@ var config = {
|
||||
},
|
||||
plugins: [
|
||||
new webpack.NoErrorsPlugin(),
|
||||
new ExternalsPlugin('commonjs', [
|
||||
'app',
|
||||
'auto-updater',
|
||||
'browser-window',
|
||||
'content-tracing',
|
||||
'dialog',
|
||||
'global-shortcut',
|
||||
'ipc',
|
||||
'menu',
|
||||
'menu-item',
|
||||
'power-monitor',
|
||||
'protocol',
|
||||
'tray',
|
||||
'remote',
|
||||
'web-frame',
|
||||
'clipboard',
|
||||
'crash-reporter',
|
||||
'screen',
|
||||
'shell'
|
||||
]),
|
||||
new NodeTargetPlugin()
|
||||
],
|
||||
externals: [
|
||||
'electron',
|
||||
'socket.io-client',
|
||||
'md5',
|
||||
'superagent',
|
||||
'superagent-promise',
|
||||
'lodash',
|
||||
'markdown-it',
|
||||
'moment'
|
||||
'moment',
|
||||
'highlight.js',
|
||||
'markdown-it-emoji',
|
||||
'fs-jetpack'
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
var webpack = require('webpack')
|
||||
module.exports = {
|
||||
devtool: 'source-map',
|
||||
entry: {
|
||||
main: './browser/main/index.js',
|
||||
finder: './browser/finder/index.js'
|
||||
@@ -8,7 +7,7 @@ module.exports = {
|
||||
output: {
|
||||
path: 'compiled',
|
||||
filename: '[name].js',
|
||||
sourceMapFilename: '[name].map',
|
||||
// sourceMapFilename: '[name].map',
|
||||
libraryTarget: 'commonjs2'
|
||||
},
|
||||
module: {
|
||||
@@ -31,24 +30,27 @@ module.exports = {
|
||||
'process.env': {
|
||||
'NODE_ENV': JSON.stringify('production')
|
||||
}
|
||||
}),
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
compressor: {
|
||||
warnings: false
|
||||
}
|
||||
})
|
||||
// new webpack.optimize.UglifyJsPlugin({
|
||||
// compressor: {
|
||||
// warnings: false
|
||||
// }
|
||||
// })
|
||||
],
|
||||
externals: [
|
||||
'electron',
|
||||
'socket.io-client',
|
||||
'md5',
|
||||
'superagent',
|
||||
'superagent-promise',
|
||||
'lodash',
|
||||
'markdown-it',
|
||||
'moment'
|
||||
'moment',
|
||||
'highlight.js',
|
||||
'markdown-it-emoji',
|
||||
'fs-jetpack'
|
||||
],
|
||||
resolve: {
|
||||
extensions: ['', '.js', '.jsx', 'styl']
|
||||
},
|
||||
target: 'atom'
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user