mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 17:56:25 +00:00
Merge branch 'master' into OnBlur_Throws_Exceptions_On_Snippet_Notes
This commit is contained in:
@@ -322,8 +322,9 @@ export default class CodeEditor extends React.Component {
|
|||||||
const cursor = editor.getCursor()
|
const cursor = editor.getCursor()
|
||||||
const LinkWithTitle = `[${parsedResponse.title}](${pastedTxt})`
|
const LinkWithTitle = `[${parsedResponse.title}](${pastedTxt})`
|
||||||
const newValue = value.replace(taggedUrl, LinkWithTitle)
|
const newValue = value.replace(taggedUrl, LinkWithTitle)
|
||||||
|
const newCursor = Object.assign({}, cursor, { ch: cursor.ch + newValue.length - value.length })
|
||||||
editor.setValue(newValue)
|
editor.setValue(newValue)
|
||||||
editor.setCursor(cursor)
|
editor.setCursor(newCursor)
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
const value = editor.getValue()
|
const value = editor.getValue()
|
||||||
const newValue = value.replace(taggedUrl, pastedTxt)
|
const newValue = value.replace(taggedUrl, pastedTxt)
|
||||||
|
|||||||
@@ -52,6 +52,16 @@ function buildStyle (fontFamily, fontSize, codeBlockFontFamily, lineNumber, scro
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
}
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Material Icons';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: local('Material Icons'),
|
||||||
|
local('MaterialIcons-Regular'),
|
||||||
|
url('${appPath}/resources/fonts/MaterialIcons-Regular.woff2') format('woff2'),
|
||||||
|
url('${appPath}/resources/fonts/MaterialIcons-Regular.woff') format('woff'),
|
||||||
|
url('${appPath}/resources/fonts/MaterialIcons-Regular.ttf') format('truetype');
|
||||||
|
}
|
||||||
${markdownStyle}
|
${markdownStyle}
|
||||||
body {
|
body {
|
||||||
font-family: '${fontFamily.join("','")}';
|
font-family: '${fontFamily.join("','")}';
|
||||||
@@ -132,7 +142,6 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
this.mouseUpHandler = (e) => this.handleMouseUp(e)
|
this.mouseUpHandler = (e) => this.handleMouseUp(e)
|
||||||
this.DoubleClickHandler = (e) => this.handleDoubleClick(e)
|
this.DoubleClickHandler = (e) => this.handleDoubleClick(e)
|
||||||
this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, {leading: false, trailing: true})
|
this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, {leading: false, trailing: true})
|
||||||
this.anchorClickHandler = (e) => this.handlePreviewAnchorClick(e)
|
|
||||||
this.checkboxClickHandler = (e) => this.handleCheckboxClick(e)
|
this.checkboxClickHandler = (e) => this.handleCheckboxClick(e)
|
||||||
this.saveAsTextHandler = () => this.handleSaveAsText()
|
this.saveAsTextHandler = () => this.handleSaveAsText()
|
||||||
this.saveAsMdHandler = () => this.handleSaveAsMd()
|
this.saveAsMdHandler = () => this.handleSaveAsMd()
|
||||||
@@ -153,22 +162,6 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePreviewAnchorClick (e) {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
|
|
||||||
const anchor = e.target.closest('a')
|
|
||||||
const href = anchor.getAttribute('href')
|
|
||||||
if (_.isString(href) && href.match(/^#/)) {
|
|
||||||
const targetElement = this.refs.root.contentWindow.document.getElementById(href.substring(1, href.length))
|
|
||||||
if (targetElement != null) {
|
|
||||||
this.getWindow().scrollTo(0, targetElement.offsetTop)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
shell.openExternal(href)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleCheckboxClick (e) {
|
handleCheckboxClick (e) {
|
||||||
this.props.onCheckboxClick(e)
|
this.props.onCheckboxClick(e)
|
||||||
}
|
}
|
||||||
@@ -390,9 +383,6 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rewriteIframe () {
|
rewriteIframe () {
|
||||||
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
|
|
||||||
el.removeEventListener('click', this.anchorClickHandler)
|
|
||||||
})
|
|
||||||
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('input[type="checkbox"]'), (el) => {
|
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('input[type="checkbox"]'), (el) => {
|
||||||
el.removeEventListener('click', this.checkboxClickHandler)
|
el.removeEventListener('click', this.checkboxClickHandler)
|
||||||
})
|
})
|
||||||
@@ -415,16 +405,12 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
let renderedHTML = this.markdown.render(value)
|
let renderedHTML = this.markdown.render(value)
|
||||||
this.refs.root.contentWindow.document.body.innerHTML = attachmentManagement.fixLocalURLS(renderedHTML, storagePath)
|
this.refs.root.contentWindow.document.body.innerHTML = attachmentManagement.fixLocalURLS(renderedHTML, storagePath)
|
||||||
|
|
||||||
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
|
|
||||||
this.fixDecodedURI(el)
|
|
||||||
el.addEventListener('click', this.anchorClickHandler)
|
|
||||||
})
|
|
||||||
|
|
||||||
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('input[type="checkbox"]'), (el) => {
|
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('input[type="checkbox"]'), (el) => {
|
||||||
el.addEventListener('click', this.checkboxClickHandler)
|
el.addEventListener('click', this.checkboxClickHandler)
|
||||||
})
|
})
|
||||||
|
|
||||||
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
|
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
|
||||||
|
this.fixDecodedURI(el)
|
||||||
el.addEventListener('click', this.linkClickHandler)
|
el.addEventListener('click', this.linkClickHandler)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -475,7 +461,7 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
el.innerHTML = ''
|
el.innerHTML = ''
|
||||||
diagram.drawSVG(el, opts)
|
diagram.drawSVG(el, opts)
|
||||||
_.forEach(el.querySelectorAll('a'), (el) => {
|
_.forEach(el.querySelectorAll('a'), (el) => {
|
||||||
el.addEventListener('click', this.anchorClickHandler)
|
el.addEventListener('click', this.linkClickHandler)
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
@@ -491,7 +477,7 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
el.innerHTML = ''
|
el.innerHTML = ''
|
||||||
diagram.drawSVG(el, {theme: 'simple'})
|
diagram.drawSVG(el, {theme: 'simple'})
|
||||||
_.forEach(el.querySelectorAll('a'), (el) => {
|
_.forEach(el.querySelectorAll('a'), (el) => {
|
||||||
el.addEventListener('click', this.anchorClickHandler)
|
el.addEventListener('click', this.linkClickHandler)
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
@@ -598,6 +584,7 @@ MarkdownPreview.propTypes = {
|
|||||||
onDoubleClick: PropTypes.func,
|
onDoubleClick: PropTypes.func,
|
||||||
onMouseUp: PropTypes.func,
|
onMouseUp: PropTypes.func,
|
||||||
onMouseDown: PropTypes.func,
|
onMouseDown: PropTypes.func,
|
||||||
|
onContextMenu: PropTypes.func,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
value: PropTypes.string,
|
value: PropTypes.string,
|
||||||
showCopyNotification: PropTypes.bool,
|
showCopyNotification: PropTypes.bool,
|
||||||
|
|||||||
@@ -68,10 +68,9 @@
|
|||||||
.menu-button-label
|
.menu-button-label
|
||||||
position fixed
|
position fixed
|
||||||
display inline-block
|
display inline-block
|
||||||
height 32px
|
height 36px
|
||||||
left 44px
|
left 44px
|
||||||
padding 0 10px
|
padding 0 10px
|
||||||
margin-top -8px
|
|
||||||
margin-left 0
|
margin-left 0
|
||||||
overflow ellipsis
|
overflow ellipsis
|
||||||
z-index 10
|
z-index 10
|
||||||
|
|||||||
@@ -58,8 +58,8 @@
|
|||||||
opacity 0
|
opacity 0
|
||||||
border-top-right-radius 2px
|
border-top-right-radius 2px
|
||||||
border-bottom-right-radius 2px
|
border-bottom-right-radius 2px
|
||||||
height 26px
|
height 34px
|
||||||
line-height 26px
|
line-height 32px
|
||||||
|
|
||||||
.folderList-item:hover, .folderList-item--active:hover
|
.folderList-item:hover, .folderList-item--active:hover
|
||||||
.folderList-item-tooltip
|
.folderList-item-tooltip
|
||||||
|
|||||||
@@ -293,6 +293,82 @@ kbd
|
|||||||
line-height 1
|
line-height 1
|
||||||
padding 3px 5px
|
padding 3px 5px
|
||||||
|
|
||||||
|
$admonition
|
||||||
|
box-shadow 0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2)
|
||||||
|
position relative
|
||||||
|
margin 1.5625em 0
|
||||||
|
padding 0 1.2rem
|
||||||
|
border-left .4rem solid #448aff
|
||||||
|
border-radius .2rem
|
||||||
|
overflow auto
|
||||||
|
|
||||||
|
html .admonition>:last-child
|
||||||
|
margin-bottom 1.2rem
|
||||||
|
|
||||||
|
.admonition .admonition
|
||||||
|
margin 1em 0
|
||||||
|
|
||||||
|
.admonition p
|
||||||
|
margin-top: 0.5em
|
||||||
|
|
||||||
|
$admonition-icon
|
||||||
|
position absolute
|
||||||
|
left 1.2rem
|
||||||
|
font-family: "Material Icons"
|
||||||
|
font-size: 24px
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1;
|
||||||
|
text-transform: none;
|
||||||
|
letter-spacing: normal;
|
||||||
|
word-wrap: normal;
|
||||||
|
white-space: nowrap;
|
||||||
|
direction: ltr;
|
||||||
|
|
||||||
|
/* Support for all WebKit browsers. */
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
/* Support for Safari and Chrome. */
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
|
||||||
|
/* Support for Firefox. */
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
|
||||||
|
/* Support for IE. */
|
||||||
|
font-feature-settings: 'liga';
|
||||||
|
|
||||||
|
$admonition-title
|
||||||
|
margin 0 -1.2rem
|
||||||
|
padding .8rem 1.2rem .8rem 4rem
|
||||||
|
border-bottom .1rem solid rgba(68,138,255,.1)
|
||||||
|
background-color rgba(68,138,255,.1)
|
||||||
|
font-weight 700
|
||||||
|
|
||||||
|
.admonition>.admonition-title:last-child
|
||||||
|
margin-bottom 0
|
||||||
|
|
||||||
|
admonition_types = {
|
||||||
|
note: {border-color: #448aff, title-color: rgba(68,138,255,.1), icon: "note"},
|
||||||
|
hint: {border-color: #00bfa5, title-color: rgba(0,191,165,.1), icon: "info"},
|
||||||
|
danger: {border-color: #ff1744, title-color: rgba(255,23,68,.1), icon: "block"},
|
||||||
|
caution: {border-color: #ff9100, title-color: rgba(255,145,0,.1), icon: "warning"},
|
||||||
|
error: {border-color: #ff1744, title-color: rgba(255,23,68,.1), icon: "error"},
|
||||||
|
attention: {border-color: #64dd17, title-color: rgba(100,221,23,.1), icon: "priority_high"}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, val in admonition_types
|
||||||
|
.admonition.{name}
|
||||||
|
@extend $admonition
|
||||||
|
border-left-color: val[border-color]
|
||||||
|
|
||||||
|
.admonition.{name}>.admonition-title
|
||||||
|
@extend $admonition-title
|
||||||
|
border-bottom-color: .1rem solid val[title-color]
|
||||||
|
background-color: val[title-color]
|
||||||
|
|
||||||
|
.admonition.{name}>.admonition-title:before
|
||||||
|
@extend $admonition-icon
|
||||||
|
color: val[border-color]
|
||||||
|
content: val[icon]
|
||||||
|
|
||||||
themeDarkBackground = darken(#21252B, 10%)
|
themeDarkBackground = darken(#21252B, 10%)
|
||||||
themeDarkText = #f9f9f9
|
themeDarkText = #f9f9f9
|
||||||
themeDarkBorder = lighten(themeDarkBackground, 20%)
|
themeDarkBorder = lighten(themeDarkBackground, 20%)
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ class Markdown {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.md.use(require('markdown-it-kbd'))
|
this.md.use(require('markdown-it-kbd'))
|
||||||
|
this.md.use(require('markdown-it-admonition'))
|
||||||
|
|
||||||
const deflate = require('markdown-it-plantuml/lib/deflate')
|
const deflate = require('markdown-it-plantuml/lib/deflate')
|
||||||
this.md.use(require('markdown-it-plantuml'), '', {
|
this.md.use(require('markdown-it-plantuml'), '', {
|
||||||
|
|||||||
@@ -54,7 +54,25 @@ export function escapeHtmlCharacters (text) {
|
|||||||
: html
|
: html
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isObjectEqual (a, b) {
|
||||||
|
const aProps = Object.getOwnPropertyNames(a)
|
||||||
|
const bProps = Object.getOwnPropertyNames(b)
|
||||||
|
|
||||||
|
if (aProps.length !== bProps.length) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < aProps.length; i++) {
|
||||||
|
const propName = aProps[i]
|
||||||
|
if (a[propName] !== b[propName]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
lastFindInArray,
|
lastFindInArray,
|
||||||
escapeHtmlCharacters
|
escapeHtmlCharacters,
|
||||||
|
isObjectEqual
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
ee.on('topbar:togglelockbutton', this.toggleLockButton)
|
ee.on('topbar:togglelockbutton', this.toggleLockButton)
|
||||||
|
ee.on('topbar:togglemodebutton', () => {
|
||||||
|
const reversedType = this.state.editorType === 'SPLIT' ? 'EDITOR_PREVIEW' : 'SPLIT'
|
||||||
|
this.handleSwitchMode(reversedType)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps (nextProps) {
|
||||||
|
|||||||
@@ -44,16 +44,9 @@ class TagSelect extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
removeLastTag () {
|
removeLastTag () {
|
||||||
let { value } = this.props
|
this.removeTagByCallback((value) => {
|
||||||
|
|
||||||
value = _.isArray(value)
|
|
||||||
? value.slice()
|
|
||||||
: []
|
|
||||||
value.pop()
|
value.pop()
|
||||||
value = _.uniq(value)
|
})
|
||||||
|
|
||||||
this.value = value
|
|
||||||
this.props.onChange()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reset () {
|
reset () {
|
||||||
@@ -96,16 +89,23 @@ class TagSelect extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleTagRemoveButtonClick (tag) {
|
handleTagRemoveButtonClick (tag) {
|
||||||
return (e) => {
|
this.removeTagByCallback((value, tag) => {
|
||||||
|
value.splice(value.indexOf(tag), 1)
|
||||||
|
}, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTagByCallback (callback, tag = null) {
|
||||||
let { value } = this.props
|
let { value } = this.props
|
||||||
|
|
||||||
value.splice(value.indexOf(tag), 1)
|
value = _.isArray(value)
|
||||||
|
? value.slice()
|
||||||
|
: []
|
||||||
|
callback(value, tag)
|
||||||
value = _.uniq(value)
|
value = _.uniq(value)
|
||||||
|
|
||||||
this.value = value
|
this.value = value
|
||||||
this.props.onChange()
|
this.props.onChange()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { value, className } = this.props
|
const { value, className } = this.props
|
||||||
@@ -118,7 +118,7 @@ class TagSelect extends React.Component {
|
|||||||
>
|
>
|
||||||
<span styleName='tag-label'>#{tag}</span>
|
<span styleName='tag-label'>#{tag}</span>
|
||||||
<button styleName='tag-removeButton'
|
<button styleName='tag-removeButton'
|
||||||
onClick={(e) => this.handleTagRemoveButtonClick(tag)(e)}
|
onClick={(e) => this.handleTagRemoveButtonClick(tag)}
|
||||||
>
|
>
|
||||||
<img className='tag-removeButton-icon' src='../resources/icon/icon-x.svg' width='8px' />
|
<img className='tag-removeButton-icon' src='../resources/icon/icon-x.svg' width='8px' />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { hashHistory } from 'react-router'
|
|||||||
import store from 'browser/main/store'
|
import store from 'browser/main/store'
|
||||||
import i18n from 'browser/lib/i18n'
|
import i18n from 'browser/lib/i18n'
|
||||||
import { getLocales } from 'browser/lib/Languages'
|
import { getLocales } from 'browser/lib/Languages'
|
||||||
|
import applyShortcuts from 'browser/main/lib/shortcutManager'
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const electron = require('electron')
|
const electron = require('electron')
|
||||||
const { remote } = electron
|
const { remote } = electron
|
||||||
@@ -159,7 +160,7 @@ class Main extends React.Component {
|
|||||||
} else {
|
} else {
|
||||||
i18n.setLocale('en')
|
i18n.setLocale('en')
|
||||||
}
|
}
|
||||||
|
applyShortcuts()
|
||||||
// Reload all data
|
// Reload all data
|
||||||
dataApi.init()
|
dataApi.init()
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import moment from 'moment'
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import ee from 'browser/main/lib/eventEmitter'
|
import ee from 'browser/main/lib/eventEmitter'
|
||||||
import dataApi from 'browser/main/lib/dataApi'
|
import dataApi from 'browser/main/lib/dataApi'
|
||||||
|
import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement'
|
||||||
import ConfigManager from 'browser/main/lib/ConfigManager'
|
import ConfigManager from 'browser/main/lib/ConfigManager'
|
||||||
import NoteItem from 'browser/components/NoteItem'
|
import NoteItem from 'browser/components/NoteItem'
|
||||||
import NoteItemSimple from 'browser/components/NoteItemSimple'
|
import NoteItemSimple from 'browser/components/NoteItemSimple'
|
||||||
@@ -662,6 +663,10 @@ class NoteList extends React.Component {
|
|||||||
title: firstNote.title + ' ' + i18n.__('copy'),
|
title: firstNote.title + ' ' + i18n.__('copy'),
|
||||||
content: firstNote.content
|
content: firstNote.content
|
||||||
})
|
})
|
||||||
|
.then((note) => {
|
||||||
|
attachmentManagement.cloneAttachments(firstNote, note)
|
||||||
|
return note
|
||||||
|
})
|
||||||
.then((note) => {
|
.then((note) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'UPDATE_NOTE',
|
type: 'UPDATE_NOTE',
|
||||||
|
|||||||
@@ -49,3 +49,4 @@ body[data-theme="dark"]
|
|||||||
border-radius 2px
|
border-radius 2px
|
||||||
opacity 0
|
opacity 0
|
||||||
transition 0.1s
|
transition 0.1s
|
||||||
|
white-space nowrap
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
height 36px
|
height 36px
|
||||||
padding-left 25px
|
padding-left 25px
|
||||||
padding-right 15px
|
padding-right 15px
|
||||||
line-height 22px
|
line-height 36px
|
||||||
cursor pointer
|
cursor pointer
|
||||||
font-size 14px
|
font-size 14px
|
||||||
border none
|
border none
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
border-radius 2px
|
border-radius 2px
|
||||||
opacity 0
|
opacity 0
|
||||||
transition 0.1s
|
transition 0.1s
|
||||||
|
white-space nowrap
|
||||||
|
|
||||||
body[data-theme="white"]
|
body[data-theme="white"]
|
||||||
.non-active-button
|
.non-active-button
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import RcParser from 'browser/lib/RcParser'
|
import RcParser from 'browser/lib/RcParser'
|
||||||
import i18n from 'browser/lib/i18n'
|
import i18n from 'browser/lib/i18n'
|
||||||
|
import ee from 'browser/main/lib/eventEmitter'
|
||||||
|
|
||||||
const OSX = global.process.platform === 'darwin'
|
const OSX = global.process.platform === 'darwin'
|
||||||
const win = global.process.platform === 'win32'
|
const win = global.process.platform === 'win32'
|
||||||
@@ -20,7 +21,8 @@ export const DEFAULT_CONFIG = {
|
|||||||
listStyle: 'DEFAULT', // 'DEFAULT', 'SMALL'
|
listStyle: 'DEFAULT', // 'DEFAULT', 'SMALL'
|
||||||
amaEnabled: true,
|
amaEnabled: true,
|
||||||
hotkey: {
|
hotkey: {
|
||||||
toggleMain: OSX ? 'Cmd + Alt + L' : 'Super + Alt + E'
|
toggleMain: OSX ? 'Cmd + Alt + L' : 'Super + Alt + E',
|
||||||
|
toggleMode: OSX ? 'Cmd + M' : 'Ctrl + M'
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
language: 'en',
|
language: 'en',
|
||||||
@@ -167,6 +169,7 @@ function set (updates) {
|
|||||||
ipcRenderer.send('config-renew', {
|
ipcRenderer.send('config-renew', {
|
||||||
config: get()
|
config: get()
|
||||||
})
|
})
|
||||||
|
ee.emit('config-renew')
|
||||||
}
|
}
|
||||||
|
|
||||||
function assignConfigValues (originalConfig, rcConfig) {
|
function assignConfigValues (originalConfig, rcConfig) {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ function copyAttachment (sourceFilePath, storageKey, noteKey, useRandomName = tr
|
|||||||
|
|
||||||
const targetStorage = findStorage.findStorage(storageKey)
|
const targetStorage = findStorage.findStorage(storageKey)
|
||||||
|
|
||||||
const inputFile = fs.createReadStream(sourceFilePath)
|
const inputFileStream = fs.createReadStream(sourceFilePath)
|
||||||
let destinationName
|
let destinationName
|
||||||
if (useRandomName) {
|
if (useRandomName) {
|
||||||
destinationName = `${uniqueSlug()}${path.extname(sourceFilePath)}`
|
destinationName = `${uniqueSlug()}${path.extname(sourceFilePath)}`
|
||||||
@@ -52,8 +52,10 @@ function copyAttachment (sourceFilePath, storageKey, noteKey, useRandomName = tr
|
|||||||
const destinationDir = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey)
|
const destinationDir = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey)
|
||||||
createAttachmentDestinationFolder(targetStorage.path, noteKey)
|
createAttachmentDestinationFolder(targetStorage.path, noteKey)
|
||||||
const outputFile = fs.createWriteStream(path.join(destinationDir, destinationName))
|
const outputFile = fs.createWriteStream(path.join(destinationDir, destinationName))
|
||||||
inputFile.pipe(outputFile)
|
inputFileStream.pipe(outputFile)
|
||||||
|
inputFileStream.on('end', () => {
|
||||||
resolve(destinationName)
|
resolve(destinationName)
|
||||||
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return reject(e)
|
return reject(e)
|
||||||
}
|
}
|
||||||
@@ -149,7 +151,7 @@ function handlePastImageEvent (codeEditor, storageKey, noteKey, dataTransferItem
|
|||||||
base64data = reader.result.replace(/^data:image\/png;base64,/, '')
|
base64data = reader.result.replace(/^data:image\/png;base64,/, '')
|
||||||
base64data += base64data.replace('+', ' ')
|
base64data += base64data.replace('+', ' ')
|
||||||
const binaryData = new Buffer(base64data, 'base64').toString('binary')
|
const binaryData = new Buffer(base64data, 'base64').toString('binary')
|
||||||
fs.writeFile(imagePath, binaryData, 'binary')
|
fs.writeFileSync(imagePath, binaryData, 'binary')
|
||||||
const imageMd = generateAttachmentMarkdown(imageName, imagePath, true)
|
const imageMd = generateAttachmentMarkdown(imageName, imagePath, true)
|
||||||
codeEditor.insertAttachmentMd(imageMd)
|
codeEditor.insertAttachmentMd(imageMd)
|
||||||
}
|
}
|
||||||
@@ -174,7 +176,7 @@ function getAttachmentsInContent (markdownContent) {
|
|||||||
* @returns {String[]} Absolute paths of the referenced attachments
|
* @returns {String[]} Absolute paths of the referenced attachments
|
||||||
*/
|
*/
|
||||||
function getAbsolutePathsOfAttachmentsInContent (markdownContent, storagePath) {
|
function getAbsolutePathsOfAttachmentsInContent (markdownContent, storagePath) {
|
||||||
const temp = getAttachmentsInContent(markdownContent)
|
const temp = getAttachmentsInContent(markdownContent) || []
|
||||||
const result = []
|
const result = []
|
||||||
for (const relativePath of temp) {
|
for (const relativePath of temp) {
|
||||||
result.push(relativePath.replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER, 'g'), path.join(storagePath, DESTINATION_FOLDER)))
|
result.push(relativePath.replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER, 'g'), path.join(storagePath, DESTINATION_FOLDER)))
|
||||||
@@ -198,8 +200,19 @@ function moveAttachments (oldPath, newPath, noteKey, newNoteKey, noteContent) {
|
|||||||
if (fse.existsSync(src)) {
|
if (fse.existsSync(src)) {
|
||||||
fse.moveSync(src, dest)
|
fse.moveSync(src, dest)
|
||||||
}
|
}
|
||||||
|
return replaceNoteKeyWithNewNoteKey(noteContent, noteKey, newNoteKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies the given content so that in all attachment references the oldNoteKey is replaced by the new one
|
||||||
|
* @param noteContent content that should be modified
|
||||||
|
* @param oldNoteKey note key to be replaced
|
||||||
|
* @param newNoteKey note key serving as a replacement
|
||||||
|
* @returns {String} modified note content
|
||||||
|
*/
|
||||||
|
function replaceNoteKeyWithNewNoteKey (noteContent, oldNoteKey, newNoteKey) {
|
||||||
if (noteContent) {
|
if (noteContent) {
|
||||||
return noteContent.replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + escapeStringRegexp(path.sep) + noteKey, 'g'), path.join(STORAGE_FOLDER_PLACEHOLDER, newNoteKey))
|
return noteContent.replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + escapeStringRegexp(path.sep) + oldNoteKey, 'g'), path.join(STORAGE_FOLDER_PLACEHOLDER, newNoteKey))
|
||||||
}
|
}
|
||||||
return noteContent
|
return noteContent
|
||||||
}
|
}
|
||||||
@@ -270,6 +283,33 @@ function deleteAttachmentsNotPresentInNote (markdownContent, storageKey, noteKey
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones the attachments of a given note.
|
||||||
|
* Copies the attachments to their new destination and updates the content of the new note so that the attachment-links again point to the correct destination.
|
||||||
|
* @param oldNote Note that is being cloned
|
||||||
|
* @param newNote Clone of the note
|
||||||
|
*/
|
||||||
|
function cloneAttachments (oldNote, newNote) {
|
||||||
|
if (newNote.type === 'MARKDOWN_NOTE') {
|
||||||
|
const oldStorage = findStorage.findStorage(oldNote.storage)
|
||||||
|
const newStorage = findStorage.findStorage(newNote.storage)
|
||||||
|
const attachmentsPaths = getAbsolutePathsOfAttachmentsInContent(oldNote.content, oldStorage.path) || []
|
||||||
|
|
||||||
|
const destinationFolder = path.join(newStorage.path, DESTINATION_FOLDER, newNote.key)
|
||||||
|
if (!sander.existsSync(destinationFolder)) {
|
||||||
|
sander.mkdirSync(destinationFolder)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const attachment of attachmentsPaths) {
|
||||||
|
const destination = path.join(newStorage.path, DESTINATION_FOLDER, newNote.key, path.basename(attachment))
|
||||||
|
sander.copyFileSync(attachment).to(destination)
|
||||||
|
}
|
||||||
|
newNote.content = replaceNoteKeyWithNewNoteKey(newNote.content, oldNote.key, newNote.key)
|
||||||
|
} else {
|
||||||
|
console.debug('Cloning of the attachment was skipped since it only works for MARKDOWN_NOTEs')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
copyAttachment,
|
copyAttachment,
|
||||||
fixLocalURLS,
|
fixLocalURLS,
|
||||||
@@ -282,6 +322,7 @@ module.exports = {
|
|||||||
deleteAttachmentFolder,
|
deleteAttachmentFolder,
|
||||||
deleteAttachmentsNotPresentInNote,
|
deleteAttachmentsNotPresentInNote,
|
||||||
moveAttachments,
|
moveAttachments,
|
||||||
|
cloneAttachments,
|
||||||
STORAGE_FOLDER_PLACEHOLDER,
|
STORAGE_FOLDER_PLACEHOLDER,
|
||||||
DESTINATION_FOLDER
|
DESTINATION_FOLDER
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ const resolveStorageNotes = require('./resolveStorageNotes')
|
|||||||
const CSON = require('@rokt33r/season')
|
const CSON = require('@rokt33r/season')
|
||||||
const sander = require('sander')
|
const sander = require('sander')
|
||||||
const { findStorage } = require('browser/lib/findStorage')
|
const { findStorage } = require('browser/lib/findStorage')
|
||||||
|
const deleteSingleNote = require('./deleteNote')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String} storageKey
|
* @param {String} storageKey
|
||||||
@@ -49,11 +50,7 @@ function deleteFolder (storageKey, folderKey) {
|
|||||||
|
|
||||||
const deleteAllNotes = targetNotes
|
const deleteAllNotes = targetNotes
|
||||||
.map(function deleteNote (note) {
|
.map(function deleteNote (note) {
|
||||||
const notePath = path.join(storage.path, 'notes', note.key + '.cson')
|
return deleteSingleNote(storageKey, note.key)
|
||||||
return sander.unlink(notePath)
|
|
||||||
.catch(function (err) {
|
|
||||||
console.warn('Failed to delete', notePath, err)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
return Promise.all(deleteAllNotes)
|
return Promise.all(deleteAllNotes)
|
||||||
.then(() => storage)
|
.then(() => storage)
|
||||||
|
|||||||
7
browser/main/lib/shortcut.js
Normal file
7
browser/main/lib/shortcut.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import ee from 'browser/main/lib/eventEmitter'
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
'toggleMode': () => {
|
||||||
|
ee.emit('topbar:togglemodebutton')
|
||||||
|
}
|
||||||
|
}
|
||||||
40
browser/main/lib/shortcutManager.js
Normal file
40
browser/main/lib/shortcutManager.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import Mousetrap from 'mousetrap'
|
||||||
|
import CM from 'browser/main/lib/ConfigManager'
|
||||||
|
import ee from 'browser/main/lib/eventEmitter'
|
||||||
|
import { isObjectEqual } from 'browser/lib/utils'
|
||||||
|
require('mousetrap-global-bind')
|
||||||
|
const functions = require('./shortcut')
|
||||||
|
|
||||||
|
let shortcuts = CM.get().hotkey
|
||||||
|
|
||||||
|
ee.on('config-renew', function () {
|
||||||
|
// only update if hotkey changed !
|
||||||
|
const newHotkey = CM.get().hotkey
|
||||||
|
if (!isObjectEqual(newHotkey, shortcuts)) {
|
||||||
|
updateShortcut(newHotkey)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function updateShortcut (newHotkey) {
|
||||||
|
Mousetrap.reset()
|
||||||
|
shortcuts = newHotkey
|
||||||
|
applyShortcuts(newHotkey)
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatShortcut (shortcut) {
|
||||||
|
return shortcut.toLowerCase().replace(/ /g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyShortcuts (shortcuts) {
|
||||||
|
for (const shortcut in shortcuts) {
|
||||||
|
const toggler = formatShortcut(shortcuts[shortcut])
|
||||||
|
// only bind if the function for that shortcut exists
|
||||||
|
if (functions[shortcut]) {
|
||||||
|
Mousetrap.bindGlobal(toggler, functions[shortcut])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyShortcuts(CM.get().hotkey)
|
||||||
|
|
||||||
|
module.exports = applyShortcuts
|
||||||
@@ -67,7 +67,8 @@ class HotkeyTab extends React.Component {
|
|||||||
handleHotkeyChange (e) {
|
handleHotkeyChange (e) {
|
||||||
const { config } = this.state
|
const { config } = this.state
|
||||||
config.hotkey = {
|
config.hotkey = {
|
||||||
toggleMain: this.refs.toggleMain.value
|
toggleMain: this.refs.toggleMain.value,
|
||||||
|
toggleMode: this.refs.toggleMode.value
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
config
|
config
|
||||||
@@ -115,6 +116,17 @@ class HotkeyTab extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div styleName='group-section'>
|
||||||
|
<div styleName='group-section-label'>{i18n.__('Toggle editor mode')}</div>
|
||||||
|
<div styleName='group-section-control'>
|
||||||
|
<input styleName='group-section-control-input'
|
||||||
|
onChange={(e) => this.handleHotkeyChange(e)}
|
||||||
|
ref='toggleMode'
|
||||||
|
value={config.hotkey.toggleMode}
|
||||||
|
type='text'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div styleName='group-control'>
|
<div styleName='group-control'>
|
||||||
<button styleName='group-control-leftButton'
|
<button styleName='group-control-leftButton'
|
||||||
onClick={(e) => this.handleHintToggleButtonClick(e)}
|
onClick={(e) => this.handleHintToggleButtonClick(e)}
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ class StoragesTab extends React.Component {
|
|||||||
<div styleName='addStorage-body-section-path'>
|
<div styleName='addStorage-body-section-path'>
|
||||||
<input styleName='addStorage-body-section-path-input'
|
<input styleName='addStorage-body-section-path-input'
|
||||||
ref='addStoragePath'
|
ref='addStoragePath'
|
||||||
placeholder='Select Folder'
|
placeholder={i18n.__('Select Folder')}
|
||||||
value={this.state.newStorage.path}
|
value={this.state.newStorage.path}
|
||||||
onChange={(e) => this.handleAddStorageChange(e)}
|
onChange={(e) => this.handleAddStorageChange(e)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ class UiTab extends React.Component {
|
|||||||
disabled={OSX}
|
disabled={OSX}
|
||||||
type='checkbox'
|
type='checkbox'
|
||||||
/>
|
/>
|
||||||
Disable Direct Write(It will be applied after restarting)
|
{i18n.__('Disable Direct Write (It will be applied after restarting)')}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
: null
|
: null
|
||||||
@@ -474,7 +474,7 @@ class UiTab extends React.Component {
|
|||||||
ref='previewSmartQuotes'
|
ref='previewSmartQuotes'
|
||||||
type='checkbox'
|
type='checkbox'
|
||||||
/>
|
/>
|
||||||
Enable smart quotes
|
{i18n.__('Enable smart quotes')}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div styleName='group-checkBoxSection'>
|
<div styleName='group-checkBoxSection'>
|
||||||
@@ -484,7 +484,7 @@ class UiTab extends React.Component {
|
|||||||
ref='previewBreaks'
|
ref='previewBreaks'
|
||||||
type='checkbox'
|
type='checkbox'
|
||||||
/>
|
/>
|
||||||
Render newlines in Markdown paragraphs as <br>
|
{i18n.__('Render newlines in Markdown paragraphs as <br>')}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -38,29 +38,13 @@ function data (state = defaultDataMap(), action) {
|
|||||||
if (note.isTrashed) {
|
if (note.isTrashed) {
|
||||||
state.trashedSet.add(uniqueKey)
|
state.trashedSet.add(uniqueKey)
|
||||||
}
|
}
|
||||||
|
const storageNoteList = getOrInitItem(state.storageNoteMap, note.storage)
|
||||||
let storageNoteList = state.storageNoteMap.get(note.storage)
|
|
||||||
if (storageNoteList == null) {
|
|
||||||
storageNoteList = new Set(storageNoteList)
|
|
||||||
state.storageNoteMap.set(note.storage, storageNoteList)
|
|
||||||
}
|
|
||||||
storageNoteList.add(uniqueKey)
|
storageNoteList.add(uniqueKey)
|
||||||
|
|
||||||
let folderNoteSet = state.folderNoteMap.get(folderKey)
|
const folderNoteSet = getOrInitItem(state.folderNoteMap, folderKey)
|
||||||
if (folderNoteSet == null) {
|
|
||||||
folderNoteSet = new Set(folderNoteSet)
|
|
||||||
state.folderNoteMap.set(folderKey, folderNoteSet)
|
|
||||||
}
|
|
||||||
folderNoteSet.add(uniqueKey)
|
folderNoteSet.add(uniqueKey)
|
||||||
|
|
||||||
note.tags.forEach((tag) => {
|
assignToTags(note.tags, state, uniqueKey)
|
||||||
let tagNoteList = state.tagNoteMap.get(tag)
|
|
||||||
if (tagNoteList == null) {
|
|
||||||
tagNoteList = new Set(tagNoteList)
|
|
||||||
state.tagNoteMap.set(tag, tagNoteList)
|
|
||||||
}
|
|
||||||
tagNoteList.add(uniqueKey)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
return state
|
return state
|
||||||
case 'UPDATE_NOTE':
|
case 'UPDATE_NOTE':
|
||||||
@@ -74,40 +58,18 @@ function data (state = defaultDataMap(), action) {
|
|||||||
state.noteMap = new Map(state.noteMap)
|
state.noteMap = new Map(state.noteMap)
|
||||||
state.noteMap.set(uniqueKey, note)
|
state.noteMap.set(uniqueKey, note)
|
||||||
|
|
||||||
if (oldNote == null || oldNote.isStarred !== note.isStarred) {
|
updateStarredChange(oldNote, note, state, uniqueKey)
|
||||||
state.starredSet = new Set(state.starredSet)
|
|
||||||
if (note.isStarred) {
|
|
||||||
state.starredSet.add(uniqueKey)
|
|
||||||
} else {
|
|
||||||
state.starredSet.delete(uniqueKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldNote == null || oldNote.isTrashed !== note.isTrashed) {
|
if (oldNote == null || oldNote.isTrashed !== note.isTrashed) {
|
||||||
state.trashedSet = new Set(state.trashedSet)
|
state.trashedSet = new Set(state.trashedSet)
|
||||||
if (note.isTrashed) {
|
if (note.isTrashed) {
|
||||||
state.trashedSet.add(uniqueKey)
|
state.trashedSet.add(uniqueKey)
|
||||||
state.starredSet.delete(uniqueKey)
|
state.starredSet.delete(uniqueKey)
|
||||||
|
removeFromTags(note.tags, state, uniqueKey)
|
||||||
note.tags.forEach(tag => {
|
|
||||||
let tagNoteList = state.tagNoteMap.get(tag)
|
|
||||||
if (tagNoteList != null) {
|
|
||||||
tagNoteList = new Set(tagNoteList)
|
|
||||||
tagNoteList.delete(uniqueKey)
|
|
||||||
state.tagNoteMap.set(tag, tagNoteList)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
state.trashedSet.delete(uniqueKey)
|
state.trashedSet.delete(uniqueKey)
|
||||||
|
|
||||||
note.tags.forEach(tag => {
|
assignToTags(note.tags, state, uniqueKey)
|
||||||
let tagNoteList = state.tagNoteMap.get(tag)
|
|
||||||
if (tagNoteList != null) {
|
|
||||||
tagNoteList = new Set(tagNoteList)
|
|
||||||
tagNoteList.add(uniqueKey)
|
|
||||||
state.tagNoteMap.set(tag, tagNoteList)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (note.isStarred) {
|
if (note.isStarred) {
|
||||||
state.starredSet.add(uniqueKey)
|
state.starredSet.add(uniqueKey)
|
||||||
@@ -125,54 +87,12 @@ function data (state = defaultDataMap(), action) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update foldermap if folder changed or post created
|
// Update foldermap if folder changed or post created
|
||||||
if (oldNote == null || oldNote.folder !== note.folder) {
|
updateFolderChange(oldNote, note, state, folderKey, uniqueKey)
|
||||||
state.folderNoteMap = new Map(state.folderNoteMap)
|
|
||||||
let folderNoteSet = state.folderNoteMap.get(folderKey)
|
|
||||||
folderNoteSet = new Set(folderNoteSet)
|
|
||||||
folderNoteSet.add(uniqueKey)
|
|
||||||
state.folderNoteMap.set(folderKey, folderNoteSet)
|
|
||||||
|
|
||||||
if (oldNote != null) {
|
if (oldNote != null) {
|
||||||
const oldFolderKey = oldNote.storage + '-' + oldNote.folder
|
updateTagChanges(oldNote, note, state, uniqueKey)
|
||||||
let oldFolderNoteList = state.folderNoteMap.get(oldFolderKey)
|
|
||||||
oldFolderNoteList = new Set(oldFolderNoteList)
|
|
||||||
oldFolderNoteList.delete(uniqueKey)
|
|
||||||
state.folderNoteMap.set(oldFolderKey, oldFolderNoteList)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldNote != null) {
|
|
||||||
const discardedTags = _.difference(oldNote.tags, note.tags)
|
|
||||||
const addedTags = _.difference(note.tags, oldNote.tags)
|
|
||||||
if (discardedTags.length + addedTags.length > 0) {
|
|
||||||
state.tagNoteMap = new Map(state.tagNoteMap)
|
|
||||||
|
|
||||||
discardedTags.forEach((tag) => {
|
|
||||||
let tagNoteList = state.tagNoteMap.get(tag)
|
|
||||||
if (tagNoteList != null) {
|
|
||||||
tagNoteList = new Set(tagNoteList)
|
|
||||||
tagNoteList.delete(uniqueKey)
|
|
||||||
state.tagNoteMap.set(tag, tagNoteList)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
addedTags.forEach((tag) => {
|
|
||||||
let tagNoteList = state.tagNoteMap.get(tag)
|
|
||||||
tagNoteList = new Set(tagNoteList)
|
|
||||||
tagNoteList.add(uniqueKey)
|
|
||||||
|
|
||||||
state.tagNoteMap.set(tag, tagNoteList)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
state.tagNoteMap = new Map(state.tagNoteMap)
|
assignToTags(note.tags, state, uniqueKey)
|
||||||
note.tags.forEach((tag) => {
|
|
||||||
let tagNoteList = state.tagNoteMap.get(tag)
|
|
||||||
if (tagNoteList == null) {
|
|
||||||
tagNoteList = new Set(tagNoteList)
|
|
||||||
state.tagNoteMap.set(tag, tagNoteList)
|
|
||||||
}
|
|
||||||
tagNoteList.add(uniqueKey)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return state
|
return state
|
||||||
@@ -220,26 +140,10 @@ function data (state = defaultDataMap(), action) {
|
|||||||
originFolderList.delete(originKey)
|
originFolderList.delete(originKey)
|
||||||
state.folderNoteMap.set(originFolderKey, originFolderList)
|
state.folderNoteMap.set(originFolderKey, originFolderList)
|
||||||
|
|
||||||
// From tagMap
|
removeFromTags(originNote.tags, state, originKey)
|
||||||
if (originNote.tags.length > 0) {
|
|
||||||
state.tagNoteMap = new Map(state.tagNoteMap)
|
|
||||||
originNote.tags.forEach((tag) => {
|
|
||||||
let noteSet = state.tagNoteMap.get(tag)
|
|
||||||
noteSet = new Set(noteSet)
|
|
||||||
noteSet.delete(originKey)
|
|
||||||
state.tagNoteMap.set(tag, noteSet)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldNote == null || oldNote.isStarred !== note.isStarred) {
|
updateStarredChange(oldNote, note, state, uniqueKey)
|
||||||
state.starredSet = new Set(state.starredSet)
|
|
||||||
if (note.isStarred) {
|
|
||||||
state.starredSet.add(uniqueKey)
|
|
||||||
} else {
|
|
||||||
state.starredSet.delete(uniqueKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldNote == null || oldNote.isTrashed !== note.isTrashed) {
|
if (oldNote == null || oldNote.isTrashed !== note.isTrashed) {
|
||||||
state.trashedSet = new Set(state.trashedSet)
|
state.trashedSet = new Set(state.trashedSet)
|
||||||
@@ -260,55 +164,13 @@ function data (state = defaultDataMap(), action) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update foldermap if folder changed or post created
|
// Update foldermap if folder changed or post created
|
||||||
if (oldNote == null || oldNote.folder !== note.folder) {
|
updateFolderChange(oldNote, note, state, folderKey, uniqueKey)
|
||||||
state.folderNoteMap = new Map(state.folderNoteMap)
|
|
||||||
let folderNoteList = state.folderNoteMap.get(folderKey)
|
|
||||||
folderNoteList = new Set(folderNoteList)
|
|
||||||
folderNoteList.add(uniqueKey)
|
|
||||||
state.folderNoteMap.set(folderKey, folderNoteList)
|
|
||||||
|
|
||||||
if (oldNote != null) {
|
|
||||||
const oldFolderKey = oldNote.storage + '-' + oldNote.folder
|
|
||||||
let oldFolderNoteList = state.folderNoteMap.get(oldFolderKey)
|
|
||||||
oldFolderNoteList = new Set(oldFolderNoteList)
|
|
||||||
oldFolderNoteList.delete(uniqueKey)
|
|
||||||
state.folderNoteMap.set(oldFolderKey, oldFolderNoteList)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove from old folder map
|
// Remove from old folder map
|
||||||
if (oldNote != null) {
|
if (oldNote != null) {
|
||||||
const discardedTags = _.difference(oldNote.tags, note.tags)
|
updateTagChanges(oldNote, note, state, uniqueKey)
|
||||||
const addedTags = _.difference(note.tags, oldNote.tags)
|
|
||||||
if (discardedTags.length + addedTags.length > 0) {
|
|
||||||
state.tagNoteMap = new Map(state.tagNoteMap)
|
|
||||||
|
|
||||||
discardedTags.forEach((tag) => {
|
|
||||||
let tagNoteList = state.tagNoteMap.get(tag)
|
|
||||||
if (tagNoteList != null) {
|
|
||||||
tagNoteList = new Set(tagNoteList)
|
|
||||||
tagNoteList.delete(uniqueKey)
|
|
||||||
state.tagNoteMap.set(tag, tagNoteList)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
addedTags.forEach((tag) => {
|
|
||||||
let tagNoteList = state.tagNoteMap.get(tag)
|
|
||||||
tagNoteList = new Set(tagNoteList)
|
|
||||||
tagNoteList.add(uniqueKey)
|
|
||||||
|
|
||||||
state.tagNoteMap.set(tag, tagNoteList)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
state.tagNoteMap = new Map(state.tagNoteMap)
|
assignToTags(note.tags, state, uniqueKey)
|
||||||
note.tags.forEach((tag) => {
|
|
||||||
let tagNoteList = state.tagNoteMap.get(tag)
|
|
||||||
if (tagNoteList == null) {
|
|
||||||
tagNoteList = new Set(tagNoteList)
|
|
||||||
state.tagNoteMap.set(tag, tagNoteList)
|
|
||||||
}
|
|
||||||
tagNoteList.add(uniqueKey)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return state
|
return state
|
||||||
@@ -347,16 +209,7 @@ function data (state = defaultDataMap(), action) {
|
|||||||
folderSet.delete(uniqueKey)
|
folderSet.delete(uniqueKey)
|
||||||
state.folderNoteMap.set(folderKey, folderSet)
|
state.folderNoteMap.set(folderKey, folderSet)
|
||||||
|
|
||||||
// From tagMap
|
removeFromTags(targetNote.tags, state, uniqueKey)
|
||||||
if (targetNote.tags.length > 0) {
|
|
||||||
state.tagNoteMap = new Map(state.tagNoteMap)
|
|
||||||
targetNote.tags.forEach((tag) => {
|
|
||||||
let noteSet = state.tagNoteMap.get(tag)
|
|
||||||
noteSet = new Set(noteSet)
|
|
||||||
noteSet.delete(uniqueKey)
|
|
||||||
state.tagNoteMap.set(tag, noteSet)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
state.noteMap = new Map(state.noteMap)
|
state.noteMap = new Map(state.noteMap)
|
||||||
state.noteMap.delete(uniqueKey)
|
state.noteMap.delete(uniqueKey)
|
||||||
@@ -420,9 +273,7 @@ function data (state = defaultDataMap(), action) {
|
|||||||
// Delete key from tag map
|
// Delete key from tag map
|
||||||
state.tagNoteMap = new Map(state.tagNoteMap)
|
state.tagNoteMap = new Map(state.tagNoteMap)
|
||||||
note.tags.forEach((tag) => {
|
note.tags.forEach((tag) => {
|
||||||
let tagNoteSet = state.tagNoteMap.get(tag)
|
const tagNoteSet = getOrInitItem(state.tagNoteMap, tag)
|
||||||
tagNoteSet = new Set(tagNoteSet)
|
|
||||||
state.tagNoteMap.set(tag, tagNoteSet)
|
|
||||||
tagNoteSet.delete(noteKey)
|
tagNoteSet.delete(noteKey)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -449,11 +300,7 @@ function data (state = defaultDataMap(), action) {
|
|||||||
state.starredSet.add(uniqueKey)
|
state.starredSet.add(uniqueKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
let storageNoteList = state.storageNoteMap.get(note.storage)
|
const storageNoteList = getOrInitItem(state.tagNoteMap, note.storage)
|
||||||
if (storageNoteList == null) {
|
|
||||||
storageNoteList = new Set(storageNoteList)
|
|
||||||
state.storageNoteMap.set(note.storage, storageNoteList)
|
|
||||||
}
|
|
||||||
storageNoteList.add(uniqueKey)
|
storageNoteList.add(uniqueKey)
|
||||||
|
|
||||||
let folderNoteSet = state.folderNoteMap.get(folderKey)
|
let folderNoteSet = state.folderNoteMap.get(folderKey)
|
||||||
@@ -464,11 +311,7 @@ function data (state = defaultDataMap(), action) {
|
|||||||
folderNoteSet.add(uniqueKey)
|
folderNoteSet.add(uniqueKey)
|
||||||
|
|
||||||
note.tags.forEach((tag) => {
|
note.tags.forEach((tag) => {
|
||||||
let tagNoteSet = state.tagNoteMap.get(tag)
|
const tagNoteSet = getOrInitItem(state.tagNoteMap, tag)
|
||||||
if (tagNoteSet == null) {
|
|
||||||
tagNoteSet = new Set(tagNoteSet)
|
|
||||||
state.tagNoteMap.set(tag, tagNoteSet)
|
|
||||||
}
|
|
||||||
tagNoteSet.add(uniqueKey)
|
tagNoteSet.add(uniqueKey)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -559,6 +402,73 @@ function status (state = defaultStatus, action) {
|
|||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateStarredChange (oldNote, note, state, uniqueKey) {
|
||||||
|
if (oldNote == null || oldNote.isStarred !== note.isStarred) {
|
||||||
|
state.starredSet = new Set(state.starredSet)
|
||||||
|
if (note.isStarred) {
|
||||||
|
state.starredSet.add(uniqueKey)
|
||||||
|
} else {
|
||||||
|
state.starredSet.delete(uniqueKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFolderChange (oldNote, note, state, folderKey, uniqueKey) {
|
||||||
|
if (oldNote == null || oldNote.folder !== note.folder) {
|
||||||
|
state.folderNoteMap = new Map(state.folderNoteMap)
|
||||||
|
let folderNoteList = state.folderNoteMap.get(folderKey)
|
||||||
|
folderNoteList = new Set(folderNoteList)
|
||||||
|
folderNoteList.add(uniqueKey)
|
||||||
|
state.folderNoteMap.set(folderKey, folderNoteList)
|
||||||
|
|
||||||
|
if (oldNote != null) {
|
||||||
|
const oldFolderKey = oldNote.storage + '-' + oldNote.folder
|
||||||
|
let oldFolderNoteList = state.folderNoteMap.get(oldFolderKey)
|
||||||
|
oldFolderNoteList = new Set(oldFolderNoteList)
|
||||||
|
oldFolderNoteList.delete(uniqueKey)
|
||||||
|
state.folderNoteMap.set(oldFolderKey, oldFolderNoteList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTagChanges (oldNote, note, state, uniqueKey) {
|
||||||
|
const discardedTags = _.difference(oldNote.tags, note.tags)
|
||||||
|
const addedTags = _.difference(note.tags, oldNote.tags)
|
||||||
|
if (discardedTags.length + addedTags.length > 0) {
|
||||||
|
removeFromTags(discardedTags, state, uniqueKey)
|
||||||
|
assignToTags(addedTags, state, uniqueKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assignToTags (tags, state, uniqueKey) {
|
||||||
|
state.tagNoteMap = new Map(state.tagNoteMap)
|
||||||
|
tags.forEach((tag) => {
|
||||||
|
const tagNoteList = getOrInitItem(state.tagNoteMap, tag)
|
||||||
|
tagNoteList.add(uniqueKey)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeFromTags (tags, state, uniqueKey) {
|
||||||
|
state.tagNoteMap = new Map(state.tagNoteMap)
|
||||||
|
tags.forEach(tag => {
|
||||||
|
let tagNoteList = state.tagNoteMap.get(tag)
|
||||||
|
if (tagNoteList != null) {
|
||||||
|
tagNoteList = new Set(tagNoteList)
|
||||||
|
tagNoteList.delete(uniqueKey)
|
||||||
|
state.tagNoteMap.set(tag, tagNoteList)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOrInitItem (target, key) {
|
||||||
|
let results = target.get(key)
|
||||||
|
if (results == null) {
|
||||||
|
results = new Set()
|
||||||
|
target.set(key, results)
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
const reducer = combineReducers({
|
const reducer = combineReducers({
|
||||||
data,
|
data,
|
||||||
config,
|
config,
|
||||||
|
|||||||
@@ -4,9 +4,10 @@
|
|||||||
"Preferences": "Preferences",
|
"Preferences": "Preferences",
|
||||||
"Make a note": "Make a note",
|
"Make a note": "Make a note",
|
||||||
"Ctrl": "Ctrl",
|
"Ctrl": "Ctrl",
|
||||||
"Ctrl(^)": "Ctrl",
|
"Ctrl(^)": "Ctrl(^)",
|
||||||
"to create a new note": "to create a new note",
|
"to create a new note": "to create a new note",
|
||||||
"Toggle Mode": "Toggle Mode",
|
"Toggle Mode": "Toggle Mode",
|
||||||
|
"Add tag...": "Add tag...",
|
||||||
"Trash": "Trash",
|
"Trash": "Trash",
|
||||||
"MODIFICATION DATE": "MODIFICATION DATE",
|
"MODIFICATION DATE": "MODIFICATION DATE",
|
||||||
"Words": "Words",
|
"Words": "Words",
|
||||||
@@ -20,9 +21,12 @@
|
|||||||
".html": ".html",
|
".html": ".html",
|
||||||
"Print": "Print",
|
"Print": "Print",
|
||||||
"Your preferences for Boostnote": "Your preferences for Boostnote",
|
"Your preferences for Boostnote": "Your preferences for Boostnote",
|
||||||
|
"Help": "Help",
|
||||||
|
"Hide Help": "Hide Help",
|
||||||
"Storages": "Storages",
|
"Storages": "Storages",
|
||||||
"Add Storage Location": "Add Storage Location",
|
"Add Storage Location": "Add Storage Location",
|
||||||
"Add Folder": "Add Folder",
|
"Add Folder": "Add Folder",
|
||||||
|
"Select Folder": "Select Folder",
|
||||||
"Open Storage folder": "Open Storage folder",
|
"Open Storage folder": "Open Storage folder",
|
||||||
"Unlink": "Unlink",
|
"Unlink": "Unlink",
|
||||||
"Edit": "Edit",
|
"Edit": "Edit",
|
||||||
@@ -34,6 +38,8 @@
|
|||||||
"Solarized Dark": "Solarized Dark",
|
"Solarized Dark": "Solarized Dark",
|
||||||
"Dark": "Dark",
|
"Dark": "Dark",
|
||||||
"Show a confirmation dialog when deleting notes": "Show a confirmation dialog when deleting notes",
|
"Show a confirmation dialog when deleting notes": "Show a confirmation dialog when deleting notes",
|
||||||
|
"Disable Direct Write (It will be applied after restarting)": "Disable Direct Write (It will be applied after restarting)",
|
||||||
|
"Show only related tags": "Show only related tags",
|
||||||
"Editor Theme": "Editor Theme",
|
"Editor Theme": "Editor Theme",
|
||||||
"Editor Font Size": "Editor Font Size",
|
"Editor Font Size": "Editor Font Size",
|
||||||
"Editor Font Family": "Editor Font Family",
|
"Editor Font Family": "Editor Font Family",
|
||||||
@@ -51,6 +57,7 @@
|
|||||||
"⚠️ Please restart boostnote after you change the keymap": "⚠️ Please restart boostnote after you change the keymap",
|
"⚠️ Please restart boostnote after you change the keymap": "⚠️ Please restart boostnote after you change the keymap",
|
||||||
"Show line numbers in the editor": "Show line numbers in the editor",
|
"Show line numbers in the editor": "Show line numbers in the editor",
|
||||||
"Allow editor to scroll past the last line": "Allow editor to scroll past the last line",
|
"Allow editor to scroll past the last line": "Allow editor to scroll past the last line",
|
||||||
|
"Enable smart quotes": "Enable smart quotes",
|
||||||
"Bring in web page title when pasting URL on editor": "Bring in web page title when pasting URL on editor",
|
"Bring in web page title when pasting URL on editor": "Bring in web page title when pasting URL on editor",
|
||||||
"Preview": "Preview",
|
"Preview": "Preview",
|
||||||
"Preview Font Size": "Preview Font Size",
|
"Preview Font Size": "Preview Font Size",
|
||||||
@@ -127,6 +134,7 @@
|
|||||||
"Storage": "Storage",
|
"Storage": "Storage",
|
||||||
"Hotkeys": "Hotkeys",
|
"Hotkeys": "Hotkeys",
|
||||||
"Show/Hide Boostnote": "Show/Hide Boostnote",
|
"Show/Hide Boostnote": "Show/Hide Boostnote",
|
||||||
|
"Toggle editor mode": "Toggle editor mode",
|
||||||
"Restore": "Restore",
|
"Restore": "Restore",
|
||||||
"Permanent Delete": "Permanent Delete",
|
"Permanent Delete": "Permanent Delete",
|
||||||
"Confirm note deletion": "Confirm note deletion",
|
"Confirm note deletion": "Confirm note deletion",
|
||||||
@@ -146,12 +154,25 @@
|
|||||||
"UserName": "UserName",
|
"UserName": "UserName",
|
||||||
"Password": "Password",
|
"Password": "Password",
|
||||||
"Russian": "Russian",
|
"Russian": "Russian",
|
||||||
|
"Hungarian": "Hungarian",
|
||||||
"Command(⌘)": "Command(⌘)",
|
"Command(⌘)": "Command(⌘)",
|
||||||
|
"Add Storage": "Add Storage",
|
||||||
|
"Name": "Name",
|
||||||
|
"Type": "Type",
|
||||||
|
"File System": "File System",
|
||||||
|
"Setting up 3rd-party cloud storage integration:": "Setting up 3rd-party cloud storage integration:",
|
||||||
|
"Cloud-Syncing-and-Backup": "Cloud-Syncing-and-Backup",
|
||||||
|
"Location": "Location",
|
||||||
|
"Add": "Add",
|
||||||
|
"Select Folder": "Select Folder",
|
||||||
|
"Unlink Storage": "Unlink Storage",
|
||||||
|
"Unlinking removes this linked storage from Boostnote. No data is removed, please manually delete the folder from your hard drive if needed.": "Unlinking removes this linked storage from Boostnote. No data is removed, please manually delete the folder from your hard drive if needed.",
|
||||||
"Editor Rulers": "Editor Rulers",
|
"Editor Rulers": "Editor Rulers",
|
||||||
"Enable": "Enable",
|
"Enable": "Enable",
|
||||||
"Disable": "Disable",
|
"Disable": "Disable",
|
||||||
"Sanitization": "Sanitization",
|
"Sanitization": "Sanitization",
|
||||||
"Only allow secure html tags (recommended)": "Only allow secure html tags (recommended)",
|
"Only allow secure html tags (recommended)": "Only allow secure html tags (recommended)",
|
||||||
|
"Render newlines in Markdown paragraphs as <br>": "Render newlines in Markdown paragraphs as <br>",
|
||||||
"Allow styles": "Allow styles",
|
"Allow styles": "Allow styles",
|
||||||
"Allow dangerous html tags": "Allow dangerous html tags"
|
"Allow dangerous html tags": "Allow dangerous html tags"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
"Ctrl(^)": "Ctrl",
|
"Ctrl(^)": "Ctrl",
|
||||||
"to create a new note": "hogy létrehozz egy jegyzetet",
|
"to create a new note": "hogy létrehozz egy jegyzetet",
|
||||||
"Toggle Mode": "Mód Váltás",
|
"Toggle Mode": "Mód Váltás",
|
||||||
|
"Add tag...": "Tag hozzáadása...",
|
||||||
"Trash": "Lomtár",
|
"Trash": "Lomtár",
|
||||||
"MODIFICATION DATE": "MÓDOSÍTÁS DÁTUMA",
|
"MODIFICATION DATE": "MÓDOSÍTÁS DÁTUMA",
|
||||||
"Words": "Szó",
|
"Words": "Szó",
|
||||||
@@ -20,9 +21,12 @@
|
|||||||
".html": ".html",
|
".html": ".html",
|
||||||
"Print": "Nyomtatás",
|
"Print": "Nyomtatás",
|
||||||
"Your preferences for Boostnote": "Boostnote beállításaid",
|
"Your preferences for Boostnote": "Boostnote beállításaid",
|
||||||
|
"Help": "Súgó",
|
||||||
|
"Hide Help": "Súgó Elrejtése",
|
||||||
"Storages": "Tárolók",
|
"Storages": "Tárolók",
|
||||||
"Add Storage Location": "Tároló Hozzáadása",
|
"Add Storage Location": "Tároló Hozzáadása",
|
||||||
"Add Folder": "Könyvtár Hozzáadása",
|
"Add Folder": "Könyvtár Hozzáadása",
|
||||||
|
"Select Folder": "Könyvtár Kiválasztása",
|
||||||
"Open Storage folder": "Tároló Megnyitása",
|
"Open Storage folder": "Tároló Megnyitása",
|
||||||
"Unlink": "Tároló Leválasztása",
|
"Unlink": "Tároló Leválasztása",
|
||||||
"Edit": "Szerkesztés",
|
"Edit": "Szerkesztés",
|
||||||
@@ -34,6 +38,8 @@
|
|||||||
"Solarized Dark": "Solarized Dark",
|
"Solarized Dark": "Solarized Dark",
|
||||||
"Dark": "Sötét",
|
"Dark": "Sötét",
|
||||||
"Show a confirmation dialog when deleting notes": "Kérjen megerősítést a jegyzetek törlése előtt",
|
"Show a confirmation dialog when deleting notes": "Kérjen megerősítést a jegyzetek törlése előtt",
|
||||||
|
"Disable Direct Write (It will be applied after restarting)": "Jegyzet Azonnali Mentésének Tiltása (Újraindítás igényel)",
|
||||||
|
"Show only related tags": "Csak a kapcsolódó tag-ek megjelenítése",
|
||||||
"Editor Theme": "Szerkesztő Témája",
|
"Editor Theme": "Szerkesztő Témája",
|
||||||
"Editor Font Size": "Szerkesztő Betűmérete",
|
"Editor Font Size": "Szerkesztő Betűmérete",
|
||||||
"Editor Font Family": "Szerkesztő Betűtípusa",
|
"Editor Font Family": "Szerkesztő Betűtípusa",
|
||||||
@@ -51,6 +57,7 @@
|
|||||||
"⚠️ Please restart boostnote after you change the keymap": "⚠️ Kérlek, indítsd újra a programot a kiosztás megváltoztatása után",
|
"⚠️ Please restart boostnote after you change the keymap": "⚠️ Kérlek, indítsd újra a programot a kiosztás megváltoztatása után",
|
||||||
"Show line numbers in the editor": "Mutatassa a sorszámokat a szerkesztőben",
|
"Show line numbers in the editor": "Mutatassa a sorszámokat a szerkesztőben",
|
||||||
"Allow editor to scroll past the last line": "A szerkesztőben az utolsó sor alá is lehessen görgetni",
|
"Allow editor to scroll past the last line": "A szerkesztőben az utolsó sor alá is lehessen görgetni",
|
||||||
|
"Enable smart quotes": "Idézőjelek párjának automatikus beírása",
|
||||||
"Bring in web page title when pasting URL on editor": "Weboldal főcímének lekérdezése URL cím beillesztésekor",
|
"Bring in web page title when pasting URL on editor": "Weboldal főcímének lekérdezése URL cím beillesztésekor",
|
||||||
"Preview": "Megtekintés",
|
"Preview": "Megtekintés",
|
||||||
"Preview Font Size": "Megtekintés Betűmérete",
|
"Preview Font Size": "Megtekintés Betűmérete",
|
||||||
@@ -127,6 +134,7 @@
|
|||||||
"Storage": "Tároló",
|
"Storage": "Tároló",
|
||||||
"Hotkeys": "Gyorsbillentyűk",
|
"Hotkeys": "Gyorsbillentyűk",
|
||||||
"Show/Hide Boostnote": "Boostnote Megjelenítése/Elrejtése",
|
"Show/Hide Boostnote": "Boostnote Megjelenítése/Elrejtése",
|
||||||
|
"Toggle editor mode": "Szerkesztő mód váltása",
|
||||||
"Restore": "Visszaállítás",
|
"Restore": "Visszaállítás",
|
||||||
"Permanent Delete": "Végleges Törlés",
|
"Permanent Delete": "Végleges Törlés",
|
||||||
"Confirm note deletion": "Törlés megerősítése",
|
"Confirm note deletion": "Törlés megerősítése",
|
||||||
@@ -146,8 +154,8 @@
|
|||||||
"UserName": "FelhasznaloNev",
|
"UserName": "FelhasznaloNev",
|
||||||
"Password": "Jelszo",
|
"Password": "Jelszo",
|
||||||
"Russian": "Russian",
|
"Russian": "Russian",
|
||||||
"Command(⌘)": "Command(⌘)",
|
|
||||||
"Hungarian": "Hungarian",
|
"Hungarian": "Hungarian",
|
||||||
|
"Command(⌘)": "Command(⌘)",
|
||||||
"Add Storage": "Tároló hozzáadása",
|
"Add Storage": "Tároló hozzáadása",
|
||||||
"Name": "Név",
|
"Name": "Név",
|
||||||
"Type": "Típus",
|
"Type": "Típus",
|
||||||
@@ -156,6 +164,15 @@
|
|||||||
"Cloud-Syncing-and-Backup": "Cloud-Syncing-and-Backup",
|
"Cloud-Syncing-and-Backup": "Cloud-Syncing-and-Backup",
|
||||||
"Location": "Hely",
|
"Location": "Hely",
|
||||||
"Add": "Hozzáadás",
|
"Add": "Hozzáadás",
|
||||||
|
"Select Folder": "Könyvtár Kiválasztása",
|
||||||
"Unlink Storage": "Tároló Leválasztása",
|
"Unlink Storage": "Tároló Leválasztása",
|
||||||
"Unlinking removes this linked storage from Boostnote. No data is removed, please manually delete the folder from your hard drive if needed.": "A leválasztás eltávolítja ezt a tárolót a Boostnote-ból. Az adatok nem lesznek törölve, kérlek manuálisan töröld a könyvtárat a merevlemezről, ha szükséges."
|
"Unlinking removes this linked storage from Boostnote. No data is removed, please manually delete the folder from your hard drive if needed.": "A leválasztás eltávolítja ezt a tárolót a Boostnote-ból. Az adatok nem lesznek törölve, kérlek manuálisan töröld a könyvtárat a merevlemezről, ha szükséges.",
|
||||||
|
"Editor Rulers": "Szerkesztő Margók",
|
||||||
|
"Enable": "Engedélyezés",
|
||||||
|
"Disable": "Tiltás",
|
||||||
|
"Sanitization": "Tisztítás",
|
||||||
|
"Only allow secure html tags (recommended)": "Csak a biztonságos html tag-ek engedélyezése (ajánlott)",
|
||||||
|
"Render newlines in Markdown paragraphs as <br>": "Az újsor karaktert <br> soremelésként jelenítse meg a Markdown jegyzetekben",
|
||||||
|
"Allow styles": "Stílusok engedélyezése",
|
||||||
|
"Allow dangerous html tags": "Veszélyes html tag-ek engedélyezése"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,7 +144,7 @@
|
|||||||
"You have to save!": "저장해주세요!",
|
"You have to save!": "저장해주세요!",
|
||||||
"Russian": "Russian",
|
"Russian": "Russian",
|
||||||
"Command(⌘)": "Command(⌘)",
|
"Command(⌘)": "Command(⌘)",
|
||||||
"Delete Folder": "폴더 삭게",
|
"Delete Folder": "폴더 삭제",
|
||||||
"This will delete all notes in the folder and can not be undone.": "폴더의 모든 노트를 지우게 되고, 되돌릴 수 없습니다.",
|
"This will delete all notes in the folder and can not be undone.": "폴더의 모든 노트를 지우게 되고, 되돌릴 수 없습니다.",
|
||||||
"UserName": "유저명",
|
"UserName": "유저명",
|
||||||
"Password": "패스워드",
|
"Password": "패스워드",
|
||||||
|
|||||||
@@ -70,6 +70,7 @@
|
|||||||
"lodash": "^4.11.1",
|
"lodash": "^4.11.1",
|
||||||
"lodash-move": "^1.1.1",
|
"lodash-move": "^1.1.1",
|
||||||
"markdown-it": "^6.0.1",
|
"markdown-it": "^6.0.1",
|
||||||
|
"markdown-it-admonition": "https://github.com/johannbre/markdown-it-admonition.git",
|
||||||
"markdown-it-checkbox": "^1.1.0",
|
"markdown-it-checkbox": "^1.1.0",
|
||||||
"markdown-it-emoji": "^1.1.1",
|
"markdown-it-emoji": "^1.1.1",
|
||||||
"markdown-it-footnote": "^3.0.0",
|
"markdown-it-footnote": "^3.0.0",
|
||||||
@@ -81,6 +82,8 @@
|
|||||||
"md5": "^2.0.0",
|
"md5": "^2.0.0",
|
||||||
"mdurl": "^1.0.1",
|
"mdurl": "^1.0.1",
|
||||||
"moment": "^2.10.3",
|
"moment": "^2.10.3",
|
||||||
|
"mousetrap": "^1.6.1",
|
||||||
|
"mousetrap-global-bind": "^1.1.0",
|
||||||
"node-ipc": "^8.1.0",
|
"node-ipc": "^8.1.0",
|
||||||
"raphael": "^2.2.7",
|
"raphael": "^2.2.7",
|
||||||
"react": "^15.5.4",
|
"react": "^15.5.4",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Boostnote is an open source project. It's an independent project with its ongoin
|
|||||||
## Community
|
## Community
|
||||||
- [Facebook Group](https://www.facebook.com/groups/boostnote/)
|
- [Facebook Group](https://www.facebook.com/groups/boostnote/)
|
||||||
- [Twitter](https://twitter.com/boostnoteapp)
|
- [Twitter](https://twitter.com/boostnoteapp)
|
||||||
- [Slack Group](https://join.slack.com/t/boostnote-group/shared_invite/enQtMzUxODgwMTc2MDg3LTgwZjA2Zjg3NjFlMzczNTVjNGMzZTk0MmIyNmE3ZjEwYTNhMTA0Y2Y4NDNlNWU4YjZlNmJiNGZhNDViOTA1ZjM)
|
- [Slack Group](https://join.slack.com/t/boostnote-group/shared_invite/enQtMzcwNDU3NDU3ODI0LTU1ZDgwZDNiZTNmN2RhOTY4OTM5ODY0ODUzMTRiNmQ0ZDMzZDRiYzg2YmQ5ZDYzZTQxYjMxYzBlNTM4NjcyYjM)
|
||||||
- [Blog](https://boostlog.io/tags/boostnote)
|
- [Blog](https://boostlog.io/tags/boostnote)
|
||||||
- [Reddit](https://www.reddit.com/r/Boostnote/)
|
- [Reddit](https://www.reddit.com/r/Boostnote/)
|
||||||
|
|
||||||
|
|||||||
BIN
resources/fonts/MaterialIcons-Regular.ttf
Executable file
BIN
resources/fonts/MaterialIcons-Regular.ttf
Executable file
Binary file not shown.
BIN
resources/fonts/MaterialIcons-Regular.woff
Executable file
BIN
resources/fonts/MaterialIcons-Regular.woff
Executable file
Binary file not shown.
BIN
resources/fonts/MaterialIcons-Regular.woff2
Executable file
BIN
resources/fonts/MaterialIcons-Regular.woff2
Executable file
Binary file not shown.
@@ -8,6 +8,7 @@ jest.mock('unique-slug')
|
|||||||
const uniqueSlug = require('unique-slug')
|
const uniqueSlug = require('unique-slug')
|
||||||
const mdurl = require('mdurl')
|
const mdurl = require('mdurl')
|
||||||
const fse = require('fs-extra')
|
const fse = require('fs-extra')
|
||||||
|
jest.mock('sander')
|
||||||
const sander = require('sander')
|
const sander = require('sander')
|
||||||
|
|
||||||
const systemUnderTest = require('browser/main/lib/dataApi/attachmentManagement')
|
const systemUnderTest = require('browser/main/lib/dataApi/attachmentManagement')
|
||||||
@@ -50,11 +51,13 @@ it('should test that copyAttachment works correctly assuming correct working of
|
|||||||
const noteKey = 'noteKey'
|
const noteKey = 'noteKey'
|
||||||
const dummyUniquePath = 'dummyPath'
|
const dummyUniquePath = 'dummyPath'
|
||||||
const dummyStorage = {path: 'dummyStoragePath'}
|
const dummyStorage = {path: 'dummyStoragePath'}
|
||||||
|
const dummyReadStream = {}
|
||||||
|
|
||||||
|
dummyReadStream.pipe = jest.fn()
|
||||||
|
dummyReadStream.on = jest.fn((event, callback) => { callback() })
|
||||||
fs.existsSync = jest.fn()
|
fs.existsSync = jest.fn()
|
||||||
fs.existsSync.mockReturnValue(true)
|
fs.existsSync.mockReturnValue(true)
|
||||||
fs.createReadStream = jest.fn()
|
fs.createReadStream = jest.fn(() => dummyReadStream)
|
||||||
fs.createReadStream.mockReturnValue({pipe: jest.fn()})
|
|
||||||
fs.createWriteStream = jest.fn()
|
fs.createWriteStream = jest.fn()
|
||||||
|
|
||||||
findStorage.findStorage = jest.fn()
|
findStorage.findStorage = jest.fn()
|
||||||
@@ -77,7 +80,11 @@ it('should test that copyAttachment creates a new folder if the attachment folde
|
|||||||
const noteKey = 'noteKey'
|
const noteKey = 'noteKey'
|
||||||
const attachmentFolderPath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER)
|
const attachmentFolderPath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER)
|
||||||
const attachmentFolderNoteKyPath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, noteKey)
|
const attachmentFolderNoteKyPath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, noteKey)
|
||||||
|
const dummyReadStream = {}
|
||||||
|
|
||||||
|
dummyReadStream.pipe = jest.fn()
|
||||||
|
dummyReadStream.on = jest.fn()
|
||||||
|
fs.createReadStream = jest.fn(() => dummyReadStream)
|
||||||
fs.existsSync = jest.fn()
|
fs.existsSync = jest.fn()
|
||||||
fs.existsSync.mockReturnValueOnce(true)
|
fs.existsSync.mockReturnValueOnce(true)
|
||||||
fs.existsSync.mockReturnValueOnce(false)
|
fs.existsSync.mockReturnValueOnce(false)
|
||||||
@@ -99,7 +106,11 @@ it('should test that copyAttachment creates a new folder if the attachment folde
|
|||||||
|
|
||||||
it('should test that copyAttachment don\'t uses a random file name if not intended ', function () {
|
it('should test that copyAttachment don\'t uses a random file name if not intended ', function () {
|
||||||
const dummyStorage = {path: 'dummyStoragePath'}
|
const dummyStorage = {path: 'dummyStoragePath'}
|
||||||
|
const dummyReadStream = {}
|
||||||
|
|
||||||
|
dummyReadStream.pipe = jest.fn()
|
||||||
|
dummyReadStream.on = jest.fn()
|
||||||
|
fs.createReadStream = jest.fn(() => dummyReadStream)
|
||||||
fs.existsSync = jest.fn()
|
fs.existsSync = jest.fn()
|
||||||
fs.existsSync.mockReturnValueOnce(true)
|
fs.existsSync.mockReturnValueOnce(true)
|
||||||
fs.existsSync.mockReturnValueOnce(false)
|
fs.existsSync.mockReturnValueOnce(false)
|
||||||
@@ -384,6 +395,7 @@ it('should test that moveAttachments returns a correct modified content version'
|
|||||||
expect(actualContent).toBe(expectedOutput)
|
expect(actualContent).toBe(expectedOutput)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
it('should test that deleteAttachmentsNotPresentInNote does nothing if noteKey, storageKey or noteContent was null', function () {
|
it('should test that deleteAttachmentsNotPresentInNote does nothing if noteKey, storageKey or noteContent was null', function () {
|
||||||
const noteKey = null
|
const noteKey = null
|
||||||
const storageKey = null
|
const storageKey = null
|
||||||
@@ -414,4 +426,76 @@ it('should test that deleteAttachmentsNotPresentInNote does nothing if noteKey,
|
|||||||
expect(fs.existsSync).not.toHaveBeenCalled()
|
expect(fs.existsSync).not.toHaveBeenCalled()
|
||||||
expect(fs.readdir).not.toHaveBeenCalled()
|
expect(fs.readdir).not.toHaveBeenCalled()
|
||||||
expect(fs.unlink).not.toHaveBeenCalled()
|
expect(fs.unlink).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
it('should test that cloneAttachments modifies the content of the new note correctly', function () {
|
||||||
|
const oldNote = {key: 'oldNoteKey', content: 'oldNoteContent', storage: 'storageKey', type: 'MARKDOWN_NOTE'}
|
||||||
|
const newNote = {key: 'newNoteKey', content: 'oldNoteContent', storage: 'storageKey', type: 'MARKDOWN_NOTE'}
|
||||||
|
const testInput =
|
||||||
|
'Test input' +
|
||||||
|
' \n' +
|
||||||
|
'[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + oldNote.key + path.sep + 'pdf.pdf](pdf})'
|
||||||
|
newNote.content = testInput
|
||||||
|
|
||||||
|
const expectedOutput =
|
||||||
|
'Test input' +
|
||||||
|
' \n' +
|
||||||
|
'[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNote.key + path.sep + 'pdf.pdf](pdf})'
|
||||||
|
systemUnderTest.cloneAttachments(oldNote, newNote)
|
||||||
|
|
||||||
|
expect(newNote.content).toBe(expectedOutput)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should test that cloneAttachments finds all attachments and copies them to the new location', function () {
|
||||||
|
const storagePathOld = 'storagePathOld'
|
||||||
|
const storagePathNew = 'storagePathNew'
|
||||||
|
const dummyStorageOld = {path: storagePathOld}
|
||||||
|
const dummyStorageNew = {path: storagePathNew}
|
||||||
|
const oldNote = {key: 'oldNoteKey', content: 'oldNoteContent', storage: 'storageKeyOldNote', type: 'MARKDOWN_NOTE'}
|
||||||
|
const newNote = {key: 'newNoteKey', content: 'oldNoteContent', storage: 'storageKeyNewNote', type: 'MARKDOWN_NOTE'}
|
||||||
|
const testInput =
|
||||||
|
'Test input' +
|
||||||
|
' \n' +
|
||||||
|
'[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + oldNote.key + path.sep + 'pdf.pdf](pdf})'
|
||||||
|
oldNote.content = testInput
|
||||||
|
newNote.content = testInput
|
||||||
|
|
||||||
|
const copyFileSyncResp = {to: jest.fn()}
|
||||||
|
sander.copyFileSync = jest.fn()
|
||||||
|
sander.copyFileSync.mockReturnValue(copyFileSyncResp)
|
||||||
|
findStorage.findStorage = jest.fn()
|
||||||
|
findStorage.findStorage.mockReturnValueOnce(dummyStorageOld)
|
||||||
|
findStorage.findStorage.mockReturnValue(dummyStorageNew)
|
||||||
|
|
||||||
|
const pathAttachmentOneFrom = path.join(storagePathOld, systemUnderTest.DESTINATION_FOLDER, oldNote.key, 'image.jpg')
|
||||||
|
const pathAttachmentOneTo = path.join(storagePathNew, systemUnderTest.DESTINATION_FOLDER, newNote.key, 'image.jpg')
|
||||||
|
|
||||||
|
const pathAttachmentTwoFrom = path.join(storagePathOld, systemUnderTest.DESTINATION_FOLDER, oldNote.key, 'pdf.pdf')
|
||||||
|
const pathAttachmentTwoTo = path.join(storagePathNew, systemUnderTest.DESTINATION_FOLDER, newNote.key, 'pdf.pdf')
|
||||||
|
|
||||||
|
systemUnderTest.cloneAttachments(oldNote, newNote)
|
||||||
|
|
||||||
|
expect(findStorage.findStorage).toHaveBeenCalledWith(oldNote.storage)
|
||||||
|
expect(findStorage.findStorage).toHaveBeenCalledWith(newNote.storage)
|
||||||
|
expect(sander.copyFileSync).toHaveBeenCalledTimes(2)
|
||||||
|
expect(copyFileSyncResp.to).toHaveBeenCalledTimes(2)
|
||||||
|
expect(sander.copyFileSync.mock.calls[0][0]).toBe(pathAttachmentOneFrom)
|
||||||
|
expect(copyFileSyncResp.to.mock.calls[0][0]).toBe(pathAttachmentOneTo)
|
||||||
|
expect(sander.copyFileSync.mock.calls[1][0]).toBe(pathAttachmentTwoFrom)
|
||||||
|
expect(copyFileSyncResp.to.mock.calls[1][0]).toBe(pathAttachmentTwoTo)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should test that cloneAttachments finds all attachments and copies them to the new location', function () {
|
||||||
|
const oldNote = {key: 'oldNoteKey', content: 'oldNoteContent', storage: 'storageKeyOldNote', type: 'SOMETHING_ELSE'}
|
||||||
|
const newNote = {key: 'newNoteKey', content: 'oldNoteContent', storage: 'storageKeyNewNote', type: 'SOMETHING_ELSE'}
|
||||||
|
const testInput = 'Test input'
|
||||||
|
oldNote.content = testInput
|
||||||
|
newNote.content = testInput
|
||||||
|
|
||||||
|
sander.copyFileSync = jest.fn()
|
||||||
|
findStorage.findStorage = jest.fn()
|
||||||
|
|
||||||
|
systemUnderTest.cloneAttachments(oldNote, newNote)
|
||||||
|
|
||||||
|
expect(findStorage.findStorage).not.toHaveBeenCalled()
|
||||||
|
expect(sander.copyFileSync).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
const test = require('ava')
|
const test = require('ava')
|
||||||
const deleteFolder = require('browser/main/lib/dataApi/deleteFolder')
|
const deleteFolder = require('browser/main/lib/dataApi/deleteFolder')
|
||||||
|
const attachmentManagement = require('browser/main/lib/dataApi/attachmentManagement')
|
||||||
|
const createNote = require('browser/main/lib/dataApi/createNote')
|
||||||
|
const fs = require('fs')
|
||||||
|
const faker = require('faker')
|
||||||
|
|
||||||
global.document = require('jsdom').jsdom('<body></body>')
|
global.document = require('jsdom').jsdom('<body></body>')
|
||||||
global.window = document.defaultView
|
global.window = document.defaultView
|
||||||
@@ -24,8 +28,32 @@ test.beforeEach((t) => {
|
|||||||
test.serial('Delete a folder', (t) => {
|
test.serial('Delete a folder', (t) => {
|
||||||
const storageKey = t.context.storage.cache.key
|
const storageKey = t.context.storage.cache.key
|
||||||
const folderKey = t.context.storage.json.folders[0].key
|
const folderKey = t.context.storage.json.folders[0].key
|
||||||
|
let noteKey
|
||||||
|
|
||||||
|
const input1 = {
|
||||||
|
type: 'SNIPPET_NOTE',
|
||||||
|
description: faker.lorem.lines(),
|
||||||
|
snippets: [{
|
||||||
|
name: faker.system.fileName(),
|
||||||
|
mode: 'text',
|
||||||
|
content: faker.lorem.lines()
|
||||||
|
}],
|
||||||
|
tags: faker.lorem.words().split(' '),
|
||||||
|
folder: folderKey
|
||||||
|
}
|
||||||
|
input1.title = input1.description.split('\n').shift()
|
||||||
|
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
|
.then(function prepare () {
|
||||||
|
return createNote(storageKey, input1)
|
||||||
|
.then(function createAttachmentFolder (data) {
|
||||||
|
fs.mkdirSync(path.join(storagePath, attachmentManagement.DESTINATION_FOLDER))
|
||||||
|
fs.mkdirSync(path.join(storagePath, attachmentManagement.DESTINATION_FOLDER, data.key))
|
||||||
|
noteKey = data.key
|
||||||
|
|
||||||
|
return data
|
||||||
|
})
|
||||||
|
})
|
||||||
.then(function doTest () {
|
.then(function doTest () {
|
||||||
return deleteFolder(storageKey, folderKey)
|
return deleteFolder(storageKey, folderKey)
|
||||||
})
|
})
|
||||||
@@ -36,6 +64,9 @@ test.serial('Delete a folder', (t) => {
|
|||||||
t.true(_.find(jsonData.folders, {key: folderKey}) == null)
|
t.true(_.find(jsonData.folders, {key: folderKey}) == null)
|
||||||
const notePaths = sander.readdirSync(data.storage.path, 'notes')
|
const notePaths = sander.readdirSync(data.storage.path, 'notes')
|
||||||
t.is(notePaths.length, t.context.storage.notes.filter((note) => note.folder !== folderKey).length)
|
t.is(notePaths.length, t.context.storage.notes.filter((note) => note.folder !== folderKey).length)
|
||||||
|
|
||||||
|
const attachmentFolderPath = path.join(storagePath, attachmentManagement.DESTINATION_FOLDER, noteKey)
|
||||||
|
t.false(fs.existsSync(attachmentFolderPath))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const TestDummy = require('../fixtures/TestDummy')
|
|||||||
const os = require('os')
|
const os = require('os')
|
||||||
const faker = require('faker')
|
const faker = require('faker')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
|
const sander = require('sander')
|
||||||
|
|
||||||
const storagePath = path.join(os.tmpdir(), 'test/export-note')
|
const storagePath = path.join(os.tmpdir(), 'test/export-note')
|
||||||
|
|
||||||
@@ -60,3 +61,8 @@ test.serial('Export a folder', (t) => {
|
|||||||
t.false(fs.existsSync(filePath))
|
t.false(fs.existsSync(filePath))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test.after.always(function after () {
|
||||||
|
localStorage.clear()
|
||||||
|
sander.rimrafSync(storagePath)
|
||||||
|
})
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ var config = {
|
|||||||
'markdown-it-checkbox',
|
'markdown-it-checkbox',
|
||||||
'markdown-it-kbd',
|
'markdown-it-kbd',
|
||||||
'markdown-it-plantuml',
|
'markdown-it-plantuml',
|
||||||
|
'markdown-it-admonition',
|
||||||
'devtron',
|
'devtron',
|
||||||
'@rokt33r/season',
|
'@rokt33r/season',
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user