1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-13 01:36:22 +00:00

cleaned up redundant variables, fixed eslint fix command, split snippetList into component

This commit is contained in:
Nguyễn Việt Hưng
2018-05-21 18:32:41 +07:00
parent ce594b0b5a
commit 2b2f17525e
13 changed files with 182 additions and 125 deletions

View File

@@ -9,7 +9,7 @@ import iconv from 'iconv-lite'
import crypto from 'crypto'
import consts from 'browser/lib/consts'
import fs from 'fs'
const { ipcRenderer, remote } = require('electron')
const { ipcRenderer } = require('electron')
CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js'
@@ -201,11 +201,11 @@ export default class CodeEditor extends React.Component {
for (let i = 0; i < snippets.length; i++) {
if (snippets[i].prefix.indexOf(wordBeforeCursor.text) !== -1) {
if (snippets[i].content.indexOf(templateCursorString) !== -1) {
let snippetLines = snippets[i].content.split('\n')
const snippetLines = snippets[i].content.split('\n')
let cursorLineNumber = 0
let cursorLinePosition = 0
for (let j = 0; j < snippetLines.length; j++) {
let cursorIndex = snippetLines[j].indexOf(templateCursorString)
const cursorIndex = snippetLines[j].indexOf(templateCursorString)
if (cursorIndex !== -1) {
cursorLineNumber = j
cursorLinePosition = cursorIndex

View File

@@ -393,7 +393,7 @@ export default class MarkdownPreview extends React.Component {
value = value.replace(codeBlock, htmlTextHelper.encodeEntities(codeBlock))
})
}
let renderedHTML = this.markdown.render(value)
const renderedHTML = this.markdown.render(value)
this.refs.root.contentWindow.document.body.innerHTML = attachmentManagement.fixLocalURLS(renderedHTML, storagePath)
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {

View File

@@ -2,7 +2,6 @@ const path = require('path')
const fs = require('sander')
const { remote } = require('electron')
const { app } = remote
const os = require('os')
const themePath = process.env.NODE_ENV === 'production'
? path.join(app.getAppPath(), './node_modules/codemirror/theme')

View File

@@ -183,7 +183,7 @@ class SideNav extends React.Component {
).filter(
note => activeTags.every(tag => note.tags.includes(tag))
)
let relatedTags = new Set()
const relatedTags = new Set()
relatedNotes.forEach(note => note.tags.map(tag => relatedTags.add(tag)))
return relatedTags
}
@@ -222,7 +222,7 @@ class SideNav extends React.Component {
handleClickNarrowToTag (tag) {
const { router } = this.context
const { location } = this.props
let listOfTags = this.getActiveTags(location.pathname)
const listOfTags = this.getActiveTags(location.pathname)
const indexOfTag = listOfTags.indexOf(tag)
if (indexOfTag > -1) {
listOfTags.splice(indexOfTag, 1)

View File

@@ -104,8 +104,8 @@ function handleAttachmentDrop (codeEditor, storageKey, noteKey, dropEvent) {
const fileType = file['type']
copyAttachment(filePath, storageKey, noteKey).then((fileName) => {
let showPreview = fileType.startsWith('image')
let imageMd = generateAttachmentMarkdown(originalFileName, path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, fileName), showPreview)
const showPreview = fileType.startsWith('image')
const imageMd = generateAttachmentMarkdown(originalFileName, path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, fileName), showPreview)
codeEditor.insertAttachmentMd(imageMd)
})
}
@@ -139,7 +139,7 @@ function handlePastImageEvent (codeEditor, storageKey, noteKey, dataTransferItem
const destinationDir = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey)
createAttachmentDestinationFolder(targetStorage.path, noteKey)
let imageName = `${uniqueSlug()}.png`
const imageName = `${uniqueSlug()}.png`
const imagePath = path.join(destinationDir, imageName)
reader.onloadend = function () {
@@ -147,7 +147,7 @@ function handlePastImageEvent (codeEditor, storageKey, noteKey, dataTransferItem
base64data += base64data.replace('+', ' ')
const binaryData = new Buffer(base64data, 'base64').toString('binary')
fs.writeFile(imagePath, binaryData, 'binary')
let imageMd = generateAttachmentMarkdown(imageName, imagePath, true)
const imageMd = generateAttachmentMarkdown(imageName, imagePath, true)
codeEditor.insertAttachmentMd(imageMd)
}
reader.readAsDataURL(blob)

View File

@@ -1,5 +1,4 @@
import fs from 'fs'
import crypto from 'crypto'
import consts from 'browser/lib/consts'
function fetchSnippet (id, snippetFile) {

View File

@@ -1,8 +1,6 @@
import CodeMirror from 'codemirror'
import React from 'react'
import _ from 'lodash'
import fs from 'fs'
import consts from 'browser/lib/consts'
import dataApi from 'browser/main/lib/dataApi'
const defaultEditorFontFamily = ['Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', 'monospace']
@@ -71,10 +69,7 @@ export default class SnippetEditor extends React.Component {
return (
<div styleName='SnippetEditor' ref='root' tabIndex='-1' style={{
fontFamily: fontFamily.join(', '),
fontSize: fontSize,
position: 'absolute',
width: '100%',
height: '90%'
fontSize: fontSize
}} />
)
}

View File

@@ -0,0 +1,87 @@
import React from 'react'
import styles from './SnippetTab.styl'
import CSSModules from 'browser/lib/CSSModules'
import dataApi from 'browser/main/lib/dataApi'
import i18n from 'browser/lib/i18n'
import eventEmitter from 'browser/main/lib/eventEmitter'
const { remote } = require('electron')
const { Menu, MenuItem } = remote
class SnippetList extends React.Component {
constructor (props) {
super(props)
this.state = {
snippets: []
}
}
componentDidMount () {
this.reloadSnippetList()
eventEmitter.on('snippetList:reload', this.reloadSnippetList.bind(this))
}
reloadSnippetList () {
dataApi.fetchSnippet().then(snippets => this.setState({snippets}))
}
handleSnippetContextMenu (snippet) {
const menu = new Menu()
menu.append(new MenuItem({
label: i18n.__('Delete snippet'),
click: () => {
this.deleteSnippet(snippet)
}
}))
menu.popup()
}
deleteSnippet (snippet) {
dataApi.deleteSnippet(snippet).then(() => {
this.reloadSnippetList()
this.props.onSnippetDeleted(snippet)
}).catch(err => { throw err })
}
handleSnippetClick (snippet) {
this.props.onSnippetClick(snippet)
}
createSnippet () {
dataApi.createSnippet().then(() => {
this.reloadSnippetList()
// scroll to end of list when added new snippet
const snippetList = document.getElementById('snippets')
snippetList.scrollTop = snippetList.scrollHeight
}).catch(err => { throw err })
}
render () {
const { snippets } = this.state
return (
<div styleName='snippet-list'>
<div styleName='group-section'>
<div styleName='group-section-control'>
<button styleName='group-control-button' onClick={() => this.createSnippet()}>
<i className='fa fa-plus' /> {i18n.__('New Snippet')}
</button>
</div>
</div>
<ul id='snippets' styleName='snippets'>
{
snippets.map((snippet) => (
<li
styleName='snippet-item'
key={snippet.id}
onContextMenu={() => this.handleSnippetContextMenu(snippet)}
onClick={() => this.handleSnippetClick(snippet)}>
{snippet.name}
</li>
))
}
</ul>
</div>
)
}
}
export default CSSModules(SnippetList, styles)

View File

@@ -1,33 +1,33 @@
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './SnippetTab.styl'
import fs from 'fs'
import SnippetEditor from './SnippetEditor'
import i18n from 'browser/lib/i18n'
import dataApi from 'browser/main/lib/dataApi'
import consts from 'browser/lib/consts'
const { remote } = require('electron')
const { Menu, MenuItem } = remote
import SnippetList from './SnippetList'
import eventEmitter from 'browser/main/lib/eventEmitter'
class SnippetTab extends React.Component {
constructor (props) {
super(props)
this.state = {
snippets: [],
currentSnippet: null
}
this.changeDelay = null
}
componentDidMount () {
this.reloadSnippetList()
handleSnippetNameOrPrefixChange () {
clearTimeout(this.changeDelay)
this.changeDelay = setTimeout(() => {
// notify the snippet editor that the name or prefix of snippet has been changed
this.snippetEditor.onSnippetNameOrPrefixChanged(this.state.currentSnippet)
eventEmitter.emit('snippetList:reload')
}, 500)
}
handleSnippetClick (snippet) {
if (this.state.currentSnippet === null || this.state.currentSnippet.id !== snippet.id) {
const { currentSnippet } = this.state
if (currentSnippet === null || currentSnippet.id !== snippet.id) {
dataApi.fetchSnippet(snippet.id).then(changedSnippet => {
// notify the snippet editor to load the content of the new snippet
this.snippetEditor.onSnippetChanged(changedSnippet)
@@ -36,70 +36,27 @@ class SnippetTab extends React.Component {
}
}
handleSnippetNameOrPrefixChange () {
clearTimeout(this.changeDelay)
this.changeDelay = setTimeout(() => {
// notify the snippet editor that the name or prefix of snippet has been changed
this.snippetEditor.onSnippetNameOrPrefixChanged(this.state.currentSnippet)
this.reloadSnippetList()
}, 500)
onSnippetNameOrPrefixChanged (e, type) {
const newSnippet = Object.assign({}, this.state.currentSnippet)
if (type === 'name') {
newSnippet.name = e.target.value
} else {
newSnippet.prefix = e.target.value
}
this.setState({ currentSnippet: newSnippet })
this.handleSnippetNameOrPrefixChange()
}
handleSnippetContextMenu (snippet) {
const menu = new Menu()
menu.append(new MenuItem({
label: i18n.__('Delete snippet'),
click: () => {
this.deleteSnippet(snippet)
}
}))
menu.popup()
}
reloadSnippetList () {
dataApi.fetchSnippet().then(snippets => this.setState({snippets}))
}
deleteSnippet (snippet) {
dataApi.deleteSnippet(snippet).then(() => {
this.reloadSnippetList()
// prevent old snippet still display when deleted
if (snippet.id === this.state.currentSnippet.id) {
this.setState({currentSnippet: null})
}
}).catch(err => { throw err })
}
createSnippet () {
dataApi.createSnippet().then(() => {
this.reloadSnippetList()
// scroll to end of list when added new snippet
const snippetList = document.getElementById('snippets')
snippetList.scrollTop = snippetList.scrollHeight
}).catch(err => { throw err })
}
renderSnippetList () {
const { snippets } = this.state
return (
<ul id='snippets' style={{height: 'calc(100% - 8px)', overflow: 'scroll', background: '#f5f5f5'}}>
{
snippets.map((snippet) => (
<li
styleName='snippet-item'
key={snippet.id}
onContextMenu={() => this.handleSnippetContextMenu(snippet)}
onClick={() => this.handleSnippetClick(snippet)}>
{snippet.name}
</li>
))
}
</ul>
)
handleDeleteSnippet (snippet) {
// prevent old snippet still display when deleted
if (snippet.id === this.state.currentSnippet.id) {
this.setState({currentSnippet: null})
}
}
render () {
const { config } = this.props
const { config, storageKey } = this.props
const { currentSnippet } = this.state
let editorFontSize = parseInt(config.editor.fontSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
@@ -108,29 +65,17 @@ class SnippetTab extends React.Component {
return (
<div styleName='root'>
<div styleName='header'>{i18n.__('Snippets')}</div>
<div styleName='snippet-list'>
<div styleName='group-section'>
<div styleName='group-section-control'>
<button styleName='group-control-button' onClick={() => this.createSnippet()}>
<i className='fa fa-plus' /> {i18n.__('New Snippet')}
</button>
</div>
</div>
{this.renderSnippetList()}
</div>
<div styleName='snippet-detail' style={{visibility: this.state.currentSnippet ? 'visible' : 'hidden'}}>
<SnippetList
onSnippetClick={this.handleSnippetClick.bind(this)}
onSnippetDeleted={this.handleDeleteSnippet.bind(this)} />
<div styleName='snippet-detail' style={{visibility: currentSnippet ? 'visible' : 'hidden'}}>
<div styleName='group-section'>
<div styleName='group-section-label'>{i18n.__('Snippet name')}</div>
<div styleName='group-section-control'>
<input
styleName='group-section-control-input'
value={this.state.currentSnippet ? this.state.currentSnippet.name : ''}
onChange={e => {
const newSnippet = Object.assign({}, this.state.currentSnippet)
newSnippet.name = e.target.value
this.setState({ currentSnippet: newSnippet })
this.handleSnippetNameOrPrefixChange()
}}
value={currentSnippet ? currentSnippet.name : ''}
onChange={e => { this.onSnippetNameOrPrefixChanged(e, 'name') }}
type='text' />
</div>
</div>
@@ -139,19 +84,14 @@ class SnippetTab extends React.Component {
<div styleName='group-section-control'>
<input
styleName='group-section-control-input'
value={this.state.currentSnippet ? this.state.currentSnippet.prefix : ''}
onChange={e => {
const newSnippet = Object.assign({}, this.state.currentSnippet)
newSnippet.prefix = e.target.value
this.setState({ currentSnippet: newSnippet })
this.handleSnippetNameOrPrefixChange()
}}
value={currentSnippet ? currentSnippet.prefix : ''}
onChange={e => { this.onSnippetNameOrPrefixChanged(e, 'prefix') }}
type='text' />
</div>
</div>
<div styleName='snippet-editor-section'>
<SnippetEditor
storageKey={this.props.storageKey}
storageKey={storageKey}
theme={config.editor.theme}
keyMap={config.editor.keyMap}
fontFamily={config.editor.fontFamily}

View File

@@ -1,4 +1,5 @@
@import('./Tab')
@import('./ConfigTab')
.root
padding 15px
@@ -96,6 +97,11 @@
height calc(100% - 200px)
position absolute
.snippets
height calc(100% - 8px)
overflow scroll
background: #f5f5f5
.snippet-item
height 50px
font-size 15px
@@ -121,3 +127,20 @@
height calc(100% - 200px)
position absolute
left 33%
.SnippetEditor
position absolute
width 100%
height 90%
body[data-theme="dark"]
.snippets
background: #2E3235
.snippet-item
color white
&::after
background rgba(255, 255, 255 0.1)
&:hover
background darken(#2E3235, 5)
.snippet-detail
color white

View File

@@ -12,7 +12,7 @@
"compile": "grunt compile",
"test": "PWD=$(pwd) NODE_ENV=test ava --serial",
"jest": "jest",
"fix": "npm run lint --fix",
"fix": "eslint . --fix",
"lint": "eslint .",
"dev-start": "concurrently --kill-others \"npm run webpack\" \"npm run hot\""
},
@@ -118,7 +118,7 @@
"eslint": "^3.13.1",
"eslint-config-standard": "^6.2.1",
"eslint-config-standard-jsx": "^3.2.0",
"eslint-plugin-react": "^7.2.0",
"eslint-plugin-react": "^7.8.2",
"eslint-plugin-standard": "^3.0.1",
"faker": "^3.1.0",
"grunt": "^0.4.5",

View File

@@ -28,7 +28,6 @@ test.serial('Delete a snippet', (t) => {
.then(function assert (data) {
data = data[0]
const snippets = JSON.parse(sander.readFileSync(snippetFile))
const snippet = snippets.find(currentSnippet => currentSnippet.id === data.id)
t.is(snippets.length, 0)
})
})

View File

@@ -2550,6 +2550,12 @@ doctrine@^2.0.0:
esutils "^2.0.2"
isarray "^1.0.0"
doctrine@^2.0.2:
version "2.1.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
dependencies:
esutils "^2.0.2"
dom-serializer@0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
@@ -2963,13 +2969,14 @@ eslint-plugin-promise@~3.4.0:
version "3.4.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.4.2.tgz#1be2793eafe2d18b5b123b8136c269f804fe7122"
eslint-plugin-react@^7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.2.0.tgz#25c77a4ec307e3eebb248ea3350960e372ab6406"
eslint-plugin-react@^7.8.2:
version "7.8.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.8.2.tgz#e95c9c47fece55d2303d1a67c9d01b930b88a51d"
dependencies:
doctrine "^2.0.0"
doctrine "^2.0.2"
has "^1.0.1"
jsx-ast-utils "^2.0.0"
jsx-ast-utils "^2.0.1"
prop-types "^15.6.0"
eslint-plugin-react@~6.7.1:
version "6.7.1"
@@ -5330,9 +5337,9 @@ jsx-ast-utils@^1.3.3:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1"
jsx-ast-utils@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.0.tgz#ec06a3d60cf307e5e119dac7bad81e89f096f0f8"
jsx-ast-utils@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz#e801b1b39985e20fffc87b40e3748080e2dcac7f"
dependencies:
array-includes "^3.0.3"
@@ -6962,6 +6969,14 @@ prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.7, prop-types@^15.5.8:
loose-envify "^1.3.1"
object-assign "^4.1.1"
prop-types@^15.6.0:
version "15.6.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca"
dependencies:
fbjs "^0.8.16"
loose-envify "^1.3.1"
object-assign "^4.1.1"
prop-types@~15.5.7:
version "15.5.10"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154"