mirror of
https://github.com/BoostIo/Boostnote
synced 2026-01-04 12:39:17 +00:00
Merge branch 'master' into fix-issue#2644-and-#2662
This commit is contained in:
@@ -39,12 +39,14 @@ class MarkdownNoteDetail extends React.Component {
|
||||
isMovingNote: false,
|
||||
note: Object.assign({
|
||||
title: '',
|
||||
content: ''
|
||||
content: '',
|
||||
linesHighlighted: []
|
||||
}, props.note),
|
||||
isLockButtonShown: false,
|
||||
isLocked: false,
|
||||
editorType: props.config.editor.type
|
||||
}
|
||||
|
||||
this.dispatchTimer = null
|
||||
|
||||
this.toggleLockButton = this.handleToggleLockButton.bind(this)
|
||||
@@ -71,7 +73,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
if (!this.state.isMovingNote && (isNewNote || hasDeletedTags)) {
|
||||
if (this.saveQueue != null) this.saveNow()
|
||||
this.setState({
|
||||
note: Object.assign({}, nextProps.note)
|
||||
note: Object.assign({linesHighlighted: []}, nextProps.note)
|
||||
}, () => {
|
||||
this.refs.content.reload()
|
||||
if (this.refs.tags) this.refs.tags.reset()
|
||||
@@ -361,6 +363,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
value={note.content}
|
||||
storageKey={note.storage}
|
||||
noteKey={note.key}
|
||||
linesHighlighted={note.linesHighlighted}
|
||||
onChange={this.handleUpdateContent.bind(this)}
|
||||
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
|
||||
/>
|
||||
@@ -371,6 +374,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
value={note.content}
|
||||
storageKey={note.storage}
|
||||
noteKey={note.key}
|
||||
linesHighlighted={note.linesHighlighted}
|
||||
onChange={this.handleUpdateContent.bind(this)}
|
||||
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
|
||||
/>
|
||||
|
||||
@@ -48,7 +48,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
note: Object.assign({
|
||||
description: ''
|
||||
}, props.note, {
|
||||
snippets: props.note.snippets.map((snippet) => Object.assign({}, snippet))
|
||||
snippets: props.note.snippets.map((snippet) => Object.assign({linesHighlighted: []}, snippet))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -76,8 +76,9 @@ class SnippetNoteDetail extends React.Component {
|
||||
const nextNote = Object.assign({
|
||||
description: ''
|
||||
}, nextProps.note, {
|
||||
snippets: nextProps.note.snippets.map((snippet) => Object.assign({}, snippet))
|
||||
snippets: nextProps.note.snippets.map((snippet) => Object.assign({linesHighlighted: []}, snippet))
|
||||
})
|
||||
|
||||
this.setState({
|
||||
snippetIndex: 0,
|
||||
note: nextNote
|
||||
@@ -410,6 +411,8 @@ class SnippetNoteDetail extends React.Component {
|
||||
return (e) => {
|
||||
const snippets = this.state.note.snippets.slice()
|
||||
snippets[index].content = this.refs['code-' + index].value
|
||||
snippets[index].linesHighlighted = e.options.linesHighlighted
|
||||
|
||||
this.setState(state => ({note: Object.assign(state.note, {snippets: snippets})}))
|
||||
this.setState(state => ({
|
||||
note: state.note
|
||||
@@ -602,7 +605,8 @@ class SnippetNoteDetail extends React.Component {
|
||||
note.snippets = note.snippets.concat([{
|
||||
name: '',
|
||||
mode: config.editor.snippetDefaultLanguage || 'text',
|
||||
content: ''
|
||||
content: '',
|
||||
linesHighlighted: []
|
||||
}])
|
||||
const snippetIndex = note.snippets.length - 1
|
||||
|
||||
@@ -692,10 +696,8 @@ class SnippetNoteDetail extends React.Component {
|
||||
|
||||
const viewList = note.snippets.map((snippet, index) => {
|
||||
const isActive = this.state.snippetIndex === index
|
||||
|
||||
let syntax = CodeMirror.findModeByName(convertModeName(snippet.mode))
|
||||
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
|
||||
|
||||
return <div styleName='tabView'
|
||||
key={index}
|
||||
style={{zIndex: isActive ? 5 : 4}}
|
||||
@@ -704,6 +706,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
? <MarkdownEditor styleName='tabView-content'
|
||||
value={snippet.content}
|
||||
config={config}
|
||||
linesHighlighted={snippet.linesHighlighted}
|
||||
onChange={(e) => this.handleCodeChange(index)(e)}
|
||||
ref={'code-' + index}
|
||||
ignorePreviewPointerEvents={this.props.ignorePreviewPointerEvents}
|
||||
@@ -712,6 +715,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
: <CodeEditor styleName='tabView-content'
|
||||
mode={snippet.mode}
|
||||
value={snippet.content}
|
||||
linesHighlighted={snippet.linesHighlighted}
|
||||
theme={config.editor.theme}
|
||||
fontFamily={config.editor.fontFamily}
|
||||
fontSize={editorFontSize}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
.tabList
|
||||
absolute left right
|
||||
top 55px
|
||||
top 70px
|
||||
height 30px
|
||||
display flex
|
||||
background-color $ui-noteDetail-backgroundColor
|
||||
@@ -57,6 +57,9 @@
|
||||
.tabList .tabButton
|
||||
navWhiteButtonColor()
|
||||
width 30px
|
||||
border-left 1px solid $ui-borderColor
|
||||
border-top 1px solid $ui-borderColor
|
||||
border-right 1px solid $ui-borderColor
|
||||
|
||||
.tabView
|
||||
absolute left right bottom
|
||||
@@ -98,17 +101,34 @@
|
||||
opacity 0
|
||||
transition 0.1s
|
||||
|
||||
body[data-theme="white"]
|
||||
body[data-theme="white"], body[data-theme="default"]
|
||||
.root
|
||||
box-shadow $note-detail-box-shadow
|
||||
border none
|
||||
|
||||
.tabButton
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||
color $ui-text-color
|
||||
transition 0.15s
|
||||
|
||||
body[data-theme="dark"]
|
||||
.root
|
||||
border-left 1px solid $ui-dark-borderColor
|
||||
background-color $ui-dark-noteDetail-backgroundColor
|
||||
box-shadow none
|
||||
|
||||
.tabList .tabButton
|
||||
border-color $ui-dark-borderColor
|
||||
&:hover
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||
|
||||
.tabButton
|
||||
&:hover
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||
color $ui-dark-text-color
|
||||
transition 0.15s
|
||||
|
||||
.body
|
||||
background-color $ui-dark-noteDetail-backgroundColor
|
||||
|
||||
@@ -118,7 +138,6 @@ body[data-theme="dark"]
|
||||
border 1px solid $ui-dark-borderColor
|
||||
|
||||
.tabList
|
||||
background-color $ui-button--active-backgroundColor
|
||||
background-color $ui-dark-noteDetail-backgroundColor
|
||||
|
||||
.tabList .list
|
||||
@@ -150,6 +169,15 @@ body[data-theme="solarized-dark"]
|
||||
color $ui-solarized-dark-text-color
|
||||
border 1px solid $ui-solarized-dark-borderColor
|
||||
|
||||
.tabList .tabButton
|
||||
border-color $ui-solarized-dark-borderColor
|
||||
|
||||
.tabButton
|
||||
&:hover
|
||||
color $ui-solarized-dark-button--active-color
|
||||
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
||||
transition 0.15s
|
||||
|
||||
.tabList
|
||||
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
||||
color $ui-solarized-dark-text-color
|
||||
@@ -167,6 +195,14 @@ body[data-theme="monokai"]
|
||||
color $ui-monokai-text-color
|
||||
border 1px solid $ui-monokai-borderColor
|
||||
|
||||
.tabList .tabButton
|
||||
border-color $ui-monokai-borderColor
|
||||
|
||||
.tabButton
|
||||
&:hover
|
||||
color $ui-monokai-text-color
|
||||
background-color $ui-monokai-noteDetail-backgroundColor
|
||||
|
||||
.tabList
|
||||
background-color $ui-monokai-noteDetail-backgroundColor
|
||||
color $ui-monokai-text-color
|
||||
@@ -184,6 +220,14 @@ body[data-theme="dracula"]
|
||||
color $ui-dracula-text-color
|
||||
border 1px solid $ui-dracula-borderColor
|
||||
|
||||
.tabList .tabButton
|
||||
border-color $ui-dracula-borderColor
|
||||
|
||||
.tabButton
|
||||
&:hover
|
||||
color $ui-dracula-text-color
|
||||
background-color $ui-dracula-noteDetail-backgroundColor
|
||||
|
||||
.tabList
|
||||
background-color $ui-dracula-noteDetail-backgroundColor
|
||||
color $ui-dracula-text-color
|
||||
@@ -96,12 +96,14 @@ class Main extends React.Component {
|
||||
{
|
||||
name: 'example.html',
|
||||
mode: 'html',
|
||||
content: "<html>\n<body>\n<h1 id='hello'>Enjoy Boostnote!</h1>\n</body>\n</html>"
|
||||
content: "<html>\n<body>\n<h1 id='hello'>Enjoy Boostnote!</h1>\n</body>\n</html>",
|
||||
linesHighlighted: []
|
||||
},
|
||||
{
|
||||
name: 'example.js',
|
||||
mode: 'javascript',
|
||||
content: "var boostnote = document.getElementById('enjoy').innerHTML\n\nconsole.log(boostnote)"
|
||||
content: "var boostnote = document.getElementById('enjoy').innerHTML\n\nconsole.log(boostnote)",
|
||||
linesHighlighted: []
|
||||
}
|
||||
]
|
||||
})
|
||||
@@ -234,8 +236,8 @@ class Main extends React.Component {
|
||||
if (this.state.isRightSliderFocused) {
|
||||
const offset = this.refs.body.getBoundingClientRect().left
|
||||
let newListWidth = e.pageX - offset
|
||||
if (newListWidth < 10) {
|
||||
newListWidth = 10
|
||||
if (newListWidth < 180) {
|
||||
newListWidth = 180
|
||||
} else if (newListWidth > 600) {
|
||||
newListWidth = 600
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import debounceRender from 'react-debounce-render'
|
||||
import styles from './NoteList.styl'
|
||||
import moment from 'moment'
|
||||
import _ from 'lodash'
|
||||
@@ -711,7 +710,8 @@ class NoteList extends React.Component {
|
||||
type: firstNote.type,
|
||||
folder: folder.key,
|
||||
title: firstNote.title + ' ' + i18n.__('copy'),
|
||||
content: firstNote.content
|
||||
content: firstNote.content,
|
||||
linesHighlighted: firstNote.linesHighlighted
|
||||
})
|
||||
.then((note) => {
|
||||
attachmentManagement.cloneAttachments(firstNote, note)
|
||||
@@ -1129,4 +1129,4 @@ NoteList.propTypes = {
|
||||
})
|
||||
}
|
||||
|
||||
export default debounceRender(CSSModules(NoteList, styles))
|
||||
export default CSSModules(NoteList, styles)
|
||||
|
||||
@@ -6,6 +6,7 @@ import _ from 'lodash'
|
||||
import ee from 'browser/main/lib/eventEmitter'
|
||||
import NewNoteButton from 'browser/main/NewNoteButton'
|
||||
import i18n from 'browser/lib/i18n'
|
||||
import debounce from 'lodash/debounce'
|
||||
|
||||
class TopBar extends React.Component {
|
||||
constructor (props) {
|
||||
@@ -25,6 +26,10 @@ class TopBar extends React.Component {
|
||||
}
|
||||
|
||||
this.codeInitHandler = this.handleCodeInit.bind(this)
|
||||
|
||||
this.updateKeyword = debounce(this.updateKeyword, 1000 / 60, {
|
||||
maxWait: 1000 / 8
|
||||
})
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
@@ -94,7 +99,6 @@ class TopBar extends React.Component {
|
||||
}
|
||||
|
||||
handleKeyUp (e) {
|
||||
const { router } = this.context
|
||||
// reset states
|
||||
this.setState({
|
||||
isConfirmTranslation: false
|
||||
@@ -106,21 +110,21 @@ class TopBar extends React.Component {
|
||||
isConfirmTranslation: true
|
||||
})
|
||||
const keyword = this.refs.searchInput.value
|
||||
router.push(`/searched/${encodeURIComponent(keyword)}`)
|
||||
this.setState({
|
||||
search: keyword
|
||||
})
|
||||
this.updateKeyword(keyword)
|
||||
}
|
||||
}
|
||||
|
||||
handleSearchChange (e) {
|
||||
const { router } = this.context
|
||||
const keyword = this.refs.searchInput.value
|
||||
if (this.state.isAlphabet || this.state.isConfirmTranslation) {
|
||||
router.push(`/searched/${encodeURIComponent(keyword)}`)
|
||||
const keyword = this.refs.searchInput.value
|
||||
this.updateKeyword(keyword)
|
||||
} else {
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
updateKeyword (keyword) {
|
||||
this.context.router.push(`/searched/${encodeURIComponent(keyword)}`)
|
||||
this.setState({
|
||||
search: keyword
|
||||
})
|
||||
|
||||
@@ -253,7 +253,15 @@ function escapeHtmlCharactersInCodeTag (splitWithCodeTag) {
|
||||
* @returns {String} postprocessed HTML in which all :storage references are mapped to the actual paths.
|
||||
*/
|
||||
function fixLocalURLS (renderedHTML, storagePath) {
|
||||
return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?"', 'g'), function (match) {
|
||||
/*
|
||||
A :storage reference is like `:storage/3b6f8bd6-4edd-4b15-96e0-eadc4475b564/f939b2c3.jpg`.
|
||||
|
||||
- `STORAGE_FOLDER_PLACEHOLDER` will match `:storage`
|
||||
- `(?:(?:\\\/|%5C)[\\w.]+)+` will match `/3b6f8bd6-4edd-4b15-96e0-eadc4475b564/f939b2c3.jpg`
|
||||
- `(?:\\\/|%5C)[\\w.]+` will either match `/3b6f8bd6-4edd-4b15-96e0-eadc4475b564` or `/f939b2c3.jpg`
|
||||
- `(?:\\\/|%5C)` match the path seperator. `\\\/` for posix systems and `%5C` for windows.
|
||||
*/
|
||||
return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '(?:(?:\\\/|%5C)[\\w.]+)+', 'g'), function (match) {
|
||||
var encodedPathSeparators = new RegExp(mdurl.encode(path.win32.sep) + '|' + mdurl.encode(path.posix.sep), 'g')
|
||||
return match.replace(encodedPathSeparators, path.sep).replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER, 'g'), 'file:///' + path.join(storagePath, DESTINATION_FOLDER))
|
||||
})
|
||||
|
||||
@@ -16,6 +16,7 @@ function validateInput (input) {
|
||||
switch (input.type) {
|
||||
case 'MARKDOWN_NOTE':
|
||||
if (!_.isString(input.content)) input.content = ''
|
||||
if (!_.isArray(input.linesHighlighted)) input.linesHighlighted = []
|
||||
break
|
||||
case 'SNIPPET_NOTE':
|
||||
if (!_.isString(input.description)) input.description = ''
|
||||
@@ -23,7 +24,8 @@ function validateInput (input) {
|
||||
input.snippets = [{
|
||||
name: '',
|
||||
mode: 'text',
|
||||
content: ''
|
||||
content: '',
|
||||
linesHighlighted: []
|
||||
}]
|
||||
}
|
||||
break
|
||||
|
||||
@@ -9,7 +9,8 @@ function createSnippet (snippetFile) {
|
||||
id: crypto.randomBytes(16).toString('hex'),
|
||||
name: 'Unnamed snippet',
|
||||
prefix: [],
|
||||
content: ''
|
||||
content: '',
|
||||
linesHighlighted: []
|
||||
}
|
||||
fetchSnippet(null, snippetFile).then((snippets) => {
|
||||
snippets.push(newSnippet)
|
||||
|
||||
@@ -69,7 +69,8 @@ function importAll (storage, data) {
|
||||
isStarred: false,
|
||||
title: article.title,
|
||||
content: '# ' + article.title + '\n\n' + article.content,
|
||||
key: noteKey
|
||||
key: noteKey,
|
||||
linesHighlighted: article.linesHighlighted
|
||||
}
|
||||
notes.push(newNote)
|
||||
} else {
|
||||
@@ -87,7 +88,8 @@ function importAll (storage, data) {
|
||||
snippets: [{
|
||||
name: article.mode,
|
||||
mode: article.mode,
|
||||
content: article.content
|
||||
content: article.content,
|
||||
linesHighlighted: article.linesHighlighted
|
||||
}]
|
||||
}
|
||||
notes.push(newNote)
|
||||
|
||||
@@ -39,6 +39,9 @@ function validateInput (input) {
|
||||
if (input.content != null) {
|
||||
if (!_.isString(input.content)) validatedInput.content = ''
|
||||
else validatedInput.content = input.content
|
||||
|
||||
if (!_.isArray(input.linesHighlighted)) validatedInput.linesHighlighted = []
|
||||
else validatedInput.linesHighlighted = input.linesHighlighted
|
||||
}
|
||||
return validatedInput
|
||||
case 'SNIPPET_NOTE':
|
||||
@@ -51,7 +54,8 @@ function validateInput (input) {
|
||||
validatedInput.snippets = [{
|
||||
name: '',
|
||||
mode: 'text',
|
||||
content: ''
|
||||
content: '',
|
||||
linesHighlighted: []
|
||||
}]
|
||||
} else {
|
||||
validatedInput.snippets = input.snippets
|
||||
@@ -96,12 +100,14 @@ function updateNote (storageKey, noteKey, input) {
|
||||
snippets: [{
|
||||
name: '',
|
||||
mode: 'text',
|
||||
content: ''
|
||||
content: '',
|
||||
linesHighlighted: []
|
||||
}]
|
||||
}
|
||||
: {
|
||||
type: 'MARKDOWN_NOTE',
|
||||
content: ''
|
||||
content: '',
|
||||
linesHighlighted: []
|
||||
}
|
||||
noteData.title = ''
|
||||
if (storage.folders.length === 0) throw new Error('Failed to restore note: No folder exists.')
|
||||
|
||||
@@ -12,7 +12,8 @@ function updateSnippet (snippet, snippetFile) {
|
||||
if (
|
||||
currentSnippet.name === snippet.name &&
|
||||
currentSnippet.prefix === snippet.prefix &&
|
||||
currentSnippet.content === snippet.content
|
||||
currentSnippet.content === snippet.content &&
|
||||
currentSnippet.linesHighlighted === snippet.linesHighlighted
|
||||
) {
|
||||
// if everything is the same then don't write to disk
|
||||
resolve(snippets)
|
||||
@@ -20,6 +21,7 @@ function updateSnippet (snippet, snippetFile) {
|
||||
currentSnippet.name = snippet.name
|
||||
currentSnippet.prefix = snippet.prefix
|
||||
currentSnippet.content = snippet.content
|
||||
currentSnippet.linesHighlighted = (snippet.linesHighlighted)
|
||||
fs.writeFile(snippetFile || consts.SNIPPET_FILE, JSON.stringify(snippets, null, 4), (err) => {
|
||||
if (err) reject(err)
|
||||
resolve(snippets)
|
||||
|
||||
@@ -73,6 +73,11 @@ class InfoTab extends React.Component {
|
||||
<div styleName='header--sub'>{i18n.__('Community')}</div>
|
||||
<div styleName='top'>
|
||||
<ul styleName='list'>
|
||||
<li>
|
||||
<a href='https://issuehunt.io/repos/53266139'
|
||||
onClick={(e) => this.handleLinkClick(e)}
|
||||
>{i18n.__('Bounty on IssueHunt')}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href='https://boostnote.io/#subscribe'
|
||||
onClick={(e) => this.handleLinkClick(e)}
|
||||
|
||||
Reference in New Issue
Block a user