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

Compare commits

...

68 Commits

Author SHA1 Message Date
Junyoung Choi
ab3ad0eb97 v0.8.20 2018-01-13 19:01:53 +09:00
Junyoung Choi (Sai)
2393889028 Merge pull request #1394 from BoostIO/fix-export-folder-test
Fix exportFolder error
2018-01-13 18:58:45 +09:00
Junyoung Choi
c36ecb1ed1 Fix exportFolder error
writeFileSync doesn't require any errors
2018-01-13 18:48:34 +09:00
Junyoung Choi (Sai)
3e9b28ff0c Merge pull request #1384 from BoostIO/fix-background-color-bug
fix bug happen on solarized-dark CreateFolderModal.
2018-01-13 18:12:59 +09:00
Junyoung Choi (Sai)
d4eec461a9 Merge pull request #1393 from BoostIO/fix-update
Fix windows update
2018-01-13 18:00:44 +09:00
Junyoung Choi (Sai)
b584f37087 Merge pull request #1392 from BoostIO/cm-close-brackets
Cm close brackets
2018-01-13 18:00:12 +09:00
Junyoung Choi
5c75644db2 Check upate every day rather than every hour 2018-01-13 17:36:33 +09:00
Junyoung Choi
72d9e3e00b Fix update error
This error was caused by using super old version of
electron-winstaller.
2018-01-13 17:35:23 +09:00
Sosuke Suzuki
b3d3362f34 set autoCloseBrackets to cm defaultConfigurations 2018-01-13 15:37:14 +09:00
Sosuke Suzuki
1cbaf55cee import the addon for autoCloseBrackets 2018-01-13 15:10:18 +09:00
Sosuke Suzuki
7771875d57 Change CreateFolderInput style from raw color code to variable 2018-01-13 11:37:47 +09:00
Kazz Yokomizo
85937d8e23 Merge pull request #1324 from mslourens/confirmation-dialog
show delete confirmation dialog
2018-01-12 09:37:05 +09:00
Sosuke Suzuki
1480986a3a change submit button style 2018-01-11 18:34:00 +09:00
Sosuke Suzuki
bc968736df add colors for SolarizedDark 2018-01-11 18:30:57 +09:00
Kazz Yokomizo
ad80b8e8b6 Merge pull request #1379 from BoostIO/destroy-initmodal
remove initmodal
2018-01-10 23:36:35 +09:00
Sosuke Suzuki
d44d220c55 change default storage name from debugging to production 2018-01-10 19:40:04 +09:00
Sosuke Suzuki
f6bcef0789 remove unnecessary variables 2018-01-10 19:36:45 +09:00
Sosuke Suzuki
a28ec752e8 remove InitModalComponent 2018-01-10 19:36:13 +09:00
Sosuke Suzuki
48ea5746d9 move init function to Main.js 2018-01-10 19:33:58 +09:00
Kazz Yokomizo
f473d31970 Merge pull request #1368 from marcusstenbeck/patch-1
fix(code-editor): use correct prop for font family
2018-01-05 19:06:57 +09:00
Marcus Stenbeck
9cd1d4b466 fix(code-editor): use correct prop for font family 2018-01-05 10:54:48 +01:00
Kazz Yokomizo
4f02065eaf Merge pull request #1354 from trinode/feature-elixir-support
Elixir syntax highlighting
2018-01-04 20:31:45 +09:00
Kazz Yokomizo
4b85e3e8fb Merge pull request #1363 from BoostIO/fix-note-disppaer-bug
fix bug when add tag, disappear note.
2018-01-04 19:57:40 +09:00
Sosuke Suzuki
e4e8c2205e splid update functions 2018-01-04 18:12:27 +09:00
Maurits Lourens
18649dd074 fixed lint errors 2018-01-04 08:32:42 +01:00
Maurits Lourens
9f9463f0e8 fixed review comments 2018-01-03 23:08:42 +01:00
Kazz Yokomizo
6cf9bc5de2 Merge pull request #1361 from BoostIO/fix-ci-errror
Fix CI error at #1147
2018-01-04 04:07:49 +09:00
Kazu Yokomizo
297b4346c5 Fix CI error at #1147 2018-01-04 03:51:49 +09:00
Kazz Yokomizo
767a203439 Merge pull request #1147 from mslourens/export-folder
export folder as md or text
2018-01-04 03:50:24 +09:00
Kazz Yokomizo
c564c253b1 Merge pull request #1302 from mslourens/open-finder
fixed opening finder on Windows and Linux
2018-01-04 03:39:14 +09:00
Anthony Graham
b4e138e21b Elixir Syntax Highlighting 2018-01-01 21:17:29 +00:00
Kazz Yokomizo
8ca01921c4 Merge pull request #1349 from BoostIO/fix-live-preview-indent-bug
fix bug that config of editorIndentSize is not working.
2017-12-30 16:38:58 +09:00
Sosuke Suzuki
c8b97ffde3 pass fontSize and indentSize to CodeEditor as props 2017-12-30 11:04:13 +09:00
Kazz Yokomizo
abc84e5710 Merge pull request #1347 from BoostIO/fix-solarized-dark-infopanel
fix style
2017-12-29 21:56:55 +09:00
Sosuke Suzuki
d732d195f3 fix style 2017-12-29 21:38:40 +09:00
Kazz Yokomizo
789975abb0 Merge pull request #1343 from BoostIO/fix-table-backgroud-color
add table style in solarized-dark-theme
2017-12-28 22:57:39 +09:00
Sosuke Suzuki
ed1eab7fcc add table style in solarized-dark-theme 2017-12-28 22:48:59 +09:00
Kazz Yokomizo
29b31c114a Merge pull request #1337 from BoostIO/update-readme
Update readme
2017-12-28 11:13:48 +09:00
Kazz Yokomizo
c8cf353c21 Merge pull request #1220 from PaulRosset/add-notification-onChangeUi
Add notification on change ui
2017-12-28 10:45:21 +09:00
Kazz Yokomizo
e82e2c71f1 Update readme 2017-12-28 10:44:07 +09:00
Kazz Yokomizo
dd729c406f Merge pull request #1256 from mslourens/export-html
first attempt to export html
2017-12-28 10:38:55 +09:00
Kazz Yokomizo
3127e85900 Merge pull request #1329 from robbawebba/fix/jump-notes-by-link
Fix linking to other notes
2017-12-26 11:51:51 +09:00
Kazz Yokomizo
304eeb3158 Merge pull request #1331 from ncaron/master
Fix starred item count
2017-12-26 11:41:28 +09:00
Niko Caron
bf781c6b50 Fix starred item count
* Deleting a starred item will now update the starred count in the side nav. (delete from starred set)
* Restoring a starred item will add it the the starred set again.

Fix: #1326
2017-12-23 18:17:48 -04:00
Rob Weber
4f7026969f Fix linking to other notes 2017-12-23 01:33:03 -08:00
Kazz Yokomizo
16d264ebfa Merge pull request #1328 from BoostIO/fix-style
Fix style
2017-12-23 15:32:02 +09:00
Kazu Yokomizo
04ffbe945b Delete unnecessary style 2017-12-23 15:25:45 +09:00
Kazu Yokomizo
974a1a1e7d Fix style 2017-12-23 15:18:16 +09:00
Kazz Yokomizo
ca2c07244f Merge pull request #1321 from mslourens/tooltips
added tooltips like the new note button
2017-12-23 15:12:17 +09:00
Maurits Lourens
e262d2f19b fixed lint error 2017-12-21 19:15:34 +01:00
Maurits Lourens
aabfe820ac refactored to prevent duplicate code 2017-12-21 17:41:08 +01:00
Maurits Lourens
3bba5442bd show delete confirmation dialog 2017-12-21 15:57:27 +01:00
Maurits Lourens
6ce1922fb3 added tooltips like the new note button (and consequently extracted some buttons and styles) 2017-12-21 15:23:13 +01:00
Maurits Lourens
9367a404ef converted line-endings back to lf 2017-12-21 09:37:17 +01:00
Maurits Lourens
775200bdd6 fixed opening finder on Windows and Linux
fixes #1291 - tested on Ubuntu Linux Mate and Windows 10
2017-12-15 12:57:18 +01:00
Paul Rosset
795fe8ae1d Add isEqual and changing haveToSaveNotif method 2017-12-14 12:26:05 +00:00
Maurits Lourens
6fba62d062 fixed review comments 2017-12-13 17:20:22 +01:00
Maurits Lourens
5d46adf8fd fixed review comments 2017-12-13 17:11:43 +01:00
Maurits Lourens
8c8a0ab46d forgot to run lint 2017-12-08 16:21:31 +01:00
Maurits Lourens
959b75bddd export folder as md or text 2017-12-08 16:21:31 +01:00
Maurits Lourens
6a9d4ae0fd first attempt to export html 2017-12-08 11:43:50 +01:00
Paul Rosset
cb59458c79 refactor 2017-12-05 18:28:59 +00:00
Paul Rosset
125a493400 Change name for state 2017-12-05 18:24:30 +00:00
Paul Rosset
83910b55d2 Correction eslint code format 2017-12-05 18:18:12 +00:00
Paul Rosset
f4fd131100 Requested Review 2017-12-05 18:16:42 +00:00
Paul Rosset
d1e5781c24 Correction UiTab 2017-12-01 19:03:04 +00:00
Paul Rosset
c86e451198 Merge branch 'master' of github.com:BoostIO/Boostnote into add-notification-onChangeUi 2017-12-01 17:34:16 +00:00
Paul Rosset
b4a7b547f0 Add notification when not saved 2017-12-01 15:23:23 +00:00
57 changed files with 1065 additions and 680 deletions

2
.gitignore vendored
View File

@@ -8,5 +8,5 @@ node_modules/*
/compiled
/secret
*.log
.vscode
.idea
.vscode

View File

@@ -2,6 +2,7 @@ import PropTypes from 'prop-types'
import React from 'react'
import _ from 'lodash'
import CodeMirror from 'codemirror'
import 'codemirror-mode-elixir'
import path from 'path'
import copyImage from 'browser/main/lib/dataApi/copyImage'
import { findStorage } from 'browser/lib/findStorage'
@@ -62,6 +63,7 @@ export default class CodeEditor extends React.Component {
scrollPastEnd: this.props.scrollPastEnd,
inputStyle: 'textarea',
dragDrop: false,
autoCloseBrackets: true,
extraKeys: {
Tab: function (cm) {
const cursor = cm.getCursor()
@@ -249,7 +251,7 @@ export default class CodeEditor extends React.Component {
render () {
const { className, fontSize } = this.props
let fontFamily = this.props.className
let fontFamily = this.props.fontFamily
fontFamily = _.isString(fontFamily) && fontFamily.length > 0
? [fontFamily].concat(defaultEditorFontFamily)
: defaultEditorFontFamily

View File

@@ -3,6 +3,7 @@ import React from 'react'
import markdown from 'browser/lib/markdown'
import _ from 'lodash'
import CodeMirror from 'codemirror'
import 'codemirror-mode-elixir'
import consts from 'browser/lib/consts'
import Raphael from 'raphael'
import flowchart from 'flowchart'
@@ -118,6 +119,7 @@ export default class MarkdownPreview extends React.Component {
this.checkboxClickHandler = (e) => this.handleCheckboxClick(e)
this.saveAsTextHandler = () => this.handleSaveAsText()
this.saveAsMdHandler = () => this.handleSaveAsMd()
this.saveAsHtmlHandler = () => this.handleSaveAsHtml()
this.printHandler = () => this.handlePrint()
this.linkClickHandler = this.handlelinkClick.bind(this)
@@ -176,21 +178,29 @@ export default class MarkdownPreview extends React.Component {
this.exportAsDocument('md')
}
handleSaveAsHtml () {
this.exportAsDocument('html', (value) => {
return this.refs.root.contentWindow.document.documentElement.outerHTML
})
}
handlePrint () {
this.refs.root.contentWindow.print()
}
exportAsDocument (fileType) {
exportAsDocument (fileType, formatter) {
const options = {
filters: [
{ name: 'Documents', extensions: [fileType] }
],
properties: ['openFile', 'createDirectory']
}
const value = formatter ? formatter.call(this, this.props.value) : this.props.value
dialog.showSaveDialog(remote.getCurrentWindow(), options,
(filename) => {
if (filename) {
fs.writeFile(filename, this.props.value, (err) => {
fs.writeFile(filename, value, (err) => {
if (err) throw err
})
}
@@ -226,6 +236,7 @@ export default class MarkdownPreview extends React.Component {
this.refs.root.contentWindow.document.addEventListener('dragover', this.preventImageDroppedHandler)
eventEmitter.on('export:save-text', this.saveAsTextHandler)
eventEmitter.on('export:save-md', this.saveAsMdHandler)
eventEmitter.on('export:save-html', this.saveAsHtmlHandler)
eventEmitter.on('print', this.printHandler)
}
@@ -237,6 +248,7 @@ export default class MarkdownPreview extends React.Component {
this.refs.root.contentWindow.document.removeEventListener('dragover', this.preventImageDroppedHandler)
eventEmitter.off('export:save-text', this.saveAsTextHandler)
eventEmitter.off('export:save-md', this.saveAsMdHandler)
eventEmitter.off('export:save-html', this.saveAsHtmlHandler)
eventEmitter.off('print', this.printHandler)
}

View File

@@ -45,6 +45,10 @@ class MarkdownSplitEditor extends React.Component {
render () {
const { config, value, storageKey } = this.props
const storage = findStorage(storageKey)
let editorFontSize = parseInt(config.editor.fontSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
let editorIndentSize = parseInt(config.editor.indentSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4
const previewStyle = {}
if (this.props.ignorePreviewPointerEvents) previewStyle.pointerEvents = 'none'
return (
@@ -57,7 +61,9 @@ class MarkdownSplitEditor extends React.Component {
theme={config.editor.theme}
keyMap={config.editor.keyMap}
fontFamily={config.editor.fontFamily}
fontSize={editorFontSize}
indentType={config.editor.indentType}
indentSize={editorIndentSize}
scrollPastEnd={config.editor.scrollPastEnd}
storageKey={storageKey}
onChange={this.handleOnChange.bind(this)}

View File

@@ -336,8 +336,29 @@ body[data-theme="dark"]
background-color themeDarkBorder
color themeDarkText
themeSolarizedDarkTableOdd = $ui-solarized-dark-noteDetail-backgroundColor
themeSolarizedDarkTableEven = darken($ui-solarized-dark-noteDetail-backgroundColor, 10%)
themeSolarizedDarkTableHead = themeSolarizedDarkTableEven
themeSolarizedDarkTableBorder = themeDarkBorder
body[data-theme="solarized-dark"]
color $ui-solarized-dark-text-color
border-color themeDarkBorder
background-color $ui-solarized-dark-noteDetail-backgroundColor
table
thead
tr
background-color themeSolarizedDarkTableHead
th
border-color themeSolarizedDarkTableBorder
&:last-child
border-right solid 1px themeSolarizedDarkTableBorder
tbody
tr:nth-child(2n + 1)
background-color themeSolarizedDarkTableOdd
tr:nth-child(2n)
background-color themeSolarizedDarkTableEven
td
border-color themeSolarizedDarkTableBorder
&:last-child
border-right solid 1px themeSolarizedDarkTableBorder

View File

@@ -5,6 +5,7 @@ import MarkdownPreview from 'browser/components/MarkdownPreview'
import MarkdownEditor from 'browser/components/MarkdownEditor'
import CodeEditor from 'browser/components/CodeEditor'
import CodeMirror from 'codemirror'
import 'codemirror-mode-elixir'
import { findStorage } from 'browser/lib/findStorage'
const electron = require('electron')

View File

@@ -1,3 +1,5 @@
import CodeMirror from 'codemirror'
import 'codemirror-mode-elixir'
CodeMirror.modeInfo.push({name: 'Stylus', mime: 'text/x-styl', mode: 'stylus', ext: ['styl'], alias: ['styl']})
CodeMirror.modeInfo.push({name: 'Elixir', mime: 'text/x-elixir', mode: 'elixir', ext: ['ex']})

View File

@@ -0,0 +1,19 @@
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './FullscreenButton.styl'
const FullscreenButton = ({
onClick
}) => (
<button styleName='control-fullScreenButton' title='Fullscreen' onMouseDown={(e) => onClick(e)}>
<img styleName='iconInfo' src='../resources/icon/icon-full.svg' />
<span styleName='tooltip'>Fullscreen</span>
</button>
)
FullscreenButton.propTypes = {
onClick: PropTypes.func.isRequired
}
export default CSSModules(FullscreenButton, styles)

View File

@@ -0,0 +1,22 @@
.control-fullScreenButton
top 80px
topBarButtonRight()
&:hover .tooltip
opacity 1
.tooltip
tooltip()
position absolute
pointer-events none
top 26px
right 0
z-index 200
padding 5px
line-height normal
border-radius 2px
opacity 0
transition 0.1s
body[data-theme="dark"]
.control-fullScreenButton
topBarButtonDark()

View File

@@ -10,6 +10,7 @@ const InfoButton = ({
onClick={(e) => onClick(e)}
>
<img className='infoButton' src='../resources/icon/icon-info.svg' />
<span styleName='tooltip'>Info</span>
</button>
)

View File

@@ -1,6 +1,21 @@
.control-infoButton
top 10px
topBarButtonRight()
&:hover .tooltip
opacity 1
.tooltip
tooltip()
position absolute
pointer-events none
top 26px
right 0
z-index 200
padding 5px
line-height normal
border-radius 2px
opacity 0
transition 0.1s
.infoButton
padding 0px

View File

@@ -4,7 +4,7 @@ import CSSModules from 'browser/lib/CSSModules'
import styles from './InfoPanel.styl'
const InfoPanel = ({
storageName, folderName, noteLink, updatedAt, createdAt, exportAsMd, exportAsTxt, wordCount, letterCount, type, print
storageName, folderName, noteLink, updatedAt, createdAt, exportAsMd, exportAsTxt, exportAsHtml, wordCount, letterCount, type, print
}) => (
<div className='infoPanel' styleName='control-infoButton-panel' style={{display: 'none'}}>
<div>
@@ -57,17 +57,22 @@ const InfoPanel = ({
<div id='export-wrap'>
<button styleName='export--enable' onClick={(e) => exportAsMd(e)}>
<i className='fa fa-file-code-o fa-fw' />
<i className='fa fa-file-code-o' />
<p>.md</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsTxt(e)}>
<i className='fa fa-file-text-o fa-fw' />
<i className='fa fa-file-text-o' />
<p>.txt</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsHtml(e)}>
<i className='fa fa-html5' />
<p>.html</p>
</button>
<button styleName='export--enable' onClick={(e) => print(e)}>
<i className='fa fa-print fa-fw' />
<i className='fa fa-print' />
<p>Print</p>
</button>
</div>
@@ -82,6 +87,7 @@ InfoPanel.propTypes = {
createdAt: PropTypes.string.isRequired,
exportAsMd: PropTypes.func.isRequired,
exportAsTxt: PropTypes.func.isRequired,
exportAsHtml: PropTypes.func.isRequired,
wordCount: PropTypes.number,
letterCount: PropTypes.number,
type: PropTypes.string.isRequired,

View File

@@ -161,3 +161,43 @@ body[data-theme="dark"]
color $ui-dark-inactive-text-color
&:hover
color $ui-dark-text-color
body[data-theme="solarized-dark"]
.control-infoButton-panel
background-color $ui-solarized-dark-noteList-backgroundColor
.control-infoButton-panel-trash
background-color $ui-solarized-ark-noteList-backgroundColor
.modification-date
color $ui-solarized-ark-text-color
.modification-date-desc
color $ui-inactive-text-color
.infoPanel-defaul-count
color $ui-solarized-dark-text-color
.infoPanel-sub-count
color $ui-inactive-text-color
.infoPanel-default
color $ui-solarized-ark-text-color
.infoPanel-sub
color $ui-inactive-text-color
.infoPanel-noteLink
background-color alpha($ui-solarized-dark-borderColor, 20%)
color $ui-solarized-dark-text-color
[id=export-wrap]
button
color $ui-dark-inactive-text-color
&:hover
background-color alpha($ui-solarized-dark-borderColor, 20%)
color $ui-solarized-ark-text-color
p
color $ui-dark-inactive-text-color
&:hover
color $ui-solarized-ark-text-color

View File

@@ -4,7 +4,7 @@ import CSSModules from 'browser/lib/CSSModules'
import styles from './InfoPanel.styl'
const InfoPanelTrashed = ({
storageName, folderName, updatedAt, createdAt, exportAsMd, exportAsTxt
storageName, folderName, updatedAt, createdAt, exportAsMd, exportAsTxt, exportAsHtml
}) => (
<div className='infoPanel' styleName='control-infoButton-panel-trash' style={{display: 'none'}}>
<div>
@@ -31,17 +31,22 @@ const InfoPanelTrashed = ({
<div id='export-wrap'>
<button styleName='export--enable' onClick={(e) => exportAsMd(e)}>
<i className='fa fa-file-code-o fa-fw' />
<i className='fa fa-file-code-o' />
<p>.md</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsTxt(e)}>
<i className='fa fa-file-text-o fa-fw' />
<i className='fa fa-file-text-o' />
<p>.txt</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsHtml(e)}>
<i className='fa fa-html5' />
<p>.html</p>
</button>
<button styleName='export--unable'>
<i className='fa fa-file-pdf-o fa-fw' />
<i className='fa fa-file-pdf-o' />
<p>.pdf</p>
</button>
</div>
@@ -54,7 +59,8 @@ InfoPanelTrashed.propTypes = {
updatedAt: PropTypes.string.isRequired,
createdAt: PropTypes.string.isRequired,
exportAsMd: PropTypes.func.isRequired,
exportAsTxt: PropTypes.func.isRequired
exportAsTxt: PropTypes.func.isRequired,
exportAsHtml: PropTypes.func.isRequired
}
export default CSSModules(InfoPanelTrashed, styles)

View File

@@ -18,18 +18,16 @@ import { findNoteTitle } from 'browser/lib/findNoteTitle'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
import ConfigManager from 'browser/main/lib/ConfigManager'
import TrashButton from './TrashButton'
import FullscreenButton from './FullscreenButton'
import PermanentDeleteButton from './PermanentDeleteButton'
import InfoButton from './InfoButton'
import ToggleModeButton from './ToggleModeButton'
import InfoPanel from './InfoPanel'
import InfoPanelTrashed from './InfoPanelTrashed'
import { formatDate } from 'browser/lib/date-formatter'
import { getTodoPercentageOfCompleted } from 'browser/lib/getTodoStatus'
import striptags from 'striptags'
const electron = require('electron')
const { remote } = electron
const { dialog } = remote
class MarkdownNoteDetail extends React.Component {
constructor (props) {
super(props)
@@ -77,17 +75,22 @@ class MarkdownNoteDetail extends React.Component {
ee.off('topbar:togglelockbutton', this.toggleLockButton)
}
handleChange (e) {
handleUpdateTag () {
const { note } = this.state
note.content = this.refs.content.value
if (this.refs.tags) note.tags = this.refs.tags.value
note.title = markdown.strip(striptags(findNoteTitle(note.content)))
note.updatedAt = new Date()
this.updateNote(note)
}
this.setState({
note
}, () => {
handleUpdateContent () {
const { note } = this.state
note.content = this.refs.content.value
note.title = markdown.strip(striptags(findNoteTitle(note.content)))
this.updateNote(note)
}
updateNote (note) {
note.updatedAt = new Date()
this.setState({note}, () => {
this.save()
})
}
@@ -173,18 +176,17 @@ class MarkdownNoteDetail extends React.Component {
ee.emit('export:save-text')
}
exportAsHtml () {
ee.emit('export:save-html')
}
handleTrashButtonClick (e) {
const { note } = this.state
const { isTrashed } = note
const { confirmDeletion } = this.props
if (isTrashed) {
const dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Confirm note deletion',
detail: 'This will permanently remove this note.',
buttons: ['Confirm', 'Cancel']
})
if (dialogueButtonIndex === 1) return
if (confirmDeletion(true)) {
const {note, dispatch} = this.props
dataApi
.deleteNote(note.storage, note.key)
@@ -198,7 +200,9 @@ class MarkdownNoteDetail extends React.Component {
}
ee.once('list:moved', dispatchHandler)
})
}
} else {
if (confirmDeletion()) {
note.isTrashed = true
this.setState({
@@ -206,9 +210,11 @@ class MarkdownNoteDetail extends React.Component {
}, () => {
this.save()
})
}
ee.emit('list:next')
}
}
}
handleUndoButtonClick (e) {
const { note } = this.state
@@ -283,7 +289,7 @@ class MarkdownNoteDetail extends React.Component {
config={config}
value={note.content}
storageKey={note.storage}
onChange={(e) => this.handleChange(e)}
onChange={this.handleUpdateContent.bind(this)}
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
/>
} else {
@@ -292,7 +298,7 @@ class MarkdownNoteDetail extends React.Component {
config={config}
value={note.content}
storageKey={note.storage}
onChange={(e) => this.handleChange(e)}
onChange={this.handleUpdateContent.bind(this)}
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
/>
}
@@ -332,6 +338,7 @@ class MarkdownNoteDetail extends React.Component {
folderName={currentOption.folder.name}
updatedAt={formatDate(note.updatedAt)}
createdAt={formatDate(note.createdAt)}
exportAsHtml={this.exportAsHtml}
exportAsMd={this.exportAsMd}
exportAsTxt={this.exportAsTxt}
/>
@@ -352,21 +359,12 @@ class MarkdownNoteDetail extends React.Component {
<TagSelect
ref='tags'
value={this.state.note.tags}
onChange={(e) => this.handleChange(e)}
onChange={this.handleUpdateTag.bind(this)}
/>
<div styleName='mode-tab'>
<div styleName={editorType === 'SPLIT' ? 'active' : 'non-active'} onClick={() => this.handleSwitchMode('SPLIT')}>
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '../resources/icon/icon-mode-split-on.svg' : '../resources/icon/icon-mode-split-on-active.svg'} />
</div>
<div styleName={editorType === 'EDITOR_PREVIEW' ? 'active' : 'non-active'} onClick={() => this.handleSwitchMode('EDITOR_PREVIEW')}>
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '../resources/icon/icon-mode-markdown-off-active.svg' : '../resources/icon/icon-mode-markdown-off.svg'} />
</div>
</div>
<ToggleModeButton onClick={(e) => this.handleSwitchMode(e)} editorType={editorType} />
<TodoListPercentage
percentageOfTodo={getTodoPercentageOfCompleted(note.content)}
/>
<TodoListPercentage percentageOfTodo={getTodoPercentageOfCompleted(note.content)} />
</div>
<div styleName='info-right'>
<InfoButton
@@ -393,11 +391,7 @@ class MarkdownNoteDetail extends React.Component {
)
})()}
<button styleName='control-fullScreenButton'
onMouseDown={(e) => this.handleFullScreenButton(e)}
>
<img styleName='iconInfo' src='../resources/icon/icon-full.svg' />
</button>
<FullscreenButton onClick={(e) => this.handleFullScreenButton(e)} />
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
@@ -409,6 +403,7 @@ class MarkdownNoteDetail extends React.Component {
createdAt={formatDate(note.createdAt)}
exportAsMd={this.exportAsMd}
exportAsTxt={this.exportAsTxt}
exportAsHtml={this.exportAsHtml}
wordCount={note.content.split(' ').length}
letterCount={note.content.replace(/\r?\n/g, '').length}
type={note.type}
@@ -447,7 +442,8 @@ MarkdownNoteDetail.propTypes = {
style: PropTypes.shape({
left: PropTypes.number
}),
ignorePreviewPointerEvents: PropTypes.bool
ignorePreviewPointerEvents: PropTypes.bool,
confirmDeletion: PropTypes.bool.isRequired
}
export default CSSModules(MarkdownNoteDetail, styles)

View File

@@ -19,10 +19,6 @@
top 40px
position relative
.control-fullScreenButton
top 80px
topBarButtonRight()
.body
absolute left right
left 0
@@ -33,26 +29,6 @@
.body-noteEditor
absolute top bottom left right
.mode-tab
border 1px solid #eee
height 34px
display flex
align-items center
div
width 40px
height 100%
background-color #f9f9f9
display flex
align-items center
justify-content center
cursor pointer
&:first-child
border-right 1px solid #eee
.active
background-color #fff
box-shadow 2px 0px 7px #eee
z-index 1
body[data-theme="white"]
.root
box-shadow $note-detail-box-shadow
@@ -73,27 +49,8 @@ body[data-theme="dark"]
.control-fullScreenButton
topBarButtonDark()
.mode-tab
border 1px solid #444444
div
background-color $ui-dark-noteDetail-backgroundColor
&:first-child
border-right 1px solid #444444
.active
background-color #3A404C
box-shadow 2px 0px 7px #444444
body[data-theme="solarized-dark"]
.root
border-left 1px solid $ui-solarized-dark-borderColor
background-color $ui-solarized-dark-noteDetail-backgroundColor
.mode-tab
border 1px solid #586E75
div
background-color $ui-solarized-dark-noteDetail-backgroundColor
&:first-child
border-right 1px solid #586E75
.active
background-color #002B36
box-shadow 2px 0px 7px #222222

View File

@@ -10,6 +10,7 @@ const PermanentDeleteButton = ({
onClick={(e) => onClick(e)}
>
<img styleName='iconInfo' src='../resources/icon/icon-trash.svg' />
<span styleName='tooltip'>Permanent Delete</span>
</button>
)

View File

@@ -11,6 +11,7 @@ import dataApi from 'browser/main/lib/dataApi'
import { hashHistory } from 'react-router'
import ee from 'browser/main/lib/eventEmitter'
import CodeMirror from 'codemirror'
import 'codemirror-mode-elixir'
import SnippetTab from 'browser/components/SnippetTab'
import StatusBar from '../StatusBar'
import context from 'browser/lib/context'
@@ -175,15 +176,10 @@ class SnippetNoteDetail extends React.Component {
handleTrashButtonClick (e) {
const { note } = this.state
const { isTrashed } = note
const { confirmDeletion } = this.props
if (isTrashed) {
const dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Confirm note deletion',
detail: 'This will permanently remove this note.',
buttons: ['Confirm', 'Cancel']
})
if (dialogueButtonIndex === 1) return
if (confirmDeletion(true)) {
const {note, dispatch} = this.props
dataApi
.deleteNote(note.storage, note.key)
@@ -197,7 +193,9 @@ class SnippetNoteDetail extends React.Component {
}
ee.once('list:moved', dispatchHandler)
})
}
} else {
if (confirmDeletion()) {
note.isTrashed = true
this.setState({
@@ -205,9 +203,11 @@ class SnippetNoteDetail extends React.Component {
}, () => {
this.save()
})
}
ee.emit('list:next')
}
}
}
handleUndoButtonClick (e) {
const { note } = this.state
@@ -380,7 +380,7 @@ class SnippetNoteDetail extends React.Component {
handleModeButtonClick (e, index) {
const menu = new Menu()
CodeMirror.modeInfo.forEach((mode) => {
CodeMirror.modeInfo.sort(function (a, b) { return a.name.localeCompare(b.name) }).forEach((mode) => {
menu.append(new MenuItem({
label: mode.name,
click: (e) => this.handleModeOptionClick(index, mode.name)(e)
@@ -603,6 +603,7 @@ class SnippetNoteDetail extends React.Component {
createdAt={formatDate(note.createdAt)}
exportAsMd={this.showWarning}
exportAsTxt={this.showWarning}
exportAsHtml={this.showWarning}
/>
</div>
</div>
@@ -634,7 +635,7 @@ class SnippetNoteDetail extends React.Component {
isActive={note.isStarred}
/>
<button styleName='control-fullScreenButton'
<button styleName='control-fullScreenButton' title='Fullscreen'
onMouseDown={(e) => this.handleFullScreenButton(e)}>
<img styleName='iconInfo' src='../resources/icon/icon-sidebar.svg' />
</button>
@@ -729,7 +730,8 @@ SnippetNoteDetail.propTypes = {
style: PropTypes.shape({
left: PropTypes.number
}),
ignorePreviewPointerEvents: PropTypes.bool
ignorePreviewPointerEvents: PropTypes.bool,
confirmDeletion: PropTypes.bool.isRequired
}
export default CSSModules(SnippetNoteDetail, styles)

View File

@@ -46,14 +46,14 @@ class StarButton extends React.Component {
onMouseDown={(e) => this.handleMouseDown(e)}
onMouseUp={(e) => this.handleMouseUp(e)}
onMouseLeave={(e) => this.handleMouseLeave(e)}
onClick={this.props.onClick}
>
onClick={this.props.onClick}>
<img styleName='icon'
src={this.state.isActive || this.props.isActive
? '../resources/icon/icon-starred.svg'
: '../resources/icon/icon-star.svg'
}
/>
<span styleName='tooltip'>Star</span>
</button>
)
}

View File

@@ -4,6 +4,22 @@
&:hover
transition 0.2s
color alpha($ui-favorite-star-button-color, 0.6)
&:hover .tooltip
opacity 1
.tooltip
tooltip()
position absolute
pointer-events none
top 26px
right 0
width 100%
z-index 200
padding 5px
line-height normal
border-radius 2px
opacity 0
transition 0.1s
.root--active
@extend .root

View File

@@ -0,0 +1,25 @@
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './ToggleModeButton.styl'
const ToggleModeButton = ({
onClick, editorType
}) => (
<div styleName='control-toggleModeButton'>
<div styleName={editorType === 'SPLIT' ? 'active' : 'non-active'} onClick={() => onClick('SPLIT')}>
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '../resources/icon/icon-mode-split-on.svg' : '../resources/icon/icon-mode-split-on-active.svg'} />
</div>
<div styleName={editorType === 'EDITOR_PREVIEW' ? 'active' : 'non-active'} onClick={() => onClick('EDITOR_PREVIEW')}>
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '../resources/icon/icon-mode-markdown-off-active.svg' : '../resources/icon/icon-mode-markdown-off.svg'} />
</div>
<span styleName='tooltip'>Toggle Mode</span>
</div>
)
ToggleModeButton.propTypes = {
onClick: PropTypes.func.isRequired,
editorType: PropTypes.string.Required
}
export default CSSModules(ToggleModeButton, styles)

View File

@@ -0,0 +1,61 @@
.control-toggleModeButton
border 1px solid #eee
height 34px
display flex
align-items center
div
width 40px
height 100%
background-color #f9f9f9
display flex
align-items center
justify-content center
cursor pointer
&:first-child
border-right 1px solid #eee
.active
background-color #fff
box-shadow 2px 0px 7px #eee
z-index 1
&:hover .tooltip
opacity 1
.tooltip
tooltip()
position absolute
pointer-events none
top 47px
right 11px
z-index 200
padding 5px
line-height normal
border-radius 2px
opacity 0
transition 0.1s
body[data-theme="dark"]
.control-fullScreenButton
topBarButtonDark()
.control-toggleModeButton
border 1px solid #444444
div
background-color $ui-dark-noteDetail-backgroundColor
&:first-child
border-right 1px solid #444444
.active
background-color #3A404C
box-shadow 2px 0px 7px #444444
body[data-theme="solarized-dark"]
.control-toggleModeButton
border 1px solid #586E75
div
background-color $ui-solarized-dark-noteDetail-backgroundColor
&:first-child
border-right 1px solid #586E75
.active
background-color #002B36
box-shadow 2px 0px 7px #222222

View File

@@ -10,6 +10,7 @@ const TrashButton = ({
onClick={(e) => onClick(e)}
>
<img styleName='iconInfo' src='../resources/icon/icon-trash.svg' />
<span styleName='tooltip'>Trash</span>
</button>
)

View File

@@ -1,6 +1,21 @@
.control-trashButton
top 115px
topBarButtonRight()
&:hover .tooltip
opacity 1
.tooltip
tooltip()
position absolute
pointer-events none
top 26px
right 0
z-index 200
padding 5px
line-height normal
border-radius 2px
opacity 0
transition 0.1s
.control-trashButton--in-trash
top 60px

View File

@@ -32,6 +32,26 @@ class Detail extends React.Component {
ee.off('detail:delete', this.deleteHandler)
}
confirmDeletion (permanent) {
if (this.props.config.ui.confirmDeletion || permanent) {
const electron = require('electron')
const { remote } = electron
const { dialog } = remote
const alertConfig = {
type: 'warning',
message: 'Confirm note deletion',
detail: 'This will permanently remove this note.',
buttons: ['Confirm', 'Cancel']
}
const dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), alertConfig)
return dialogueButtonIndex === 0
}
return true
}
render () {
const { location, data, config } = this.props
let note = null
@@ -64,6 +84,7 @@ class Detail extends React.Component {
<SnippetNoteDetail
note={note}
config={config}
confirmDeletion={(permanent) => this.confirmDeletion(permanent)}
ref='root'
{..._.pick(this.props, [
'dispatch',
@@ -80,6 +101,7 @@ class Detail extends React.Component {
<MarkdownNoteDetail
note={note}
config={config}
confirmDeletion={(permanent) => this.confirmDeletion(permanent)}
ref='root'
{..._.pick(this.props, [
'dispatch',

View File

@@ -10,10 +10,13 @@ import Detail from './Detail'
import dataApi from 'browser/main/lib/dataApi'
import _ from 'lodash'
import ConfigManager from 'browser/main/lib/ConfigManager'
import modal from 'browser/main/lib/modal'
import InitModal from 'browser/main/modals/InitModal'
import mobileAnalytics from 'browser/main/lib/AwsMobileAnalyticsConfig'
import eventEmitter from 'browser/main/lib/eventEmitter'
import { hashHistory } from 'react-router'
import store from 'browser/main/store'
const path = require('path')
const electron = require('electron')
const { remote } = electron
class Main extends React.Component {
@@ -48,6 +51,91 @@ class Main extends React.Component {
}
}
init () {
dataApi
.addStorage({
name: 'My Storage',
path: path.join(remote.app.getPath('home'), 'Boostnote')
})
.then((data) => {
return data
})
.then((data) => {
if (data.storage.folders[0] != null) {
return data
} else {
return dataApi
.createFolder(data.storage.key, {
color: '#1278BD',
name: 'Default'
})
.then((_data) => {
return {
storage: _data.storage,
notes: data.notes
}
})
}
})
.then((data) => {
console.log(data)
store.dispatch({
type: 'ADD_STORAGE',
storage: data.storage,
notes: data.notes
})
const defaultSnippetNote = dataApi
.createNote(data.storage.key, {
type: 'SNIPPET_NOTE',
folder: data.storage.folders[0].key,
title: 'Snippet note example',
description: 'Snippet note example\nYou can store a series of snippets as a single note, like Gist.',
snippets: [
{
name: 'example.html',
mode: 'html',
content: '<html>\n<body>\n<h1 id=\'hello\'>Enjoy Boostnote!</h1>\n</body>\n</html>'
},
{
name: 'example.js',
mode: 'javascript',
content: 'var boostnote = document.getElementById(\'enjoy\').innerHTML\n\nconsole.log(boostnote)'
}
]
})
.then((note) => {
store.dispatch({
type: 'UPDATE_NOTE',
note: note
})
})
const defaultMarkdownNote = dataApi
.createNote(data.storage.key, {
type: 'MARKDOWN_NOTE',
folder: data.storage.folders[0].key,
title: 'Welcome to Boostnote!',
content: '# Welcome to Boostnote!\n## Click here to edit markdown :wave:\n\n<iframe width="560" height="315" src="https://www.youtube.com/embed/L0qNPLsvmyM" frameborder="0" allowfullscreen></iframe>\n\n## Docs :memo:\n- [Boostnote | Boost your happiness, productivity and creativity.](https://hackernoon.com/boostnote-boost-your-happiness-productivity-and-creativity-315034efeebe)\n- [Cloud Syncing & Backups](https://github.com/BoostIO/Boostnote/wiki/Cloud-Syncing-and-Backup)\n- [How to sync your data across Desktop and Mobile apps](https://github.com/BoostIO/Boostnote/wiki/Sync-Data-Across-Desktop-and-Mobile-apps)\n- [Convert data from **Evernote** to Boostnote.](https://github.com/BoostIO/Boostnote/wiki/Evernote)\n- [Keyboard Shortcuts](https://github.com/BoostIO/Boostnote/wiki/Keyboard-Shortcuts)\n- [Keymaps in Editor mode](https://github.com/BoostIO/Boostnote/wiki/Keymaps-in-Editor-mode)\n- [How to set syntax highlight in Snippet note](https://github.com/BoostIO/Boostnote/wiki/Syntax-Highlighting)\n\n---\n\n## Article Archive :books:\n- [Reddit English](http://bit.ly/2mOJPu7)\n- [Reddit Spanish](https://www.reddit.com/r/boostnote_es/)\n- [Reddit Chinese](https://www.reddit.com/r/boostnote_cn/)\n- [Reddit Japanese](https://www.reddit.com/r/boostnote_jp/)\n\n---\n\n## Community :beers:\n- [GitHub](http://bit.ly/2AWWzkD)\n- [Twitter](http://bit.ly/2z8BUJZ)\n- [Facebook Group](http://bit.ly/2jcca8t)'
})
.then((note) => {
store.dispatch({
type: 'UPDATE_NOTE',
note: note
})
})
return Promise.resolve(defaultSnippetNote)
.then(defaultMarkdownNote)
.then(() => data.storage)
})
.then((storage) => {
hashHistory.push('/storages/' + storage.key)
})
.catch((err) => {
throw err
})
}
componentDidMount () {
const { dispatch, config } = this.props
@@ -71,7 +159,7 @@ class Main extends React.Component {
})
if (data.storages.length < 1) {
modal.open(InitModal)
this.init()
}
})

View File

@@ -235,18 +235,8 @@ class NoteList extends React.Component {
return
}
const { router } = this.context
const { location } = this.props
let targetIndex = this.getTargetIndex()
if (targetIndex < 0) targetIndex = 0
const selectedNoteKeys = []
const nextNoteKey = this.getNoteKeyFromTargetIndex(targetIndex)
selectedNoteKeys.push(nextNoteKey)
this.focusNote(selectedNoteKeys, nextNoteKey)
const selectedNoteKeys = [noteHash]
this.focusNote(selectedNoteKeys, noteHash)
ee.emit('list:moved')
}

View File

@@ -0,0 +1,24 @@
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './SwitchButton.styl'
const ListButton = ({
onClick, isTagActive
}) => (
<button styleName={isTagActive ? 'non-active-button' : 'active-button'} onClick={onClick}>
<img src={isTagActive
? '../resources/icon/icon-list.svg'
: '../resources/icon/icon-list-active.svg'
}
/>
<span styleName='tooltip'>Notes</span>
</button>
)
ListButton.propTypes = {
onClick: PropTypes.func.isRequired,
isTagActive: PropTypes.bool.isRequired
}
export default CSSModules(ListButton, styles)

View File

@@ -0,0 +1,19 @@
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './PreferenceButton.styl'
const PreferenceButton = ({
onClick
}) => (
<button styleName='top-menu-preference' onClick={(e) => onClick(e)}>
<img styleName='iconTag' src='../resources/icon/icon-setting.svg' />
<span styleName='tooltip'>Preferences</span>
</button>
)
PreferenceButton.propTypes = {
onClick: PropTypes.func.isRequired
}
export default CSSModules(PreferenceButton, styles)

View File

@@ -0,0 +1,51 @@
.top-menu-preference
navButtonColor()
position absolute
top 22px
right 10px
width 2em
background-color transparent
&:hover
color $ui-button-default--active-backgroundColor
background-color transparent
.tooltip
opacity 1
&:active, &:active:hover
color $ui-button-default--active-backgroundColor
body[data-theme="white"]
.top-menu-preference
navWhiteButtonColor()
background-color transparent
&:hover
color #0B99F1
background-color transparent
&:active, &:active:hover
color #0B99F1
background-color transparent
body[data-theme="dark"]
.top-menu-preference
navDarkButtonColor()
background-color transparent
&:active
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
background-color transparent
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
background-color transparent
.tooltip
tooltip()
position absolute
pointer-events none
top 26px
left -20px
z-index 200
padding 5px
line-height normal
border-radius 2px
opacity 0
transition 0.1s

View File

@@ -11,19 +11,6 @@
.top
padding-bottom 15px
.top-menu-preference
navButtonColor()
position absolute
top 22px
right 10px
width 2em
background-color transparent
&:hover
color $ui-button-default--active-backgroundColor
background-color transparent
&:active, &:active:hover
color $ui-button-default--active-backgroundColor
.switch-buttons
background-color transparent
border 0
@@ -31,21 +18,7 @@
display flex
text-align center
.non-active-button
color $ui-inactive-text-color
font-size 16px
border 0
background-color transparent
transition 0.2s
display flex
text-align center
margin-right 4px;
&:hover
color alpha(#239F86, 60%)
.active-button
@extend .non-active-button
color $ui-button-default--active-backgroundColor
.top-menu-label
margin-left 5px
@@ -109,33 +82,6 @@ body[data-theme="white"]
background-color #f9f9f9
color $ui-text-color
.top-menu-preference
navWhiteButtonColor()
background-color transparent
&:hover
color #0B99F1
background-color transparent
&:active, &:active:hover
color #0B99F1
background-color transparent
.non-active-button
color $ui-inactive-text-color
&:hover
color alpha(#0B99F1, 60%)
.tag-title
p
color $ui-text-color
.non-active-button
&:hover
color alpha(#0B99F1, 60%)
.active-button
@extend .non-active-button
color #0B99F1
body[data-theme="dark"]
.root, .root--folded
border-right 1px solid $ui-dark-borderColor
@@ -145,25 +91,6 @@ body[data-theme="dark"]
.top
border-color $ui-dark-borderColor
.top-menu-preference
navDarkButtonColor()
background-color transparent
&:active
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
background-color transparent
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
background-color transparent
.non-active-button
color alpha($ui-dark-text-color, 60%)
&:hover
color alpha(#0B99F1, 60%)
.tag-title
p
color alpha($ui-dark-text-color, 60%)
body[data-theme="solarized-dark"]
.root, .root--folded
background-color $ui-solarized-dark-backgroundColor

View File

@@ -10,6 +10,7 @@ import dataApi from 'browser/main/lib/dataApi'
import StorageItemChild from 'browser/components/StorageItem'
import eventEmitter from 'browser/main/lib/eventEmitter'
import _ from 'lodash'
import * as path from 'path'
const { remote } = require('electron')
const { Menu, MenuItem, dialog } = remote
@@ -24,18 +25,20 @@ class StorageItem extends React.Component {
}
handleHeaderContextMenu (e) {
const menu = new Menu()
menu.append(new MenuItem({
const menu = Menu.buildFromTemplate([
{
label: 'Add Folder',
click: (e) => this.handleAddFolderButtonClick(e)
}))
menu.append(new MenuItem({
},
{
type: 'separator'
}))
menu.append(new MenuItem({
},
{
label: 'Unlink Storage',
click: (e) => this.handleUnlinkStorageClick(e)
}))
}
])
menu.popup()
}
@@ -89,18 +92,36 @@ class StorageItem extends React.Component {
}
handleFolderButtonContextMenu (e, folder) {
const menu = new Menu()
menu.append(new MenuItem({
const menu = Menu.buildFromTemplate([
{
label: 'Rename Folder',
click: (e) => this.handleRenameFolderClick(e, folder)
}))
menu.append(new MenuItem({
},
{
type: 'separator'
}))
menu.append(new MenuItem({
},
{
label: 'Export Folder',
submenu: [
{
label: 'Export as txt',
click: (e) => this.handleExportFolderClick(e, folder, 'txt')
},
{
label: 'Export as md',
click: (e) => this.handleExportFolderClick(e, folder, 'md')
}
]
},
{
type: 'separator'
},
{
label: 'Delete Folder',
click: (e) => this.handleFolderDeleteClick(e, folder)
}))
}
])
menu.popup()
}
@@ -112,6 +133,31 @@ class StorageItem extends React.Component {
})
}
handleExportFolderClick (e, folder, fileType) {
const options = {
properties: ['openDirectory', 'createDirectory'],
buttonLabel: 'Select directory',
title: 'Select a folder to export the files to',
multiSelections: false
}
dialog.showOpenDialog(remote.getCurrentWindow(), options,
(paths) => {
if (paths && paths.length === 1) {
const { storage, dispatch } = this.props
dataApi
.exportFolder(storage.key, folder.key, fileType, paths[0])
.then((data) => {
dispatch({
type: 'EXPORT_FOLDER',
storage: data.storage,
folderKey: data.folderKey,
fileType: data.fileType
})
})
}
})
}
handleFolderDeleteClick (e, folder) {
const index = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',

View File

@@ -0,0 +1,59 @@
.non-active-button
color $ui-inactive-text-color
font-size 16px
border 0
background-color transparent
transition 0.2s
display flex
text-align center
margin-right 4px
position relative
&:hover
color alpha(#239F86, 60%)
.tooltip
opacity 1
.active-button
@extend .non-active-button
color $ui-button-default--active-backgroundColor
.tooltip
tooltip()
position absolute
pointer-events none
top 22px
left -2px
z-index 200
padding 5px
line-height normal
border-radius 2px
opacity 0
transition 0.1s
body[data-theme="white"]
.non-active-button
color $ui-inactive-text-color
&:hover
color alpha(#0B99F1, 60%)
.tag-title
p
color $ui-text-color
.non-active-button
&:hover
color alpha(#0B99F1, 60%)
.active-button
@extend .non-active-button
color #0B99F1
body[data-theme="dark"]
.non-active-button
color alpha($ui-dark-text-color, 60%)
&:hover
color alpha(#0B99F1, 60%)
.tag-title
p
color alpha($ui-dark-text-color, 60%)

View File

@@ -0,0 +1,24 @@
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './SwitchButton.styl'
const TagButton = ({
onClick, isTagActive
}) => (
<button styleName={isTagActive ? 'active-button' : 'non-active-button'} onClick={onClick}>
<img src={isTagActive
? '../resources/icon/icon-tag-active.svg'
: '../resources/icon/icon-tag.svg'
}
/>
<span styleName='tooltip'>Tags</span>
</button>
)
TagButton.propTypes = {
onClick: PropTypes.func.isRequired,
isTagActive: PropTypes.bool.isRequired
}
export default CSSModules(TagButton, styles)

View File

@@ -11,6 +11,9 @@ import SideNavFilter from 'browser/components/SideNavFilter'
import StorageList from 'browser/components/StorageList'
import NavToggleButton from 'browser/components/NavToggleButton'
import EventEmitter from 'browser/main/lib/eventEmitter'
import PreferenceButton from './PreferenceButton'
import ListButton from './ListButton'
import TagButton from './TagButton'
class SideNav extends React.Component {
// TODO: should not use electron stuff v0.7
@@ -162,27 +165,11 @@ class SideNav extends React.Component {
>
<div styleName='top'>
<div styleName='switch-buttons'>
<button styleName={isTagActive ? 'non-active-button' : 'active-button'} onClick={this.handleSwitchFoldersButtonClick.bind(this)}>
<img src={isTagActive
? '../resources/icon/icon-list.svg'
: '../resources/icon/icon-list-active.svg'
}
/>
</button>
<button styleName={isTagActive ? 'active-button' : 'non-active-button'} onClick={this.handleSwitchTagsButtonClick.bind(this)}>
<img src={isTagActive
? '../resources/icon/icon-tag-active.svg'
: '../resources/icon/icon-tag.svg'
}
/>
</button>
<ListButton onClick={this.handleSwitchFoldersButtonClick.bind(this)} isTagActive={isTagActive} />
<TagButton onClick={this.handleSwitchTagsButtonClick.bind(this)} isTagActive={isTagActive} />
</div>
<div>
<button styleName='top-menu-preference'
onClick={(e) => this.handleMenuButtonClick(e)}
>
<img styleName='iconTag' src='../resources/icon/icon-setting.svg' />
</button>
<PreferenceButton onClick={this.handleMenuButtonClick} />
</div>
</div>
{this.SideNavComponent(isFolded, storageList)}

View File

@@ -0,0 +1,61 @@
import { findStorage } from 'browser/lib/findStorage'
import resolveStorageData from './resolveStorageData'
import resolveStorageNotes from './resolveStorageNotes'
import * as path from 'path'
import * as fs from 'fs'
/**
* @param {String} storageKey
* @param {String} folderKey
* @param {String} fileType
* @param {String} exportDir
*
* @return {Object}
* ```
* {
* storage: Object,
* folderKey: String,
* fileType: String,
* exportDir: String
* }
* ```
*/
function exportFolder (storageKey, folderKey, fileType, exportDir) {
let targetStorage
try {
targetStorage = findStorage(storageKey)
} catch (e) {
return Promise.reject(e)
}
return resolveStorageData(targetStorage)
.then(function assignNotes (storage) {
return resolveStorageNotes(storage)
.then((notes) => {
return {
storage,
notes
}
})
})
.then(function exportNotes (data) {
const { storage, notes } = data
notes
.filter(note => note.folder === folderKey && note.isTrashed === false && note.type === 'MARKDOWN_NOTE')
.forEach(snippet => {
const notePath = path.join(exportDir, `${snippet.title}.${fileType}`)
fs.writeFileSync(notePath, snippet.content)
})
return {
storage,
folderKey,
fileType,
exportDir
}
})
}
module.exports = exportFolder

View File

@@ -7,6 +7,7 @@ const dataApi = {
updateFolder: require('./updateFolder'),
deleteFolder: require('./deleteFolder'),
reorderFolder: require('./reorderFolder'),
exportFolder: require('./exportFolder'),
createNote: require('./createNote'),
updateNote: require('./updateNote'),
deleteNote: require('./deleteNote'),

View File

@@ -29,7 +29,7 @@
width 490px
padding 0 5px
margin 10px 0
border 1px solid #C9C9C9 // TODO: use variable.
border 1px solid $ui-input--create-folder-modal
border-radius 2px
background-color transparent
outline none
@@ -68,7 +68,7 @@ body[data-theme="dark"]
color $ui-dark-text-color
.control-folder-input
border 1px solid #C9C9C9 // TODO: use variable.
border 1px solid $ui-input--create-folder-modal
color white
.description
@@ -76,3 +76,29 @@ body[data-theme="dark"]
.control-confirmButton
colorDarkPrimaryButton()
body[data-theme="solarized-dark"]
.root
modalSolarizedDark()
width 500px
height 270px
overflow hidden
position relative
.header
background-color transparent
border-color $ui-dark-borderColor
color $ui-solarized-dark-text-color
.control-folder-label
color $ui-solarized-dark-text-color
.control-folder-input
border 1px solid $ui-input--create-folder-modal
color white
.description
color $ui-inactive-text-color
.control-confirmButton
colorSolarizedDarkPrimaryButton()

View File

@@ -1,254 +0,0 @@
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './InitModal.styl'
import dataApi from 'browser/main/lib/dataApi'
import store from 'browser/main/store'
import { hashHistory } from 'react-router'
import _ from 'lodash'
const CSON = require('@rokt33r/season')
const path = require('path')
const electron = require('electron')
const { remote } = electron
function browseFolder () {
const dialog = remote.dialog
const defaultPath = remote.app.getPath('home')
return new Promise((resolve, reject) => {
dialog.showOpenDialog({
title: 'Select Directory',
defaultPath,
properties: ['openDirectory', 'createDirectory']
}, function (targetPaths) {
if (targetPaths == null) return resolve('')
resolve(targetPaths[0])
})
})
}
class InitModal extends React.Component {
constructor (props) {
super(props)
this.state = {
path: path.join(remote.app.getPath('home'), 'Boostnote'),
migrationRequested: true,
isLoading: true,
data: null,
legacyStorageExists: false,
isSending: false
}
}
handlePathChange (e) {
this.setState({
path: e.target.value
})
}
componentDidMount () {
let data = null
try {
data = CSON.readFileSync(path.join(remote.app.getPath('userData'), 'local.json'))
} catch (err) {
console.error(err)
}
const newState = {
isLoading: false
}
if (data != null) {
newState.legacyStorageExists = true
newState.data = data
}
this.setState(newState, () => {
this.refs.createButton.focus()
})
}
handlePathBrowseButtonClick (e) {
browseFolder()
.then((targetPath) => {
if (targetPath.length > 0) {
this.setState({
path: targetPath
})
}
})
.catch((err) => {
console.error('BrowseFAILED')
console.error(err)
})
}
handleSubmitButtonClick (e) {
this.setState({
isSending: true
}, () => {
dataApi
.addStorage({
name: 'My Storage',
path: this.state.path
})
.then((data) => {
if (this.state.migrationRequested && _.isObject(this.state.data) && _.isArray(this.state.data.folders) && _.isArray(this.state.data.articles)) {
return dataApi.migrateFromV5Storage(data.storage.key, this.state.data)
}
return data
})
.then((data) => {
if (data.storage.folders[0] != null) {
return data
} else {
return dataApi
.createFolder(data.storage.key, {
color: '#1278BD',
name: 'Default'
})
.then((_data) => {
return {
storage: _data.storage,
notes: data.notes
}
})
}
})
.then((data) => {
console.log(data)
store.dispatch({
type: 'ADD_STORAGE',
storage: data.storage,
notes: data.notes
})
const defaultSnippetNote = dataApi
.createNote(data.storage.key, {
type: 'SNIPPET_NOTE',
folder: data.storage.folders[0].key,
title: 'Snippet note example',
description: 'Snippet note example\nYou can store a series of snippets as a single note, like Gist.',
snippets: [
{
name: 'example.html',
mode: 'html',
content: '<html>\n<body>\n<h1 id=\'hello\'>Enjoy Boostnote!</h1>\n</body>\n</html>'
},
{
name: 'example.js',
mode: 'javascript',
content: 'var boostnote = document.getElementById(\'enjoy\').innerHTML\n\nconsole.log(boostnote)'
}
]
})
.then((note) => {
store.dispatch({
type: 'UPDATE_NOTE',
note: note
})
})
const defaultMarkdownNote = dataApi
.createNote(data.storage.key, {
type: 'MARKDOWN_NOTE',
folder: data.storage.folders[0].key,
title: 'Welcome to Boostnote!',
content: '# Welcome to Boostnote!\n## Click here to edit markdown :wave:\n\n<iframe width="560" height="315" src="https://www.youtube.com/embed/L0qNPLsvmyM" frameborder="0" allowfullscreen></iframe>\n\n## Docs :memo:\n- [Boostnote | Boost your happiness, productivity and creativity.](https://hackernoon.com/boostnote-boost-your-happiness-productivity-and-creativity-315034efeebe)\n- [Cloud Syncing & Backups](https://github.com/BoostIO/Boostnote/wiki/Cloud-Syncing-and-Backup)\n- [How to sync your data across Desktop and Mobile apps](https://github.com/BoostIO/Boostnote/wiki/Sync-Data-Across-Desktop-and-Mobile-apps)\n- [Convert data from **Evernote** to Boostnote.](https://github.com/BoostIO/Boostnote/wiki/Evernote)\n- [Keyboard Shortcuts](https://github.com/BoostIO/Boostnote/wiki/Keyboard-Shortcuts)\n- [Keymaps in Editor mode](https://github.com/BoostIO/Boostnote/wiki/Keymaps-in-Editor-mode)\n- [How to set syntax highlight in Snippet note](https://github.com/BoostIO/Boostnote/wiki/Syntax-Highlighting)\n\n---\n\n## Article Archive :books:\n- [Reddit English](http://bit.ly/2mOJPu7)\n- [Reddit Spanish](https://www.reddit.com/r/boostnote_es/)\n- [Reddit Chinese](https://www.reddit.com/r/boostnote_cn/)\n- [Reddit Japanese](https://www.reddit.com/r/boostnote_jp/)\n\n---\n\n## Community :beers:\n- [GitHub](http://bit.ly/2AWWzkD)\n- [Twitter](http://bit.ly/2z8BUJZ)\n- [Facebook Group](http://bit.ly/2jcca8t)'
})
.then((note) => {
store.dispatch({
type: 'UPDATE_NOTE',
note: note
})
})
return Promise.resolve(defaultSnippetNote)
.then(defaultMarkdownNote)
.then(() => data.storage)
})
.then((storage) => {
hashHistory.push('/storages/' + storage.key)
this.props.close()
})
.catch((err) => {
this.setState({
isSending: false
})
throw err
})
})
}
handleMigrationRequestedChange (e) {
this.setState({
migrationRequested: e.target.checked
})
}
handleKeyDown (e) {
if (e.keyCode === 27) {
this.props.close()
}
}
render () {
if (this.state.isLoading) {
return <div styleName='root--loading'>
<i styleName='spinner' className='fa fa-spin fa-spinner' />
<div styleName='loadingMessage'>Preparing initialization...</div>
</div>
}
return (
<div styleName='root'
tabIndex='-1'
onKeyDown={(e) => this.handleKeyDown(e)}
>
<div styleName='body'>
<div styleName='body-welcome'>
Welcome to Boostnote!
</div>
<div styleName='body-description'>
Please select a directory for data storage.
</div>
<div styleName='body-path'>
<input styleName='body-path-input'
placeholder='Select Folder'
value={this.state.path}
onChange={(e) => this.handlePathChange(e)}
/>
<button styleName='body-path-button'
onClick={(e) => this.handlePathBrowseButtonClick(e)}
>
...
</button>
</div>
{this.state.legacyStorageExists &&
<div styleName='body-migration'>
<label><input type='checkbox' checked={this.state.migrationRequested} onChange={(e) => this.handleMigrationRequestedChange(e)} /> Migrate old data from the legacy app v0.5</label>
</div>
}
<div styleName='body-control'>
<button styleName='body-control-createButton'
ref='createButton'
onClick={(e) => this.handleSubmitButtonClick(e)}
disabled={this.state.isSending}
>
{this.state.isSending
? <span>
<i className='fa fa-spin fa-spinner' /> Loading...
</span>
: 'CREATE'
}
</button>
</div>
</div>
</div>
)
}
}
InitModal.propTypes = {
}
export default CSSModules(InitModal, styles)

View File

@@ -1,76 +0,0 @@
.root
modal()
background-color #fff
max-width 100vw
max-height 100vh
overflow hidden
margin 0
padding 150px 0
position relative
.root--loading
@extend .root
text-align center
.spinner
font-size 100px
margin 35px auto
color $ui-text-color
.loadingMessage
color $ui-text-color
margin 15px auto 35px
.body
padding 30px
.body-welcome
text-align center
margin-bottom 25px
font-size 32px
color $ui-text-color
.body-description
font-size 16px
color $ui-text-color
text-align center
margin-bottom 25px
.body-path
margin 0 auto 25px
width 330px
.body-path-input
height 40px
vertical-align middle
width 300px
font-size 14px
border-style solid
border-width 1px 0 1px 1px
border-color $border-color
border-top-left-radius 2px
border-bottom-left-radius 2px
padding 0 5px
.body-path-button
height 42px
width 30px
font-size 16px
font-weight 600
border none
border-top-right-radius 2px
border-bottom-right-radius 2px
colorPrimaryButton()
vertical-align middle
.body-migration
margin 0 auto 25px
text-align center
.body-control
text-align center
.body-control-createButton
colorPrimaryButton()
font-size 14px
font-weight 600
border none
border-radius 2px
height 40px
padding 0 25px

View File

@@ -76,8 +76,8 @@
color #1EC38B
.error
color red
.warning
color #FFA500
.group-control-leftButton
colorDefaultButton()

View File

@@ -32,6 +32,7 @@ class HotkeyTab extends React.Component {
message: err.message != null ? err.message : 'Error occurs!'
}})
}
this.oldHotkey = this.state.config.hotkey
ipc.addListener('APP_SETTING_DONE', this.handleSettingDone)
ipc.addListener('APP_SETTING_ERROR', this.handleSettingError)
}
@@ -53,6 +54,7 @@ class HotkeyTab extends React.Component {
config: newConfig
})
this.clearMessage()
this.props.haveToSave()
}
handleHintToggleButtonClick (e) {
@@ -70,6 +72,15 @@ class HotkeyTab extends React.Component {
this.setState({
config
})
if (_.isEqual(this.oldHotkey, config.hotkey)) {
this.props.haveToSave()
} else {
this.props.haveToSave({
tab: 'Hotkey',
type: 'warning',
message: 'You have to save!'
})
}
}
clearMessage () {
@@ -161,7 +172,8 @@ class HotkeyTab extends React.Component {
}
HotkeyTab.propTypes = {
dispatch: PropTypes.func
dispatch: PropTypes.func,
haveToSave: PropTypes.func
}
export default CSSModules(HotkeyTab, styles)

View File

@@ -42,6 +42,8 @@ top-bar--height = 50px
background-color transparent
color $ui-text-color
font-size 16px
.saving--warning
haveToSave()
.nav-button--active
@extend .nav-button
@@ -49,6 +51,8 @@ top-bar--height = 50px
background-color $ui-button--active-backgroundColor
&:hover
color $ui-text-color
.saving--warning
haveToSave()
.nav-button-icon
display block

View File

@@ -20,3 +20,8 @@ $tab--dark-text-color = #E5E5E5
body[data-theme="dark"]
.header
color $tab--dark-text-color
haveToSave()
color #FFA500
font-size 10px
margin-top 3px

View File

@@ -7,11 +7,11 @@ import store from 'browser/main/store'
import consts from 'browser/lib/consts'
import ReactCodeMirror from 'react-codemirror'
import CodeMirror from 'codemirror'
import 'codemirror-mode-elixir'
import _ from 'lodash'
const OSX = global.process.platform === 'darwin'
import _ from 'lodash'
const electron = require('electron')
const ipc = electron.ipcRenderer
@@ -62,6 +62,7 @@ class UiTab extends React.Component {
ui: {
theme: this.refs.uiTheme.value,
showCopyNotification: this.refs.showCopyNotification.checked,
confirmDeletion: this.refs.confirmDeletion.checked,
disableDirectWrite: this.refs.uiD2w != null
? this.refs.uiD2w.checked
: false
@@ -93,8 +94,19 @@ class UiTab extends React.Component {
if (newCodemirrorTheme !== codemirrorTheme) {
checkHighLight.setAttribute('href', `../node_modules/codemirror/theme/${newCodemirrorTheme.split(' ')[0]}.css`)
}
this.setState({ config: newConfig, codemirrorTheme: newCodemirrorTheme })
this.setState({ config: newConfig, codemirrorTheme: newCodemirrorTheme }, () => {
const {ui, editor, preview} = this.props.config
this.currentConfig = {ui, editor, preview}
if (_.isEqual(this.currentConfig, this.state.config)) {
this.props.haveToSave()
} else {
this.props.haveToSave({
tab: 'UI',
type: 'warning',
message: 'You have to save!'
})
}
})
}
handleSaveUIClick (e) {
@@ -111,6 +123,7 @@ class UiTab extends React.Component {
config: newConfig
})
this.clearMessage()
this.props.haveToSave()
}
clearMessage () {
@@ -161,6 +174,16 @@ class UiTab extends React.Component {
Show &quot;Saved to Clipboard&quot; notification when copying
</label>
</div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
checked={this.state.config.ui.confirmDeletion}
ref='confirmDeletion'
type='checkbox'
/>&nbsp;
Show a confirmation dialog when deleting notes
</label>
</div>
{
global.process.platform === 'win32'
? <div styleName='group-checkBoxSection'>
@@ -170,7 +193,7 @@ class UiTab extends React.Component {
refs='uiD2w'
disabled={OSX}
type='checkbox'
/>
/>&nbsp;
Disable Direct Write(It will be applied after restarting)
</label>
</div>
@@ -412,7 +435,8 @@ UiTab.propTypes = {
user: PropTypes.shape({
name: PropTypes.string
}),
dispatch: PropTypes.func
dispatch: PropTypes.func,
haveToSave: PropTypes.func
}
export default CSSModules(UiTab, styles)

View File

@@ -17,7 +17,9 @@ class Preferences extends React.Component {
super(props)
this.state = {
currentTab: 'STORAGES'
currentTab: 'STORAGES',
UIAlert: '',
HotkeyAlert: ''
}
}
@@ -58,6 +60,7 @@ class Preferences extends React.Component {
<HotkeyTab
dispatch={dispatch}
config={config}
haveToSave={alert => this.setState({HotkeyAlert: alert})}
/>
)
case 'UI':
@@ -65,6 +68,7 @@ class Preferences extends React.Component {
<UiTab
dispatch={dispatch}
config={config}
haveToSave={alert => this.setState({UIAlert: alert})}
/>
)
case 'CROWDFUNDING':
@@ -94,19 +98,26 @@ class Preferences extends React.Component {
return node.getBoundingClientRect()
}
haveToSaveNotif (type, message) {
return (
<p styleName={`saving--${type}`}>{message}</p>
)
}
render () {
const content = this.renderContent()
const tabs = [
{target: 'STORAGES', label: 'Storages'},
{target: 'HOTKEY', label: 'Hotkey'},
{target: 'UI', label: 'UI'},
{target: 'HOTKEY', label: 'Hotkey', Hotkey: this.state.HotkeyAlert},
{target: 'UI', label: 'UI', UI: this.state.UIAlert},
{target: 'INFO', label: 'Community / Info'},
{target: 'CROWDFUNDING', label: 'Crowdfunding'}
]
const navButtons = tabs.map((tab) => {
const isActive = this.state.currentTab === tab.target
const isUiHotkeyTab = _.isObject(tab[tab.label]) && tab.label === tab[tab.label].tab
return (
<button styleName={isActive
? 'nav-button--active'
@@ -118,6 +129,7 @@ class Preferences extends React.Component {
<span styleName='nav-button-label'>
{tab.label}
</span>
{isUiHotkeyTab ? this.haveToSaveNotif(tab[tab.label].type, tab[tab.label].message) : null}
</button>
)
})

View File

@@ -87,8 +87,13 @@ function data (state = defaultDataMap(), action) {
state.trashedSet = new Set(state.trashedSet)
if (note.isTrashed) {
state.trashedSet.add(uniqueKey)
state.starredSet.delete(uniqueKey)
} else {
state.trashedSet.delete(uniqueKey)
if (note.isStarred) {
state.starredSet.add(uniqueKey)
}
}
}
@@ -349,6 +354,13 @@ function data (state = defaultDataMap(), action) {
state.storageMap = new Map(state.storageMap)
state.storageMap.set(action.storage.key, action.storage)
return state
case 'EXPORT_FOLDER':
{
state = Object.assign({}, state)
state.storageMap = new Map(state.storageMap)
state.storageMap.set(action.storage.key, action.storage)
}
return state
case 'DELETE_FOLDER':
{
state = Object.assign({}, state)

View File

@@ -46,6 +46,7 @@ tooltip()
// UI Input
$ui-input--focus-borderColor = #369DCD
$ui-input--disabled-backgroundColor = #DDD
$ui-input--create-folder-modal = #C9C9C9
// Parts
$ui-favorite-star-button-color = #FFC216
@@ -340,3 +341,11 @@ $ui-solarized-dark-button--active-color = #93a1a1
$ui-solarized-dark-button--active-backgroundColor = #073642
$ui-solarized-dark-button--hover-backgroundColor = lighten($ui-dark-backgroundColor, 10%)
$ui-solarized-dark-button--focus-borderColor = lighten(#369DCD, 25%)
modalSolarizedDark()
position relative
z-index $modal-z-index
width 100%
background-color $ui-solarized-dark-backgroundColor
overflow hidden
border-radius $modal-border-radius

View File

@@ -28,12 +28,14 @@
<script src="../node_modules/codemirror/lib/codemirror.js"></script>
<script src="../node_modules/codemirror/mode/meta.js"></script>
<script src="../node_modules/codemirror-mode-elixir/dist/elixir.js"></script>
<script src="../node_modules/codemirror/addon/mode/overlay.js"></script>
<script src="../node_modules/codemirror/addon/mode/loadmode.js"></script>
<script src="../node_modules/codemirror/keymap/sublime.js"></script>
<script src="../node_modules/codemirror/keymap/vim.js"></script>
<script src="../node_modules/codemirror/keymap/emacs.js"></script>
<script src="../node_modules/codemirror/addon/runmode/runmode.js"></script>
<script src="../node_modules/codemirror/addon/edit/closebrackets.js"></script>
<script src="../node_modules/raphael/raphael.min.js"></script>
<script src="../node_modules/flowchart.js/release/flowchart.min.js"></script>

View File

@@ -65,14 +65,6 @@ updater.autoUpdater.on('error', (err) => {
console.log(err)
})
ipc.on('update-check', function (event, msg) {
if (isUpdateReady) {
mainWindow.webContents.send('update-ready', 'Update available!')
} else {
checkUpdate()
}
})
ipc.on('update-app-confirm', function (event, msg) {
if (isUpdateReady) {
mainWindow.removeAllListeners()
@@ -102,12 +94,11 @@ app.on('ready', function () {
Menu.setApplicationMenu(menu)
break
case 'win32':
/* eslint-disable */
finderWindow = require('./finder-window')
/* eslint-disable */
require('./finder-window')
mainWindow.setMenu(menu)
break
case 'linux':
require('./finder-window')
Menu.setApplicationMenu(menu)
mainWindow.setMenu(menu)
}
@@ -115,9 +106,20 @@ app.on('ready', function () {
// Check update every hour
setInterval(function () {
checkUpdate()
}, 1000 * 60 * 60)
}, 1000 * 60 * 60 * 24)
// Check update after 10 secs to prevent file locking of Windows
setTimeout(() => {
checkUpdate()
ipc.on('update-check', function (event, msg) {
if (isUpdateReady) {
mainWindow.webContents.send('update-ready', 'Update available!')
} else {
checkUpdate()
}
})
}, 10000)
ipcServer = require('./ipcServer')
ipcServer.server.start()
})

View File

@@ -108,6 +108,13 @@ const file = {
mainWindow.webContents.send('list:isMarkdownNote')
mainWindow.webContents.send('export:save-md')
}
},
{
label: 'HTML (.html)',
click () {
mainWindow.webContents.send('list:isMarkdownNote')
mainWindow.webContents.send('export:save-html')
}
}
]
},

View File

@@ -69,6 +69,7 @@
<script src="../node_modules/codemirror/lib/codemirror.js"></script>
<script src="../node_modules/codemirror/mode/meta.js"></script>
<script src="../node_modules/codemirror-mode-elixir/dist/elixir.js"></script>
<script src="../node_modules/codemirror/addon/mode/overlay.js"></script>
<script src="../node_modules/codemirror/addon/mode/loadmode.js"></script>
<script src="../node_modules/codemirror/addon/mode/simple.js"></script>
@@ -79,6 +80,8 @@
<script src="../node_modules/codemirror/addon/edit/continuelist.js"></script>
<script src="../node_modules/codemirror/addon/edit/closebrackets.js"></script>
<script src="../node_modules/codemirror/addon/search/search.js"></script>
<script src="../node_modules/codemirror/addon/search/searchcursor.js"></script>
<script src="../node_modules/codemirror/addon/scroll/annotatescrollbar.js"></script>

View File

@@ -1,7 +1,7 @@
{
"name": "boost",
"productName": "Boostnote",
"version": "0.8.19",
"version": "0.8.20",
"main": "index.js",
"description": "Boostnote",
"license": "GPL-3.0",
@@ -16,7 +16,7 @@
"dev-start": "concurrently --kill-others \"npm run webpack\" \"npm run hot\""
},
"config": {
"electron-version": "1.6.15"
"electron-version": "1.7.10"
},
"repository": {
"type": "git",
@@ -53,6 +53,7 @@
"aws-sdk": "^2.48.0",
"aws-sdk-mobile-analytics": "^0.9.2",
"codemirror": "^5.19.0",
"codemirror-mode-elixir": "^1.1.1",
"electron-config": "^0.2.1",
"electron-gh-releases": "^2.0.2",
"flowchart.js": "^1.6.5",
@@ -102,7 +103,7 @@
"css-loader": "^0.19.0",
"devtron": "^1.1.0",
"dom-storage": "^2.0.2",
"electron": "^1.6.15",
"electron": "1.7.10",
"electron-packager": "^6.0.0",
"eslint": "^3.13.1",
"eslint-config-standard": "^6.2.1",
@@ -111,7 +112,7 @@
"eslint-plugin-standard": "^3.0.1",
"faker": "^3.1.0",
"grunt": "^0.4.5",
"grunt-electron-installer": "^1.2.0",
"grunt-electron-installer": "2.1.0",
"history": "^1.17.0",
"jsdom": "^9.4.2",
"json-loader": "^0.5.4",

View File

@@ -1,6 +1,4 @@
New:zap:
Open sourcing our [Android and iOS apps](https://github.com/BoostIO/Boostnote-mobile)!
:mega: Open sourcing our [Android and iOS apps](https://github.com/BoostIO/Boostnote-mobile)!
![Boostnote app screenshot](./resources/repository/top.png)
@@ -12,7 +10,6 @@ Open sourcing our [Android and iOS apps](https://github.com/BoostIO/Boostnote-mo
## Authors & Maintainers
- [Rokt33r](https://github.com/rokt33r)
- [sota1235](https://github.com/sota1235)
- [Kohei TAKATA](https://github.com/kohei-takata)
- [Sosuke](https://github.com/sosukesuzuki)
- [Kazz](https://github.com/kazup01)
@@ -29,7 +26,7 @@ Boostnote is an open source project. It's an independent project with its ongoin
## Community
- [Facebook Group](https://www.facebook.com/groups/boostnote/)
- [Twitter](https://twitter.com/boostnoteapp)
- [Slack Group](https://join.slack.com/t/boostnote-group/shared_invite/enQtMjc2MDM0MDEyODk2LThlZDlhYmYwMjdkMmJjMGM5MGFiMGJmNzk5ZTdhNzFhMmNmMDFlY2M2YTE1MTZkOThiOGZmNTI3YzJiOTBhMTQ)
- [Slack Group](https://join.slack.com/t/boostnote-group/shared_invite/enQtMjkxMzMwODYxMDI1LTgwZmRiODg0NzA5MWRmOTJjNzBjZjAwMmMyZGQ4Y2RkOGE0MDg0YjcyMjA5OGUzMmZhNmFiNTMzOTlkYWNlMTM)
- [Blog](https://medium.com/boostnote)
- [Reddit](https://www.reddit.com/r/Boostnote/)

View File

@@ -0,0 +1,62 @@
const test = require('ava')
const exportFolder = require('browser/main/lib/dataApi/exportFolder')
const createNote = require('browser/main/lib/dataApi/createNote')
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 os = require('os')
const faker = require('faker')
const fs = require('fs')
const storagePath = path.join(os.tmpdir(), 'test/export-note')
test.beforeEach((t) => {
t.context.storage = TestDummy.dummyStorage(storagePath)
localStorage.setItem('storages', JSON.stringify([t.context.storage.cache]))
})
test.serial('Export a folder', (t) => {
const storageKey = t.context.storage.cache.key
const folderKey = t.context.storage.json.folders[0].key
const input1 = {
type: 'MARKDOWN_NOTE',
description: '*Some* markdown text',
tags: faker.lorem.words().split(' '),
folder: folderKey
}
input1.title = 'input1'
const input2 = {
type: 'SNIPPET_NOTE',
description: 'Some normal text',
snippets: [{
name: faker.system.fileName(),
mode: 'text',
content: faker.lorem.lines()
}],
tags: faker.lorem.words().split(' '),
folder: folderKey
}
input2.title = 'input2'
return createNote(storageKey, input1)
.then(function () {
return createNote(storageKey, input2)
})
.then(function () {
return exportFolder(storageKey, folderKey, 'md', storagePath)
})
.then(function assert () {
let filePath = path.join(storagePath, 'input1.md')
t.true(fs.existsSync(filePath))
filePath = path.join(storagePath, 'input2.md')
t.false(fs.existsSync(filePath))
})
})

View File

@@ -245,18 +245,6 @@ asar@^0.9.0:
mkdirp "^0.5.0"
mksnapshot "0.1.0"
asar@~0.8.0:
version "0.8.3"
resolved "https://registry.yarnpkg.com/asar/-/asar-0.8.3.tgz#c2e03f9054516dbbf56759e854e9ce8d1a9d39d3"
dependencies:
chromium-pickle-js "0.1.0"
commander "2.3.0"
cuint "0.1.5"
glob "^5.0.5"
minimatch "2.0.4"
mkdirp "^0.5.0"
mksnapshot "0.1.0"
asn1@0.1.11:
version "0.1.11"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.1.11.tgz#559be18376d08a4ec4dbe80877d27818639b2df7"
@@ -1183,6 +1171,10 @@ bluebird@^3.0.0, bluebird@^3.1.1, bluebird@^3.4.1:
version "3.5.0"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c"
bluebird@^3.3.4:
version "3.5.1"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
boom@2.x.x:
version "2.10.1"
resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f"
@@ -1503,10 +1495,20 @@ code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
codemirror-mode-elixir@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/codemirror-mode-elixir/-/codemirror-mode-elixir-1.1.1.tgz#cc5b79bf5f93b6da426e32364a673a681391416c"
dependencies:
codemirror "^5.20.2"
codemirror@^5.18.2, codemirror@^5.19.0:
version "5.26.0"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.26.0.tgz#bcbee86816ed123870c260461c2b5c40b68746e5"
codemirror@^5.20.2:
version "5.33.0"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.33.0.tgz#462ad9a6fe8d38b541a9536a3997e1ef93b40c6a"
coffee-script@^1.10.0:
version "1.12.6"
resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.12.6.tgz#285a3f7115689065064d6bf9ef4572db66695cbf"
@@ -2199,9 +2201,20 @@ electron-to-chromium@^1.2.7:
version "1.3.11"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.11.tgz#744761df1d67b492b322ce9aa0aba5393260eb61"
electron@^1.6.15:
version "1.7.9"
resolved "https://registry.yarnpkg.com/electron/-/electron-1.7.9.tgz#add54e9f8f83ed02f6519ec10135f698b19336cf"
electron-winstaller@^2.2.0:
version "2.6.3"
resolved "https://registry.yarnpkg.com/electron-winstaller/-/electron-winstaller-2.6.3.tgz#d54f77c0cececc4fc55eeb5968d345cf69645ea4"
dependencies:
asar "^0.11.0"
bluebird "^3.3.4"
debug "^2.2.0"
fs-extra "^0.26.7"
lodash.template "^4.2.2"
temp "^0.8.3"
electron@1.7.10:
version "1.7.10"
resolved "https://registry.yarnpkg.com/electron/-/electron-1.7.10.tgz#3a3e83d965fd7fafe473be8ddf8f472561b6253d"
dependencies:
"@types/node" "^7.0.18"
electron-download "^3.0.1"
@@ -2885,7 +2898,7 @@ fs-extra@0.18.2:
jsonfile "^2.0.0"
rimraf "^2.2.8"
fs-extra@0.26.7, fs-extra@^0.26.0, fs-extra@^0.26.5:
fs-extra@0.26.7, fs-extra@^0.26.0, fs-extra@^0.26.5, fs-extra@^0.26.7:
version "0.26.7"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.26.7.tgz#9ae1fdd94897798edab76d0918cf42d0c3184fa9"
dependencies:
@@ -3156,13 +3169,11 @@ grunt-electron-installer-redhat@^0.3.1:
dependencies:
electron-installer-redhat "^0.3.0"
grunt-electron-installer@^1.2.0:
version "1.2.3"
resolved "https://registry.yarnpkg.com/grunt-electron-installer/-/grunt-electron-installer-1.2.3.tgz#50652ec4d0248233da76b4ac2ca69f3894c7240e"
grunt-electron-installer@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/grunt-electron-installer/-/grunt-electron-installer-2.1.0.tgz#b39e7eb1abb4488a1d8b7587fd4e72d68a741030"
dependencies:
asar "~0.8.0"
temp "^0.8.1"
underscore "^1.7.0"
electron-winstaller "^2.2.0"
grunt-legacy-log-utils@~0.1.1:
version "0.1.1"
@@ -4016,7 +4027,7 @@ lodash.some@^4.5.1:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
lodash.template@^4.3.0:
lodash.template@^4.2.2, lodash.template@^4.3.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.4.0.tgz#e73a0385c8355591746e020b99679c690e68fba0"
dependencies:
@@ -6336,7 +6347,7 @@ tar@^2.2.1:
fstream "^1.0.2"
inherits "2"
temp@^0.8.1, temp@^0.8.3:
temp@^0.8.3:
version "0.8.3"
resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59"
dependencies:
@@ -6540,7 +6551,7 @@ underscore.string@~2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-2.4.0.tgz#8cdd8fbac4e2d2ea1e7e2e8097c42f442280f85b"
underscore@^1.7.0, underscore@^1.8.2:
underscore@^1.8.2:
version "1.8.3"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022"