mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-14 02:06:29 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2027f60014 | ||
|
|
076edd375f | ||
|
|
ab1aa56059 | ||
|
|
46f7dfdfeb | ||
|
|
fcaa5e21cf | ||
|
|
1e202db50f | ||
|
|
9405b95825 | ||
|
|
f05e256afc | ||
|
|
731ffd4a22 | ||
|
|
8f4566b7e1 | ||
|
|
95aec54f60 | ||
|
|
f14ce0d68e | ||
|
|
cc1c7f3820 | ||
|
|
2df901288a | ||
|
|
821a7c780e | ||
|
|
6e2e48fa64 | ||
|
|
2864ac88f5 | ||
|
|
4a292d6518 | ||
|
|
e934182e86 | ||
|
|
d8fa73287b | ||
|
|
1c7cba2951 |
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"amaEnabled": true,
|
||||
"editor": {
|
||||
"fontFamily": "Monaco, Consolas",
|
||||
"fontSize": "14",
|
||||
@@ -20,7 +21,7 @@
|
||||
"codeBlockTheme": "dracula",
|
||||
"fontFamily": "Lato",
|
||||
"fontSize": "14",
|
||||
"lineNumber": true,
|
||||
"lineNumber": true
|
||||
},
|
||||
"sortBy": "UPDATED_AT",
|
||||
"ui": {
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import { isArray } from 'lodash'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import { getTodoStatus } from 'browser/lib/getTodoStatus'
|
||||
import styles from './NoteItem.styl'
|
||||
import TodoProcess from './TodoProcess'
|
||||
|
||||
/**
|
||||
* @description Tag element component.
|
||||
@@ -68,6 +70,10 @@ const NoteItem = ({ isActive, note, dateDisplay, handleNoteClick, handleDragStar
|
||||
{note.isStarred
|
||||
? <i styleName='item-star' className='fa fa-star' /> : ''
|
||||
}
|
||||
{note.type === 'MARKDOWN_NOTE'
|
||||
? <TodoProcess todoStatus={getTodoStatus(note.content)} />
|
||||
: ''
|
||||
}
|
||||
<div styleName='item-bottom'>
|
||||
<div styleName='item-bottom-tagList'>
|
||||
{note.tags.length > 0
|
||||
|
||||
@@ -39,6 +39,7 @@ $control-height = 30px
|
||||
.item-wrapper
|
||||
padding 15px 0
|
||||
border-bottom $ui-border
|
||||
position relative
|
||||
|
||||
.item--active
|
||||
@extend .item
|
||||
@@ -116,8 +117,8 @@ $control-height = 30px
|
||||
|
||||
.item-star
|
||||
position absolute
|
||||
right 5px
|
||||
bottom 0px
|
||||
right -20px
|
||||
bottom 2px
|
||||
width 34px
|
||||
height 34px
|
||||
color alpha($ui-favorite-star-button-color, 60%)
|
||||
|
||||
@@ -48,6 +48,7 @@ $control-height = 30px
|
||||
overflow ellipsis
|
||||
color $ui-inactive-text-color
|
||||
border-bottom $ui-border
|
||||
position relative
|
||||
|
||||
.item-simple-title-icon
|
||||
font-size 12px
|
||||
|
||||
33
browser/components/TodoProcess.js
Normal file
33
browser/components/TodoProcess.js
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @fileoverview Percentage of todo achievement.
|
||||
*/
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './TodoProcess.styl'
|
||||
|
||||
const TodoProcess = ({
|
||||
todoStatus: {
|
||||
total: totalTodo,
|
||||
completed: completedTodo
|
||||
}
|
||||
}) => (
|
||||
<div styleName='todo-process' style={{display: totalTodo > 0 ? '' : 'none'}}>
|
||||
<div styleName='todo-process-text'>
|
||||
<i className='fa fa-fw fa-check-square-o' />
|
||||
{completedTodo} of {totalTodo}
|
||||
</div>
|
||||
<div styleName='todo-process-bar'>
|
||||
<div styleName='todo-process-bar--inner' style={{width: parseInt(completedTodo / totalTodo * 100) + '%'}} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
TodoProcess.propTypes = {
|
||||
todoStatus: {
|
||||
total: PropTypes.number.isRequired,
|
||||
completed: PropTypes.number.isRequired
|
||||
}
|
||||
}
|
||||
|
||||
export default CSSModules(TodoProcess, styles)
|
||||
45
browser/components/TodoProcess.styl
Normal file
45
browser/components/TodoProcess.styl
Normal file
@@ -0,0 +1,45 @@
|
||||
.todo-process
|
||||
font-size 12px
|
||||
display flex
|
||||
padding-top 15px
|
||||
width 85%
|
||||
|
||||
.todo-process-text
|
||||
display inline-block
|
||||
padding-right 10px
|
||||
white-space nowrap
|
||||
text-overflow ellipsis
|
||||
color $ui-inactive-text-color
|
||||
i
|
||||
color $ui-inactive-text-color
|
||||
padding-right 5px
|
||||
|
||||
.todo-process-bar
|
||||
display inline-block
|
||||
margin auto
|
||||
height 4px
|
||||
border-radius 10px
|
||||
background-color #DADFE1
|
||||
border-radius 2px
|
||||
flex-grow 1
|
||||
border 1px solid alpha(#6C7A89, 10%)
|
||||
|
||||
.todo-process-bar--inner
|
||||
height 100%
|
||||
border-radius 5px
|
||||
background-color #6C7A89
|
||||
transition 0.3s
|
||||
|
||||
|
||||
body[data-theme="dark"]
|
||||
.todo-process
|
||||
color $ui-dark-text-color
|
||||
|
||||
.todo-process-bar
|
||||
background-color #363A3D
|
||||
|
||||
.todo-process-text
|
||||
color $ui-inactive-text-color
|
||||
|
||||
.todo-process-bar--inner
|
||||
background-color: alpha(#939395, 50%)
|
||||
21
browser/lib/RcParser.js
Normal file
21
browser/lib/RcParser.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import path from 'path'
|
||||
import sander from 'sander'
|
||||
|
||||
const BOOSTNOTERC = '.boostnoterc'
|
||||
const homePath = global.process.env.HOME || global.process.env.USERPROFILE
|
||||
const _boostnotercPath = path.join(homePath, BOOSTNOTERC)
|
||||
|
||||
export function parse (boostnotercPath = _boostnotercPath) {
|
||||
if (!sander.existsSync(boostnotercPath)) return {}
|
||||
try {
|
||||
return JSON.parse(sander.readFileSync(boostnotercPath).toString())
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
console.warn('Your .boostnoterc is broken so it\'s not used.')
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
parse
|
||||
}
|
||||
25
browser/lib/getTodoStatus.js
Normal file
25
browser/lib/getTodoStatus.js
Normal file
@@ -0,0 +1,25 @@
|
||||
export function getTodoStatus (content) {
|
||||
let splitted = content.split('\n')
|
||||
let numberOfTodo = 0
|
||||
let numberOfCompletedTodo = 0
|
||||
|
||||
splitted.forEach((line) => {
|
||||
let trimmedLine = line.trim()
|
||||
if (trimmedLine.match(/^[\+\-\*] \[\s|x\] ./)) {
|
||||
numberOfTodo++
|
||||
}
|
||||
if (trimmedLine.match(/^[\+\-\*] \[x\] ./)) {
|
||||
numberOfCompletedTodo++
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
total: numberOfTodo,
|
||||
completed: numberOfCompletedTodo
|
||||
}
|
||||
}
|
||||
|
||||
export function getTodoPercentageOfCompleted (content) {
|
||||
const state = getTodoStatus(content)
|
||||
return Math.floor(state.completed / state.total * 100)
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import InfoButton from './InfoButton'
|
||||
import InfoPanel from './InfoPanel'
|
||||
import InfoPanelTrashed from './InfoPanelTrashed'
|
||||
import { formatDate } from 'browser/lib/date-formatter'
|
||||
import { getTodoPercentageOfCompleted } from 'browser/lib/getTodoStatus'
|
||||
|
||||
const electron = require('electron')
|
||||
const { remote } = electron
|
||||
@@ -70,24 +71,6 @@ class MarkdownNoteDetail extends React.Component {
|
||||
ee.off('topbar:togglelockbutton', this.toggleLockButton)
|
||||
}
|
||||
|
||||
getPercentageOfCompleteTodo (noteContent) {
|
||||
let splitted = noteContent.split('\n')
|
||||
let numberOfTodo = 0
|
||||
let numberOfCompletedTodo = 0
|
||||
|
||||
splitted.forEach((line) => {
|
||||
let trimmedLine = line.trim()
|
||||
if (trimmedLine.match(/^[\+\-\*] \[\s|x\] ./)) {
|
||||
numberOfTodo++
|
||||
}
|
||||
if (trimmedLine.match(/^[\+\-\*] \[x\] ./)) {
|
||||
numberOfCompletedTodo++
|
||||
}
|
||||
})
|
||||
|
||||
return Math.floor(numberOfCompletedTodo / numberOfTodo * 100)
|
||||
}
|
||||
|
||||
handleChange (e) {
|
||||
let { note } = this.state
|
||||
|
||||
@@ -333,7 +316,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
onChange={(e) => this.handleChange(e)}
|
||||
/>
|
||||
<TodoListPercentage
|
||||
percentageOfTodo={this.getPercentageOfCompleteTodo(note.content)}
|
||||
percentageOfTodo={getTodoPercentageOfCompleted(note.content)}
|
||||
/>
|
||||
</div>
|
||||
<div styleName='info-right'>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import _ from 'lodash'
|
||||
import RcParser from 'browser/main/lib/RcParser'
|
||||
import RcParser from 'browser/lib/RcParser'
|
||||
|
||||
const OSX = global.process.platform === 'darwin'
|
||||
const win = global.process.platform === 'win32'
|
||||
@@ -60,19 +60,17 @@ function _save (config) {
|
||||
}
|
||||
|
||||
function get () {
|
||||
let config = window.localStorage.getItem('config')
|
||||
const rawStoredConfig = window.localStorage.getItem('config')
|
||||
const storedConfig = Object.assign({}, DEFAULT_CONFIG, JSON.parse(rawStoredConfig))
|
||||
let config = storedConfig
|
||||
|
||||
try {
|
||||
const boostnotercConfig = RcParser.parse()
|
||||
|
||||
config = Object.assign({}, DEFAULT_CONFIG, JSON.parse(config))
|
||||
|
||||
config = Object.assign({}, DEFAULT_CONFIG, boostnotercConfig)
|
||||
config = assignConfigValues(config, boostnotercConfig, config)
|
||||
config = assignConfigValues(storedConfig, boostnotercConfig)
|
||||
|
||||
if (!validate(config)) throw new Error('INVALID CONFIG')
|
||||
} catch (err) {
|
||||
console.warn('Boostnote resets the malformed configuration.')
|
||||
console.warn('Boostnote resets the invalid configuration.')
|
||||
config = DEFAULT_CONFIG
|
||||
_save(config)
|
||||
}
|
||||
@@ -131,12 +129,12 @@ function set (updates) {
|
||||
})
|
||||
}
|
||||
|
||||
function assignConfigValues (config, rcConfig, originalConfig) {
|
||||
config = Object.assign({}, DEFAULT_CONFIG, rcConfig, originalConfig)
|
||||
config.hotkey = Object.assign({}, DEFAULT_CONFIG.hotkey, rcConfig.hotkey, originalConfig.hotkey)
|
||||
config.ui = Object.assign({}, DEFAULT_CONFIG.ui, rcConfig.ui, originalConfig.ui)
|
||||
config.editor = Object.assign({}, DEFAULT_CONFIG.editor, rcConfig.editor, originalConfig.editor)
|
||||
config.preview = Object.assign({}, DEFAULT_CONFIG.preview, rcConfig.preview, originalConfig.preview)
|
||||
function assignConfigValues (originalConfig, rcConfig) {
|
||||
let config = Object.assign({}, DEFAULT_CONFIG, originalConfig, rcConfig)
|
||||
config.hotkey = Object.assign({}, DEFAULT_CONFIG.hotkey, originalConfig.hotkey, rcConfig.hotkey)
|
||||
config.ui = Object.assign({}, DEFAULT_CONFIG.ui, originalConfig.ui, rcConfig.ui)
|
||||
config.editor = Object.assign({}, DEFAULT_CONFIG.editor, originalConfig.editor, rcConfig.editor)
|
||||
config.preview = Object.assign({}, DEFAULT_CONFIG.preview, originalConfig.preview, rcConfig.preview)
|
||||
return config
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import path from 'path'
|
||||
import sander from 'sander'
|
||||
|
||||
function parse () {
|
||||
const BOOSTNOTERC = '.boostnoterc'
|
||||
const homePath = global.process.env.HOME || global.process.env.USERPROFILE
|
||||
const boostnotercPath = path.join(homePath, BOOSTNOTERC)
|
||||
|
||||
if (!sander.existsSync(boostnotercPath)) return {}
|
||||
return JSON.parse(sander.readFileSync(boostnotercPath).toString())
|
||||
}
|
||||
|
||||
export default {
|
||||
parse
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "boost",
|
||||
"version": "0.8.12",
|
||||
"version": "0.8.13",
|
||||
"main": "index.js",
|
||||
"description": "Boostnote",
|
||||
"license": "GPL-3.0",
|
||||
|
||||
33
tests/lib/boostnoterc/.boostnoterc.all
Normal file
33
tests/lib/boostnoterc/.boostnoterc.all
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"amaEnabled": true,
|
||||
"editor": {
|
||||
"fontFamily": "Monaco, Consolas",
|
||||
"fontSize": "14",
|
||||
"indentSize": "2",
|
||||
"indentType": "space",
|
||||
"keyMap": "vim",
|
||||
"switchPreview": "BLUR",
|
||||
"theme": "monokai"
|
||||
},
|
||||
"hotkey": {
|
||||
"toggleFinder": "Cmd + Alt + S",
|
||||
"toggleMain": "Cmd + Alt + L"
|
||||
},
|
||||
"isSideNavFolded": false,
|
||||
"listStyle": "DEFAULT",
|
||||
"listWidth": 174,
|
||||
"navWidth": 200,
|
||||
"preview": {
|
||||
"codeBlockTheme": "dracula",
|
||||
"fontFamily": "Lato",
|
||||
"fontSize": "14",
|
||||
"lineNumber": true
|
||||
},
|
||||
"sortBy": "UPDATED_AT",
|
||||
"ui": {
|
||||
"defaultNote": "ALWAYS_ASK",
|
||||
"disableDirectWrite": false,
|
||||
"theme": "default"
|
||||
},
|
||||
"zoom": 1
|
||||
}
|
||||
12
tests/lib/boostnoterc/.boostnoterc.invalid
Normal file
12
tests/lib/boostnoterc/.boostnoterc.invalid
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"editor": {
|
||||
"keyMap": "vim",
|
||||
"switchPreview": "BLUR",
|
||||
"theme": "monokai",
|
||||
},
|
||||
"hotkey": {
|
||||
"toggleMain": "Control + L"
|
||||
},
|
||||
"listWidth": 135,
|
||||
"navWidth": 135
|
||||
}
|
||||
12
tests/lib/boostnoterc/.boostnoterc.valid
Normal file
12
tests/lib/boostnoterc/.boostnoterc.valid
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"editor": {
|
||||
"keyMap": "vim",
|
||||
"switchPreview": "BLUR",
|
||||
"theme": "monokai"
|
||||
},
|
||||
"hotkey": {
|
||||
"toggleMain": "Control + L"
|
||||
},
|
||||
"listWidth": 135,
|
||||
"navWidth": 135
|
||||
}
|
||||
23
tests/lib/get-todo-status-test.js
Normal file
23
tests/lib/get-todo-status-test.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const test = require('ava')
|
||||
const { getTodoStatus } = require('browser/lib/getTodoStatus')
|
||||
|
||||
// Unit test
|
||||
test('getTodoStatus should return a correct hash object', t => {
|
||||
// [input, expected]
|
||||
const testCases = [
|
||||
['', { total: 0, completed: 0 }],
|
||||
['* [ ] a\n', { total: 1, completed: 0 }],
|
||||
['* [ ] a\n* [x] a\n', { total: 2, completed: 1 }],
|
||||
['- [ ] a\n', { total: 1, completed: 0 }],
|
||||
['- [ ] a\n- [x] a\n', { total: 2, completed: 1 }],
|
||||
['+ [ ] a\n', { total: 1, completed: 0 }],
|
||||
['+ [ ] a\n+ [x] a\n', { total: 2, completed: 1 }]
|
||||
]
|
||||
|
||||
testCases.forEach(testCase => {
|
||||
const [input, expected] = testCase
|
||||
t.is(getTodoStatus(input).total, expected.total, `Test for getTodoStatus() input: ${input} expected: ${expected.total}`)
|
||||
t.is(getTodoStatus(input).completed, expected.completed, `Test for getTodoStatus() input: ${input} expected: ${expected.completed}`)
|
||||
})
|
||||
})
|
||||
|
||||
33
tests/lib/rc-parser-test.js
Normal file
33
tests/lib/rc-parser-test.js
Normal file
@@ -0,0 +1,33 @@
|
||||
const test = require('ava')
|
||||
const path = require('path')
|
||||
const { parse } = require('browser/lib/RcParser')
|
||||
|
||||
// Unit test
|
||||
test('RcParser should return a json object', t => {
|
||||
const validJson = { 'editor': { 'keyMap': 'vim', 'switchPreview': 'BLUR', 'theme': 'monokai' }, 'hotkey': { 'toggleMain': 'Control + L' }, 'listWidth': 135, 'navWidth': 135 }
|
||||
const allJson = { 'amaEnabled': true, 'editor': { 'fontFamily': 'Monaco, Consolas', 'fontSize': '14', 'indentSize': '2', 'indentType': 'space', 'keyMap': 'vim', 'switchPreview': 'BLUR', 'theme': 'monokai' }, 'hotkey': { 'toggleFinder': 'Cmd + Alt + S', 'toggleMain': 'Cmd + Alt + L' }, 'isSideNavFolded': false, 'listStyle': 'DEFAULT', 'listWidth': 174, 'navWidth': 200, 'preview': { 'codeBlockTheme': 'dracula', 'fontFamily': 'Lato', 'fontSize': '14', 'lineNumber': true }, 'sortBy': 'UPDATED_AT', 'ui': { 'defaultNote': 'ALWAYS_ASK', 'disableDirectWrite': false, 'theme': 'default' }, 'zoom': 1 }
|
||||
|
||||
// [input, expected]
|
||||
const validTestCases = [
|
||||
['.boostnoterc.valid', validJson],
|
||||
['.boostnoterc.all', allJson]
|
||||
]
|
||||
|
||||
const invalidTestCases = [
|
||||
['.boostnoterc.invalid', {}]
|
||||
]
|
||||
|
||||
validTestCases.forEach(validTestCase => {
|
||||
const [input, expected] = validTestCase
|
||||
t.is(parse(filePath(input)).editor.keyMap, expected.editor.keyMap, `Test for getTodoStatus() input: ${input} expected: ${expected.keyMap}`)
|
||||
})
|
||||
|
||||
invalidTestCases.forEach(invalidTestCase => {
|
||||
const [input, expected] = invalidTestCase
|
||||
t.is(parse(filePath(input)).editor, expected.editor, `Test for getTodoStatus() input: ${input} expected: ${expected.editor}`)
|
||||
})
|
||||
})
|
||||
|
||||
function filePath (filename) {
|
||||
return path.join('boostnoterc', filename)
|
||||
}
|
||||
Reference in New Issue
Block a user