1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-14 10:16:26 +00:00

Compare commits

...

47 Commits

Author SHA1 Message Date
SuenagaRyota
35938c09e8 Merge pull request #783 from asmsuechan/add-boostnoterc-for-config
Add boostnoterc revival
2017-08-10 16:58:23 +09:00
SuenagaRyota
9eaa90c691 Merge pull request #768 from BoostIO/fix-infoPanel-layout-in-trash
Fixed InfoPanel layout in Trash
2017-08-10 16:52:07 +09:00
Kazu Yokomizo
049835d426 Fix typo “in Trash” to “Trash” 2017-08-10 16:43:25 +09:00
asmsuechan
af91c40406 Remove unused variable 2017-08-10 16:42:14 +09:00
asmsuechan
4940ad6825 Add .boostnoterc.sample 2017-08-10 16:38:20 +09:00
asmsuechan
d02b740300 Fix assignConfigValues because it didn't return proper hash object 2017-08-10 16:37:31 +09:00
asmsuechan
9cb443dc2f Remove unused variable 2017-08-10 15:18:55 +09:00
asmsuechan
473b80710d Remove RcParser.exec 2017-08-10 11:14:42 +09:00
asmsuechan
2247c0835d Ignore any errors in ~/.boostnoterc 2017-08-10 09:54:56 +09:00
asmsuechan
b7b715ba3d Fix a return value of RcParser.parse 2017-08-10 09:44:00 +09:00
asmsuechan
6c43fb2325 Enable to set configs in ~/.boostnoterc 2017-08-10 09:43:50 +09:00
asmsuechan
a6fe3c27d4 Fix a cyclic object value error 2017-08-10 09:43:19 +09:00
asmsuechan
d47ff96b13 Enable to set configs in ~/.boostnoterc 2017-08-10 09:43:19 +09:00
SuenagaRyota
a0def654bd Merge pull request #782 from MrBMT/update-app-wording
Update application wording
2017-08-10 07:15:18 +09:00
Ben
4873b40e49 Update application text
Updates application text in various places to correct the wording of or better reflect the functionality provided.
2017-08-09 16:48:39 +01:00
SuenagaRyota
0a758f20a7 Merge pull request #780 from asmsuechan/fix-storageKey-undefined
Add storageKey to MarkdownEditor
2017-08-09 23:16:53 +09:00
asmsuechan
5e58d457a3 Add storageKey to MarkdownEditor 2017-08-09 23:12:07 +09:00
SuenagaRyota
0f745361ad Merge pull request #779 from MrBMT/fix-default-fonts
Add Lato font-face definition to main.html
2017-08-09 22:51:43 +09:00
Ben
bf6cae9a0e Add Lato font definition to main.html
Lato is the default "Preview font family" font. However, in the case that Lato is not installed on the user's system, it falls back to using a different font instead of using the font included with the application.
2017-08-09 14:42:07 +01:00
SuenagaRyota
ab640a7676 Merge pull request #777 from MrBMT/finder-fix
Finder UI Fix (My Storage positioning)
2017-08-09 21:04:26 +09:00
Ben
820171e19e Fixes storage positioning
Fixes positioning of the My Storage section on Finder, so that it is not shown over the top of the Trash.
2017-08-09 12:34:14 +01:00
Kazu Yokomizo
f1e9d0ab81 Merge pull request #767 from mrseanbaines/fix-typo
Fixes typo in welcome screen
2017-08-09 11:55:58 +09:00
Sean Baines
0646484c83 Fixes wording on folder delete
"This work will deletes all notes in the folder and can not be undone." to "This will delete all notes in the folder and can not be undone."
2017-08-06 11:34:39 +01:00
Kazu Yokomizo
a27b79c213 Add newline at InfoPanel 2017-08-06 13:59:51 +09:00
Kazu Yokomizo
773a9b4b7f Fixed InfoPanel layout in Trash 2017-08-06 12:56:52 +09:00
Sean Baines
07b838ef7b Fixes typo in welcome screen 2017-08-05 17:17:33 +01:00
SuenagaRyota
85217a7171 Merge pull request #766 from asmsuechan/improve-search
Context search
2017-08-05 22:56:31 +09:00
asmsuechan
886d7b7227 Fix test 2017-08-05 22:48:04 +09:00
asmsuechan
6987b762dd Make context-search work 2017-08-05 22:23:33 +09:00
SuenagaRyota
f32ac81f84 Merge pull request #764 from asmsuechan/fix-noteCount-by-trash
Fix noteCount on a note trased
2017-08-05 22:03:44 +09:00
asmsuechan
87ea66bb92 Change iterate variable 2017-08-05 22:00:12 +09:00
SuenagaRyota
ff6fd62932 Merge pull request #765 from asmsuechan/iss-758
iss #758 Add InfoButton and InfoPanel in Trash
2017-08-05 21:56:22 +09:00
asmsuechan
76728448ff iss #758 Add InfoButton and InfoPanel in SnippetNoteDetail 2017-08-05 17:34:26 +09:00
asmsuechan
3b7225e0fa iss #758 Add InfoButton and InfoPanel in Trash 2017-08-05 17:30:59 +09:00
asmsuechan
d6280f4397 Fix noteCount on a note trased 2017-08-05 17:16:57 +09:00
SuenagaRyota
8df867046f Merge pull request #753 from asmsuechan/work-shortcuts-in-right-click
Change to work ctrl-e and ctrl-w in RIGHTCLICK
2017-08-05 12:05:22 +09:00
SuenagaRyota
331c822816 Merge pull request #754 from asmsuechan/add-print
Add Print
2017-08-05 11:59:16 +09:00
SuenagaRyota
6219173945 Merge pull request #763 from asmsuechan/fix-issue_template
Stress words in ISSUE_TEMPLATE
2017-08-04 08:36:27 +09:00
asmsuechan
6207e02e7f Stress words in ISSUE_TEMPLATE 2017-08-04 08:33:09 +09:00
asmsuechan
537ba537dc Add Print 2017-08-01 19:30:48 +09:00
asmsuechan
3e919241e6 Change to work ctrl-e and ctrl-w in RIGHTCLICK 2017-08-01 09:20:22 +09:00
SuenagaRyota
2324327e7e Merge pull request #749 from asmsuechan/change-order-for-ama-event
Change order for AMA event
2017-07-31 23:08:41 +09:00
asmsuechan
b8374494ea Change order for AMA event 2017-07-31 23:02:49 +09:00
SuenagaRyota
a480ca7b55 Merge pull request #731 from asmsuechan/add-find-storage-path-module
Add find storage path module
2017-07-30 16:15:33 +09:00
asmsuechan
f39b7594ab Add tests for findStorage() 2017-07-26 15:16:21 +09:00
asmsuechan
f79734391e Change to use the module 2017-07-26 15:16:18 +09:00
asmsuechan
e54f516418 Add findStorage() 2017-07-26 14:40:50 +09:00
34 changed files with 325 additions and 84 deletions

32
.boostnoterc.sample Normal file
View File

@@ -0,0 +1,32 @@
{
"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
}

View File

@@ -1 +1 @@
Please paste some **screenshots** with opening the developer tool if you report a bug. Please paste some **screenshots** with the **developer tool** open if you report a bug.

View File

@@ -4,6 +4,7 @@ import styles from './MarkdownEditor.styl'
import CodeEditor from 'browser/components/CodeEditor' import CodeEditor from 'browser/components/CodeEditor'
import MarkdownPreview from 'browser/components/MarkdownPreview' import MarkdownPreview from 'browser/components/MarkdownPreview'
import eventEmitter from 'browser/main/lib/eventEmitter' import eventEmitter from 'browser/main/lib/eventEmitter'
import { findStorage } from 'browser/lib/findStorage'
const _ = require('lodash') const _ = require('lodash')
class MarkdownEditor extends React.Component { class MarkdownEditor extends React.Component {
@@ -80,7 +81,6 @@ class MarkdownEditor extends React.Component {
if (newStatus === 'CODE') { if (newStatus === 'CODE') {
this.refs.code.focus() this.refs.code.focus()
} else { } else {
this.refs.code.blur()
this.refs.preview.focus() this.refs.preview.focus()
} }
eventEmitter.emit('topbar:togglelockbutton', this.state.status) eventEmitter.emit('topbar:togglelockbutton', this.state.status)
@@ -163,15 +163,18 @@ class MarkdownEditor extends React.Component {
} }
handleKeyDown (e) { handleKeyDown (e) {
let { config } = this.props
if (this.state.status !== 'CODE') return false if (this.state.status !== 'CODE') return false
const keyPressed = this.state.keyPressed const keyPressed = this.state.keyPressed
keyPressed.add(e.keyCode) keyPressed.add(e.keyCode)
this.setState({ keyPressed }) this.setState({ keyPressed })
let isNoteHandlerKey = (el) => { return keyPressed.has(el) } let isNoteHandlerKey = (el) => { return keyPressed.has(el) }
// These conditions are for ctrl-e and ctrl-w
if (keyPressed.size === this.escapeFromEditor.length && if (keyPressed.size === this.escapeFromEditor.length &&
!this.state.isLocked && this.state.status === 'CODE' && !this.state.isLocked && this.state.status === 'CODE' &&
this.escapeFromEditor.every(isNoteHandlerKey)) { this.escapeFromEditor.every(isNoteHandlerKey)) {
document.activeElement.blur() this.handleContextMenu()
if (config.editor.switchPreview === 'BLUR') document.activeElement.blur()
} }
if (keyPressed.size === this.supportMdSelectionBold.length && this.supportMdSelectionBold.every(isNoteHandlerKey)) { if (keyPressed.size === this.supportMdSelectionBold.length && this.supportMdSelectionBold.every(isNoteHandlerKey)) {
this.addMdAroundWord('**') this.addMdAroundWord('**')
@@ -214,10 +217,7 @@ class MarkdownEditor extends React.Component {
let previewStyle = {} let previewStyle = {}
if (this.props.ignorePreviewPointerEvents) previewStyle.pointerEvents = 'none' if (this.props.ignorePreviewPointerEvents) previewStyle.pointerEvents = 'none'
const cachedStorageList = JSON.parse(localStorage.getItem('storages')) const storage = findStorage(storageKey)
if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.')
const storage = _.find(cachedStorageList, {key: storageKey})
if (storage === undefined) throw new Error('Target storage doesn\'t exist.')
return ( return (
<div className={className == null <div className={className == null

View File

@@ -108,6 +108,7 @@ export default class MarkdownPreview extends React.Component {
this.checkboxClickHandler = (e) => this.handleCheckboxClick(e) this.checkboxClickHandler = (e) => this.handleCheckboxClick(e)
this.saveAsTextHandler = () => this.handleSaveAsText() this.saveAsTextHandler = () => this.handleSaveAsText()
this.saveAsMdHandler = () => this.handleSaveAsMd() this.saveAsMdHandler = () => this.handleSaveAsMd()
this.printHandler = () => this.handlePrint()
this.linkClickHandler = this.handlelinkClick.bind(this) this.linkClickHandler = this.handlelinkClick.bind(this)
} }
@@ -162,6 +163,10 @@ export default class MarkdownPreview extends React.Component {
this.exportAsDocument('md') this.exportAsDocument('md')
} }
handlePrint () {
this.refs.root.contentWindow.print()
}
exportAsDocument (fileType) { exportAsDocument (fileType) {
const options = { const options = {
filters: [ filters: [
@@ -198,6 +203,7 @@ export default class MarkdownPreview extends React.Component {
this.refs.root.contentWindow.document.addEventListener('dragover', this.preventImageDroppedHandler) this.refs.root.contentWindow.document.addEventListener('dragover', this.preventImageDroppedHandler)
eventEmitter.on('export:save-text', this.saveAsTextHandler) eventEmitter.on('export:save-text', this.saveAsTextHandler)
eventEmitter.on('export:save-md', this.saveAsMdHandler) eventEmitter.on('export:save-md', this.saveAsMdHandler)
eventEmitter.on('print', this.printHandler)
} }
componentWillUnmount () { componentWillUnmount () {
@@ -208,6 +214,7 @@ export default class MarkdownPreview extends React.Component {
this.refs.root.contentWindow.document.removeEventListener('dragover', this.preventImageDroppedHandler) this.refs.root.contentWindow.document.removeEventListener('dragover', this.preventImageDroppedHandler)
eventEmitter.off('export:save-text', this.saveAsTextHandler) eventEmitter.off('export:save-text', this.saveAsTextHandler)
eventEmitter.off('export:save-md', this.saveAsMdHandler) eventEmitter.off('export:save-md', this.saveAsMdHandler)
eventEmitter.off('print', this.printHandler)
} }
componentDidUpdate (prevProps) { componentDidUpdate (prevProps) {

View File

@@ -64,7 +64,7 @@ $list-width = 250px
.result-nav-storageList .result-nav-storageList
absolute bottom left right absolute bottom left right
top 80px + 32px + 10px + 10px top 110px + 32px + 10px + 10px
overflow-y auto overflow-y auto
.result-list .result-list

View File

@@ -5,6 +5,7 @@ import MarkdownPreview from 'browser/components/MarkdownPreview'
import MarkdownEditor from 'browser/components/MarkdownEditor' import MarkdownEditor from 'browser/components/MarkdownEditor'
import CodeEditor from 'browser/components/CodeEditor' import CodeEditor from 'browser/components/CodeEditor'
import CodeMirror from 'codemirror' import CodeMirror from 'codemirror'
import { findStorage } from 'browser/lib/findStorage'
const electron = require('electron') const electron = require('electron')
const { clipboard } = electron const { clipboard } = electron
@@ -106,10 +107,7 @@ class NoteDetail extends React.Component {
let editorIndentSize = parseInt(config.editor.indentSize, 10) let editorIndentSize = parseInt(config.editor.indentSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4 if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4
const cachedStorageList = JSON.parse(localStorage.getItem('storages')) const storage = findStorage(note.storage)
if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.')
const storage = _.find(cachedStorageList, {key: note.storage})
if (storage === undefined) throw new Error('Target storage doesn\'t exist.')
if (note.type === 'SNIPPET_NOTE') { if (note.type === 'SNIPPET_NOTE') {
let tabList = note.snippets.map((snippet, index) => { let tabList = note.snippets.map((snippet, index) => {
@@ -148,6 +146,7 @@ class NoteDetail extends React.Component {
config={config} config={config}
value={snippet.content} value={snippet.content}
ref={'code-' + index} ref={'code-' + index}
storageKey={note.storage}
/> />
: <CodeEditor styleName='tabView-content' : <CodeEditor styleName='tabView-content'
mode={snippet.mode} mode={snippet.mode}

View File

@@ -0,0 +1,14 @@
const _ = require('lodash')
export function findStorage (storageKey) {
const cachedStorageList = JSON.parse(localStorage.getItem('storages'))
if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.')
const storage = _.find(cachedStorageList, {key: storageKey})
if (storage === undefined) throw new Error('Target storage doesn\'t exist.')
return storage
}
export default {
findStorage
}

View File

@@ -1,7 +1,6 @@
import _ from 'lodash' import _ from 'lodash'
export default function searchFromNotes (data, search) { export default function searchFromNotes (notes, search) {
let notes = data.noteMap.map((note) => note)
if (search.trim().length === 0) return [] if (search.trim().length === 0) return []
let searchBlocks = search.split(' ') let searchBlocks = search.split(' ')
searchBlocks.forEach((block) => { searchBlocks.forEach((block) => {

View File

@@ -24,7 +24,7 @@ const InfoPanel = ({
</div> </div>
<div styleName='group-section'> <div styleName='group-section'>
<div styleName='group-section-label'> <div styleName='group-section-label'>
Created at Created
</div> </div>
<div styleName='group-section-control'> <div styleName='group-section-control'>
{createdAt} {createdAt}
@@ -32,7 +32,7 @@ const InfoPanel = ({
</div> </div>
<div styleName='group-section'> <div styleName='group-section'>
<div styleName='group-section-label'> <div styleName='group-section-label'>
Updated at Updated
</div> </div>
<div styleName='group-section-control'> <div styleName='group-section-control'>
{updatedAt} {updatedAt}

View File

@@ -18,6 +18,16 @@
background-color $ui-noteList-backgroundColor background-color $ui-noteList-backgroundColor
border 1px solid $border-color border 1px solid $border-color
.control-infoButton-panel-trash
z-index 200
margin-top 45px
margin-left -230px
position absolute
padding 20px 20px 0 20px
width 320px
background-color $ui-noteList-backgroundColor
border 1px solid $border-color
.group-section .group-section
display flex display flex
line-height 30px line-height 30px
@@ -40,6 +50,19 @@
width 160px width 160px
height 25px height 25px
.group-section-control text
color #EA4447
font-weight 600
font-size 14px
width 70px
height 25px
background-color rgba(226,33,113,0.1)
border none
outline none
border-radius 2px
margin-right 5px
padding 2px 5px
[id=export-wrap] [id=export-wrap]
height 90px height 90px
display flex display flex
@@ -75,6 +98,10 @@ body[data-theme="dark"]
background-color $ui-dark-noteList-backgroundColor background-color $ui-dark-noteList-backgroundColor
border 1px solid $ui-dark-borderColor border 1px solid $ui-dark-borderColor
.control-infoButton-panel-trash
background-color $ui-dark-noteList-backgroundColor
border 1px solid $ui-dark-borderColor
.group-section-label .group-section-label
color $ui-inactive-text-color color $ui-inactive-text-color

View File

@@ -0,0 +1,70 @@
import React, { PropTypes } from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './InfoPanel.styl'
const InfoPanelTrashed = ({
storageName, folderName, updatedAt, createdAt, exportAsMd, exportAsTxt
}) => (
<div className='infoPanel' styleName='control-infoButton-panel-trash' style={{display: 'none'}}>
<div styleName='group-section'>
<div styleName='group-section-label'>
Storage
</div>
<div styleName='group-section-control'>
{storageName}
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
Folder
</div>
<div styleName='group-section-control'>
<text>Trash</text>{folderName}
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
Created
</div>
<div styleName='group-section-control'>
{createdAt}
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
Updated
</div>
<div styleName='group-section-control'>
{updatedAt}
</div>
</div>
<div id='export-wrap'>
<button styleName='export--enable' onClick={(e) => exportAsMd(e)}>
<i className='fa fa-file-code-o fa-fw' />
<p>.md</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsTxt(e)}>
<i className='fa fa-file-text-o fa-fw' />
<p>.txt</p>
</button>
<button styleName='export--unable'>
<i className='fa fa-file-pdf-o fa-fw' />
<p>.pdf</p>
</button>
</div>
</div>
)
InfoPanelTrashed.propTypes = {
storageName: PropTypes.string.isRequired,
folderName: PropTypes.string.isRequired,
updatedAt: PropTypes.string.isRequired,
createdAt: PropTypes.string.isRequired,
exportAsMd: PropTypes.func.isRequired,
exportAsTxt: PropTypes.func.isRequired
}
export default CSSModules(InfoPanelTrashed, styles)

View File

@@ -17,6 +17,7 @@ import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
import TrashButton from './TrashButton' import TrashButton from './TrashButton'
import InfoButton from './InfoButton' import InfoButton from './InfoButton'
import InfoPanel from './InfoPanel' import InfoPanel from './InfoPanel'
import InfoPanelTrashed from './InfoPanelTrashed'
import { formatDate } from 'browser/lib/date-formatter' import { formatDate } from 'browser/lib/date-formatter'
const electron = require('electron') const electron = require('electron')
@@ -190,8 +191,8 @@ class MarkdownNoteDetail extends React.Component {
if (isTrashed) { if (isTrashed) {
let dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), { let dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning', type: 'warning',
message: 'Delete a note', message: 'Confirm note deletion',
detail: 'This work cannot be undone.', detail: 'This will permanently remove this note.',
buttons: ['Confirm', 'Cancel'] buttons: ['Confirm', 'Cancel']
}) })
if (dialogueButtonIndex === 1) return if (dialogueButtonIndex === 1) return
@@ -297,6 +298,17 @@ class MarkdownNoteDetail extends React.Component {
</div> </div>
<div styleName='info-right'> <div styleName='info-right'>
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} /> <TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
<InfoButton
onClick={(e) => this.handleInfoButtonClick(e)}
/>
<InfoPanelTrashed
storageName={currentOption.storage.name}
folderName={currentOption.folder.name}
updatedAt={formatDate(note.updatedAt)}
createdAt={formatDate(note.createdAt)}
exportAsMd={this.exportAsMd}
exportAsTxt={this.exportAsTxt}
/>
</div> </div>
</div> </div>

View File

@@ -20,6 +20,7 @@ import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
import TrashButton from './TrashButton' import TrashButton from './TrashButton'
import InfoButton from './InfoButton' import InfoButton from './InfoButton'
import InfoPanel from './InfoPanel' import InfoPanel from './InfoPanel'
import InfoPanelTrashed from './InfoPanelTrashed'
import { formatDate } from 'browser/lib/date-formatter' import { formatDate } from 'browser/lib/date-formatter'
function pass (name) { function pass (name) {
@@ -176,8 +177,8 @@ class SnippetNoteDetail extends React.Component {
if (isTrashed) { if (isTrashed) {
let dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), { let dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning', type: 'warning',
message: 'Delete a note', message: 'Confirm note deletion',
detail: 'This work cannot be undone.', detail: 'This will permanently remove this note.',
buttons: ['Confirm', 'Cancel'] buttons: ['Confirm', 'Cancel']
}) })
if (dialogueButtonIndex === 1) return if (dialogueButtonIndex === 1) return
@@ -519,6 +520,7 @@ class SnippetNoteDetail extends React.Component {
onChange={(e) => this.handleCodeChange(index)(e)} onChange={(e) => this.handleCodeChange(index)(e)}
ref={'code-' + index} ref={'code-' + index}
ignorePreviewPointerEvents={this.props.ignorePreviewPointerEvents} ignorePreviewPointerEvents={this.props.ignorePreviewPointerEvents}
storageKey={storageKey}
/> />
: <CodeEditor styleName='tabView-content' : <CodeEditor styleName='tabView-content'
mode={snippet.mode} mode={snippet.mode}
@@ -556,6 +558,17 @@ class SnippetNoteDetail extends React.Component {
</div> </div>
<div styleName='info-right'> <div styleName='info-right'>
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} /> <TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
<InfoButton
onClick={(e) => this.handleInfoButtonClick(e)}
/>
<InfoPanelTrashed
storageName={currentOption.storage.name}
folderName={currentOption.folder.name}
updatedAt={formatDate(note.updatedAt)}
createdAt={formatDate(note.createdAt)}
exportAsMd={this.showWarning}
exportAsTxt={this.showWarning}
/>
</div> </div>
</div> </div>

View File

@@ -49,7 +49,7 @@ class Detail extends React.Component {
tabIndex='0' tabIndex='0'
> >
<div styleName='empty'> <div styleName='empty'>
<div styleName='empty-message'>{OSX ? 'Command(⌘)' : 'Ctrl(^)'} + N<br />to create a new post</div> <div styleName='empty-message'>{OSX ? 'Command(⌘)' : 'Ctrl(^)'} + N<br />to create a new note</div>
</div> </div>
<StatusBar <StatusBar
{..._.pick(this.props, ['config', 'location', 'dispatch'])} {..._.pick(this.props, ['config', 'location', 'dispatch'])}

View File

@@ -246,7 +246,7 @@ class NoteList extends React.Component {
if (searchInputText === '') { if (searchInputText === '') {
router.push('/home') router.push('/home')
} }
return searchFromNotes(this.props.data, searchInputText) return searchFromNotes(this.notes, searchInputText)
} }
if (location.pathname.match(/\/trashed/)) { if (location.pathname.match(/\/trashed/)) {
@@ -446,9 +446,9 @@ class NoteList extends React.Component {
value={config.sortBy} value={config.sortBy}
onChange={(e) => this.handleSortByChange(e)} onChange={(e) => this.handleSortByChange(e)}
> >
<option value='UPDATED_AT'>Updated Time</option> <option value='UPDATED_AT'>Last Updated</option>
<option value='CREATED_AT'>Created Time</option> <option value='CREATED_AT'>Creation Time</option>
<option value='ALPHABETICAL'>Alphabetical</option> <option value='ALPHABETICAL'>Alphabetically</option>
</select> </select>
</div> </div>
<div styleName='control-button-area'> <div styleName='control-button-area'>

View File

@@ -114,7 +114,7 @@ class StorageItem extends React.Component {
let index = dialog.showMessageBox(remote.getCurrentWindow(), { let index = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning', type: 'warning',
message: 'Delete Folder', message: 'Delete Folder',
detail: 'This work will deletes all notes in the folder and can not be undone.', detail: 'This will delete all notes in the folder and can not be undone.',
buttons: ['Confirm', 'Cancel'] buttons: ['Confirm', 'Cancel']
}) })
@@ -180,14 +180,20 @@ class StorageItem extends React.Component {
render () { render () {
let { storage, location, isFolded, data, dispatch } = this.props let { storage, location, isFolded, data, dispatch } = this.props
let { folderNoteMap } = data let { folderNoteMap, trashedSet } = data
let folderList = storage.folders.map((folder) => { let folderList = storage.folders.map((folder) => {
let isActive = !!(location.pathname.match(new RegExp('\/storages\/' + storage.key + '\/folders\/' + folder.key))) let isActive = !!(location.pathname.match(new RegExp('\/storages\/' + storage.key + '\/folders\/' + folder.key)))
let noteSet = folderNoteMap.get(storage.key + '-' + folder.key) let noteSet = folderNoteMap.get(storage.key + '-' + folder.key)
let noteCount = noteSet != null let noteCount = 0
? noteSet.size if (noteSet) {
: 0 let trashedNoteCount = 0
const noteKeys = noteSet.map(noteKey => { return noteKey })
trashedSet.toJS().forEach(trashedKey => {
if (noteKeys.some(noteKey => { return noteKey === trashedKey })) trashedNoteCount++
})
noteCount = noteSet.size - trashedNoteCount
}
return ( return (
<StorageItemChild <StorageItemChild
key={folder.key} key={folder.key}

View File

@@ -1,10 +1,13 @@
import _ from 'lodash' import _ from 'lodash'
import RcParser from 'browser/main/lib/RcParser'
const OSX = global.process.platform === 'darwin' const OSX = global.process.platform === 'darwin'
const win = global.process.platform === 'win32' const win = global.process.platform === 'win32'
const electron = require('electron') const electron = require('electron')
const { ipcRenderer } = electron const { ipcRenderer } = electron
const consts = require('browser/lib/consts') const consts = require('browser/lib/consts')
const path = require('path')
const fs = require('fs')
let isInitialized = false let isInitialized = false
@@ -60,11 +63,13 @@ function get () {
let config = window.localStorage.getItem('config') let config = window.localStorage.getItem('config')
try { try {
const boostnotercConfig = RcParser.parse()
config = Object.assign({}, DEFAULT_CONFIG, JSON.parse(config)) config = Object.assign({}, DEFAULT_CONFIG, JSON.parse(config))
config.hotkey = Object.assign({}, DEFAULT_CONFIG.hotkey, config.hotkey)
config.ui = Object.assign({}, DEFAULT_CONFIG.ui, config.ui) config = Object.assign({}, DEFAULT_CONFIG, boostnotercConfig)
config.editor = Object.assign({}, DEFAULT_CONFIG.editor, config.editor) config = assignConfigValues(config, boostnotercConfig, config)
config.preview = Object.assign({}, DEFAULT_CONFIG.preview, config.preview)
if (!validate(config)) throw new Error('INVALID CONFIG') if (!validate(config)) throw new Error('INVALID CONFIG')
} catch (err) { } catch (err) {
console.warn('Boostnote resets the malformed configuration.') console.warn('Boostnote resets the malformed configuration.')
@@ -126,6 +131,15 @@ 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)
return config
}
export default { export default {
get, get,
set, set,

View File

@@ -0,0 +1,15 @@
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
}

View File

@@ -2,6 +2,7 @@ const fs = require('fs')
const path = require('path') const path = require('path')
const _ = require('lodash') const _ = require('lodash')
const sander = require('sander') const sander = require('sander')
const { findStorage } = require('browser/lib/findStorage')
/** /**
* @description To copy an image and return the path. * @description To copy an image and return the path.
@@ -12,11 +13,7 @@ const sander = require('sander')
function copyImage (filePath, storageKey) { function copyImage (filePath, storageKey) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
try { try {
const cachedStorageList = JSON.parse(localStorage.getItem('storages')) const targetStorage = findStorage(storageKey)
if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.')
const storage = _.find(cachedStorageList, {key: storageKey})
if (storage === undefined) throw new Error('Target storage doesn\'t exist.')
const targetStorage = storage
const inputImage = fs.createReadStream(filePath) const inputImage = fs.createReadStream(filePath)
const imageExt = path.extname(filePath) const imageExt = path.extname(filePath)

View File

@@ -3,6 +3,7 @@ const keygen = require('browser/lib/keygen')
const path = require('path') const path = require('path')
const resolveStorageData = require('./resolveStorageData') const resolveStorageData = require('./resolveStorageData')
const CSON = require('@rokt33r/season') const CSON = require('@rokt33r/season')
const { findStorage } = require('browser/lib/findStorage')
/** /**
* @param {String} storageKey * @param {String} storageKey
@@ -29,11 +30,7 @@ function createFolder (storageKey, input) {
if (!_.isString(input.name)) throw new Error('Name must be a string.') if (!_.isString(input.name)) throw new Error('Name must be a string.')
if (!_.isString(input.color)) throw new Error('Color must be a string.') if (!_.isString(input.color)) throw new Error('Color must be a string.')
rawStorages = JSON.parse(localStorage.getItem('storages')) targetStorage = findStorage(storageKey)
if (!_.isArray(rawStorages)) throw new Error('Target storage doesn\'t exist.')
targetStorage = _.find(rawStorages, {key: storageKey})
if (targetStorage == null) throw new Error('Target storage doesn\'t exist.')
} catch (e) { } catch (e) {
return Promise.reject(e) return Promise.reject(e)
} }

View File

@@ -4,6 +4,7 @@ const _ = require('lodash')
const keygen = require('browser/lib/keygen') const keygen = require('browser/lib/keygen')
const path = require('path') const path = require('path')
const CSON = require('@rokt33r/season') const CSON = require('@rokt33r/season')
const { findStorage } = require('browser/lib/findStorage')
function validateInput (input) { function validateInput (input) {
if (!_.isArray(input.tags)) input.tags = [] if (!_.isArray(input.tags)) input.tags = []
@@ -38,11 +39,7 @@ function createNote (storageKey, input) {
input = Object.assign({}, input) input = Object.assign({}, input)
validateInput(input) validateInput(input)
let cachedStorageList = JSON.parse(localStorage.getItem('storages')) targetStorage = findStorage(storageKey)
if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.')
targetStorage = _.find(cachedStorageList, {key: storageKey})
if (targetStorage == null) throw new Error('Target storage doesn\'t exist.')
} catch (e) { } catch (e) {
return Promise.reject(e) return Promise.reject(e)
} }

View File

@@ -4,6 +4,7 @@ const resolveStorageData = require('./resolveStorageData')
const resolveStorageNotes = require('./resolveStorageNotes') const resolveStorageNotes = require('./resolveStorageNotes')
const CSON = require('@rokt33r/season') const CSON = require('@rokt33r/season')
const sander = require('sander') const sander = require('sander')
const { findStorage } = require('browser/lib/findStorage')
/** /**
* @param {String} storageKey * @param {String} storageKey
@@ -21,11 +22,7 @@ function deleteFolder (storageKey, folderKey) {
let rawStorages let rawStorages
let targetStorage let targetStorage
try { try {
rawStorages = JSON.parse(localStorage.getItem('storages')) targetStorage = findStorage(storageKey)
if (!_.isArray(rawStorages)) throw new Error('Target storage doesn\'t exist.')
targetStorage = _.find(rawStorages, {key: storageKey})
if (targetStorage == null) throw new Error('Target storage doesn\'t exist.')
} catch (e) { } catch (e) {
return Promise.reject(e) return Promise.reject(e)
} }

View File

@@ -2,15 +2,12 @@ const resolveStorageData = require('./resolveStorageData')
const _ = require('lodash') const _ = require('lodash')
const path = require('path') const path = require('path')
const sander = require('sander') const sander = require('sander')
const { findStorage } = require('browser/lib/findStorage')
function deleteNote (storageKey, noteKey) { function deleteNote (storageKey, noteKey) {
let targetStorage let targetStorage
try { try {
let cachedStorageList = JSON.parse(localStorage.getItem('storages')) targetStorage = findStorage(storageKey)
if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.')
targetStorage = _.find(cachedStorageList, {key: storageKey})
if (targetStorage == null) throw new Error('Target storage doesn\'t exist.')
} catch (e) { } catch (e) {
return Promise.reject(e) return Promise.reject(e)
} }

View File

@@ -4,17 +4,13 @@ const path = require('path')
const CSON = require('@rokt33r/season') const CSON = require('@rokt33r/season')
const keygen = require('browser/lib/keygen') const keygen = require('browser/lib/keygen')
const sander = require('sander') const sander = require('sander')
const { findStorage } = require('browser/lib/findStorage')
function moveNote (storageKey, noteKey, newStorageKey, newFolderKey) { function moveNote (storageKey, noteKey, newStorageKey, newFolderKey) {
let oldStorage, newStorage let oldStorage, newStorage
try { try {
let cachedStorageList = JSON.parse(localStorage.getItem('storages')) oldStorage = findStorage(storageKey)
if (!_.isArray(cachedStorageList)) throw new Error('Storage doesn\'t exist.') newStorage = findStorage(newStorageKey)
oldStorage = _.find(cachedStorageList, {key: storageKey})
if (oldStorage == null) throw new Error('Storage doesn\'t exist.')
newStorage = _.find(cachedStorageList, {key: newStorageKey})
if (newStorage == null) throw new Error('Target storage doesn\'t exist.') if (newStorage == null) throw new Error('Target storage doesn\'t exist.')
} catch (e) { } catch (e) {
return Promise.reject(e) return Promise.reject(e)

View File

@@ -1,5 +1,7 @@
const _ = require('lodash') const _ = require('lodash')
const resolveStorageData = require('./resolveStorageData') const resolveStorageData = require('./resolveStorageData')
const { findStorage } = require('browser/lib/findStorage')
/** /**
* @param {String} key * @param {String} key
* @param {String} name * @param {String} name

View File

@@ -2,6 +2,7 @@ const resolveStorageData = require('./resolveStorageData')
const _ = require('lodash') const _ = require('lodash')
const path = require('path') const path = require('path')
const CSON = require('@rokt33r/season') const CSON = require('@rokt33r/season')
const { findStorage } = require('browser/lib/findStorage')
function validateInput (input) { function validateInput (input) {
let validatedInput = {} let validatedInput = {}
@@ -68,11 +69,7 @@ function updateNote (storageKey, noteKey, input) {
if (input == null) throw new Error('No input found.') if (input == null) throw new Error('No input found.')
input = validateInput(input) input = validateInput(input)
let cachedStorageList = JSON.parse(localStorage.getItem('storages')) targetStorage = findStorage(storageKey)
if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.')
targetStorage = _.find(cachedStorageList, {key: storageKey})
if (targetStorage == null) throw new Error('Target storage doesn\'t exist.')
} catch (e) { } catch (e) {
return Promise.reject(e) return Promise.reject(e)
} }

View File

@@ -152,7 +152,7 @@ class InitModal extends React.Component {
type: 'MARKDOWN_NOTE', type: 'MARKDOWN_NOTE',
folder: data.storage.folders[0].key, folder: data.storage.folders[0].key,
title: 'Welcome to Boostnote!', title: 'Welcome to Boostnote!',
content: '# Welcome to Boostnote! \n### _Click to edit this note._\n\n---\n\nBoostnote is an *open source* note-taking app. \nRepository is published on [GitHub](https://github.com/BoostIO/Boostnote), and tweeting everyday on [@Boostnoteapp](https://twitter.com/boostnoteapp)!\n\n## Features \n- [x] No Internet and Registration Required. \n- [ ] Quick search and copy the content of note. `macOS: Cmd + Alt + S / windows: Ctrl + Alt + S` \n- [ ] Markdown & Snippet note. \n- [ ] Available for `vim` and `emacs` mode. \n- [ ] Choose your favorite theme on UI, Editor and Code Block! \n--- \n\n- Copy Codeblock on Makrdown Preview.\n```javascript\nvar boostnote = document.getElementById(\'enjoy\').innerHTML\n\nconsole.log(boostnote)\n```' content: '# Welcome to Boostnote! \n### _Click to edit this note._\n\n---\n\nBoostnote is an *open source* note-taking app. \nRepository is published on [GitHub](https://github.com/BoostIO/Boostnote), and tweeting everyday on [@Boostnoteapp](https://twitter.com/boostnoteapp)!\n\n## Features \n- [x] No Internet and Registration Required. \n- [ ] Quick search and copy the content of note. `macOS: Cmd + Alt + S / windows: Ctrl + Alt + S` \n- [ ] Markdown & Snippet note. \n- [ ] Available for `vim` and `emacs` mode. \n- [ ] Choose your favorite theme on UI, Editor and Code Block! \n--- \n\n- Copy Codeblock on Markdown Preview.\n```javascript\nvar boostnote = document.getElementById(\'enjoy\').innerHTML\n\nconsole.log(boostnote)\n```'
}) })
.then((note) => { .then((note) => {
store.dispatch({ store.dispatch({

View File

@@ -34,15 +34,16 @@ class InfoTab extends React.Component {
amaEnabled: this.state.config.amaEnabled amaEnabled: this.state.config.amaEnabled
} }
if (!newConfig.amaEnabled) {
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('DISABLE_AMA')
}
ConfigManager.set(newConfig) ConfigManager.set(newConfig)
store.dispatch({ store.dispatch({
type: 'SET_CONFIG', type: 'SET_CONFIG',
config: newConfig config: newConfig
}) })
if (!newConfig.amaEnabled) {
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('DISABLE_AMA')
}
} }
render () { render () {

View File

@@ -298,8 +298,8 @@ class StorageItem extends React.Component {
let index = dialog.showMessageBox(remote.getCurrentWindow(), { let index = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning', type: 'warning',
message: 'Unlink Storage', message: 'Unlink Storage',
detail: 'This work just detatches a storage from Boostnote. (Any data won\'t be deleted.)', detail: 'Unlinking removes this linked storage from Boostnote. No data is removed, please manually delete the folder from your hard drive if needed.',
buttons: ['Confirm', 'Cancel'] buttons: ['Unlink', 'Cancel']
}) })
if (index === 0) { if (index === 0) {

View File

@@ -192,7 +192,7 @@ class UiTab extends React.Component {
<div styleName='group-section'> <div styleName='group-section'>
<div styleName='group-section-label'> <div styleName='group-section-label'>
Switching Preview Switch to Preview
</div> </div>
<div styleName='group-section-control'> <div styleName='group-section-control'>
<select value={config.editor.switchPreview} <select value={config.editor.switchPreview}
@@ -200,7 +200,7 @@ class UiTab extends React.Component {
onChange={(e) => this.handleUIChange(e)} onChange={(e) => this.handleUIChange(e)}
> >
<option value='BLUR'>When Editor Blurred</option> <option value='BLUR'>When Editor Blurred</option>
<option value='RIGHTCLICK'>When Right Clicking</option> <option value='RIGHTCLICK'>On Right Click</option>
</select> </select>
</div> </div>
</div> </div>
@@ -218,7 +218,7 @@ class UiTab extends React.Component {
<option value='vim'>vim</option> <option value='vim'>vim</option>
<option value='emacs'>emacs</option> <option value='emacs'>emacs</option>
</select> </select>
<span styleName='note-for-keymap'>Please reload boostnote after you change the keymap</span> <span styleName='note-for-keymap'>Please restart boostnote after you change the keymap</span>
</div> </div>
</div> </div>
@@ -271,7 +271,7 @@ class UiTab extends React.Component {
ref='previewLineNumber' ref='previewLineNumber'
type='checkbox' type='checkbox'
/>&nbsp; />&nbsp;
Code block line numbering Show line numbers for preview code blocks
</label> </label>
</div> </div>

View File

@@ -106,6 +106,17 @@ const file = {
{ {
type: 'separator' type: 'separator'
}, },
{
label: 'Print',
accelerator: 'CommandOrControl+P',
click () {
mainWindow.webContents.send('list:isMarkdownNote')
mainWindow.webContents.send('print')
}
},
{
type: 'separator'
},
{ {
label: 'Delete Note', label: 'Delete Note',
accelerator: macOS ? 'Control+Backspace' : 'Control+Delete', accelerator: macOS ? 'Control+Backspace' : 'Control+Delete',

View File

@@ -20,6 +20,15 @@
font-weight: normal; font-weight: normal;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
} }
@font-face {
font-family: 'Lato';
src: url('../resources/fonts/Lato-Regular.woff2') format('woff2'), /* Modern Browsers */
url('../resources/fonts/Lato-Regular.woff') format('woff'), /* Modern Browsers */
url('../resources/fonts/Lato-Regular.ttf') format('truetype');
font-style: normal;
font-weight: normal;
text-rendering: optimizeLegibility;
}
#loadingCover{ #loadingCover{
background-color: #f4f4f4; background-color: #f4f4f4;
position: absolute; position: absolute;

View File

@@ -0,0 +1,32 @@
const test = require('ava')
const { findStorage } = require('browser/lib/findStorage')
global.document = require('jsdom').jsdom('<body></body>')
global.window = document.defaultView
global.navigator = window.navigator
const Storage = require('dom-storage')
const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true })
const path = require('path')
const TestDummy = require('../fixtures/TestDummy')
const sander = require('sander')
const os = require('os')
const storagePath = path.join(os.tmpdir(), 'test/find-storage')
test.beforeEach((t) => {
t.context.storage = TestDummy.dummyStorage(storagePath)
localStorage.setItem('storages', JSON.stringify([t.context.storage.cache]))
})
// Unit test
test('findStorage() should return a correct storage path(string)', t => {
const storageKey = t.context.storage.cache.key
t.is(findStorage(storageKey).key, storageKey)
t.is(findStorage(storageKey).path, storagePath)
})
test.after(function after () {
localStorage.clear()
sander.rimrafSync(storagePath)
})

View File

@@ -5,7 +5,7 @@ import _ from 'lodash'
const pickContents = (notes) => notes.map((note) => { return note.content }) const pickContents = (notes) => notes.map((note) => { return note.content })
let noteList = { noteMap: [] } let notes = []
let note1, note2 let note1, note2
test.before(t => { test.before(t => {
@@ -14,7 +14,7 @@ test.before(t => {
note1 = dummyNote(data1) note1 = dummyNote(data1)
note2 = dummyNote(data2) note2 = dummyNote(data2)
noteList.noteMap = [note1, note2] notes = [note1, note2]
}) })
test('it can find notes by tags or words', t => { test('it can find notes by tags or words', t => {
@@ -30,7 +30,7 @@ test('it can find notes by tags or words', t => {
testCases.forEach((testCase) => { testCases.forEach((testCase) => {
const [input, expectedContents] = testCase const [input, expectedContents] = testCase
const results = searchFromNotes(noteList, input) const results = searchFromNotes(notes, input)
t.true(_.isEqual(pickContents(results).sort(), expectedContents.sort())) t.true(_.isEqual(pickContents(results).sort(), expectedContents.sort()))
}) })
}) })