1
0
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:
ehhc
2018-05-28 09:00:57 +02:00
committed by GitHub
36 changed files with 1638 additions and 1692 deletions

View File

@@ -322,8 +322,9 @@ export default class CodeEditor extends React.Component {
const cursor = editor.getCursor()
const LinkWithTitle = `[${parsedResponse.title}](${pastedTxt})`
const newValue = value.replace(taggedUrl, LinkWithTitle)
const newCursor = Object.assign({}, cursor, { ch: cursor.ch + newValue.length - value.length })
editor.setValue(newValue)
editor.setCursor(cursor)
editor.setCursor(newCursor)
}).catch((e) => {
const value = editor.getValue()
const newValue = value.replace(taggedUrl, pastedTxt)

View File

@@ -52,6 +52,16 @@ function buildStyle (fontFamily, fontSize, codeBlockFontFamily, lineNumber, scro
font-weight: 700;
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}
body {
font-family: '${fontFamily.join("','")}';
@@ -132,7 +142,6 @@ export default class MarkdownPreview extends React.Component {
this.mouseUpHandler = (e) => this.handleMouseUp(e)
this.DoubleClickHandler = (e) => this.handleDoubleClick(e)
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.saveAsTextHandler = () => this.handleSaveAsText()
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) {
this.props.onCheckboxClick(e)
}
@@ -390,9 +383,6 @@ export default class MarkdownPreview extends React.Component {
}
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) => {
el.removeEventListener('click', this.checkboxClickHandler)
})
@@ -415,16 +405,12 @@ export default class MarkdownPreview extends React.Component {
let renderedHTML = this.markdown.render(value)
this.refs.root.contentWindow.document.body.innerHTML = attachmentManagement.fixLocalURLS(renderedHTML, storagePath)
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
this.fixDecodedURI(el)
el.addEventListener('click', this.anchorClickHandler)
})
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('input[type="checkbox"]'), (el) => {
el.addEventListener('click', this.checkboxClickHandler)
})
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
this.fixDecodedURI(el)
el.addEventListener('click', this.linkClickHandler)
})
@@ -475,7 +461,7 @@ export default class MarkdownPreview extends React.Component {
el.innerHTML = ''
diagram.drawSVG(el, opts)
_.forEach(el.querySelectorAll('a'), (el) => {
el.addEventListener('click', this.anchorClickHandler)
el.addEventListener('click', this.linkClickHandler)
})
} catch (e) {
console.error(e)
@@ -491,7 +477,7 @@ export default class MarkdownPreview extends React.Component {
el.innerHTML = ''
diagram.drawSVG(el, {theme: 'simple'})
_.forEach(el.querySelectorAll('a'), (el) => {
el.addEventListener('click', this.anchorClickHandler)
el.addEventListener('click', this.linkClickHandler)
})
} catch (e) {
console.error(e)
@@ -598,6 +584,7 @@ MarkdownPreview.propTypes = {
onDoubleClick: PropTypes.func,
onMouseUp: PropTypes.func,
onMouseDown: PropTypes.func,
onContextMenu: PropTypes.func,
className: PropTypes.string,
value: PropTypes.string,
showCopyNotification: PropTypes.bool,

View File

@@ -18,7 +18,7 @@
.iconWrap
width 20px
text-align center
.counters
float right
color $ui-inactive-text-color
@@ -68,10 +68,9 @@
.menu-button-label
position fixed
display inline-block
height 32px
height 36px
left 44px
padding 0 10px
margin-top -8px
margin-left 0
overflow ellipsis
z-index 10

View File

@@ -58,8 +58,8 @@
opacity 0
border-top-right-radius 2px
border-bottom-right-radius 2px
height 26px
line-height 26px
height 34px
line-height 32px
.folderList-item:hover, .folderList-item--active:hover
.folderList-item-tooltip

View File

@@ -293,6 +293,82 @@ kbd
line-height 1
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%)
themeDarkText = #f9f9f9
themeDarkBorder = lighten(themeDarkBackground, 20%)

View File

@@ -141,6 +141,7 @@ class Markdown {
}
})
this.md.use(require('markdown-it-kbd'))
this.md.use(require('markdown-it-admonition'))
const deflate = require('markdown-it-plantuml/lib/deflate')
this.md.use(require('markdown-it-plantuml'), '', {

View File

@@ -54,7 +54,25 @@ export function escapeHtmlCharacters (text) {
: 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 {
lastFindInArray,
escapeHtmlCharacters
escapeHtmlCharacters,
isObjectEqual
}

View File

@@ -55,6 +55,10 @@ class MarkdownNoteDetail extends React.Component {
componentDidMount () {
ee.on('topbar:togglelockbutton', this.toggleLockButton)
ee.on('topbar:togglemodebutton', () => {
const reversedType = this.state.editorType === 'SPLIT' ? 'EDITOR_PREVIEW' : 'SPLIT'
this.handleSwitchMode(reversedType)
})
}
componentWillReceiveProps (nextProps) {

View File

@@ -44,16 +44,9 @@ class TagSelect extends React.Component {
}
removeLastTag () {
let { value } = this.props
value = _.isArray(value)
? value.slice()
: []
value.pop()
value = _.uniq(value)
this.value = value
this.props.onChange()
this.removeTagByCallback((value) => {
value.pop()
})
}
reset () {
@@ -96,15 +89,22 @@ class TagSelect extends React.Component {
}
handleTagRemoveButtonClick (tag) {
return (e) => {
let { value } = this.props
this.removeTagByCallback((value, tag) => {
value.splice(value.indexOf(tag), 1)
value = _.uniq(value)
}, tag)
}
this.value = value
this.props.onChange()
}
removeTagByCallback (callback, tag = null) {
let { value } = this.props
value = _.isArray(value)
? value.slice()
: []
callback(value, tag)
value = _.uniq(value)
this.value = value
this.props.onChange()
}
render () {
@@ -118,7 +118,7 @@ class TagSelect extends React.Component {
>
<span styleName='tag-label'>#{tag}</span>
<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' />
</button>

View File

@@ -16,6 +16,7 @@ import { hashHistory } from 'react-router'
import store from 'browser/main/store'
import i18n from 'browser/lib/i18n'
import { getLocales } from 'browser/lib/Languages'
import applyShortcuts from 'browser/main/lib/shortcutManager'
const path = require('path')
const electron = require('electron')
const { remote } = electron
@@ -159,7 +160,7 @@ class Main extends React.Component {
} else {
i18n.setLocale('en')
}
applyShortcuts()
// Reload all data
dataApi.init()
.then((data) => {

View File

@@ -7,6 +7,7 @@ import moment from 'moment'
import _ from 'lodash'
import ee from 'browser/main/lib/eventEmitter'
import dataApi from 'browser/main/lib/dataApi'
import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement'
import ConfigManager from 'browser/main/lib/ConfigManager'
import NoteItem from 'browser/components/NoteItem'
import NoteItemSimple from 'browser/components/NoteItemSimple'
@@ -662,6 +663,10 @@ class NoteList extends React.Component {
title: firstNote.title + ' ' + i18n.__('copy'),
content: firstNote.content
})
.then((note) => {
attachmentManagement.cloneAttachments(firstNote, note)
return note
})
.then((note) => {
dispatch({
type: 'UPDATE_NOTE',

View File

@@ -48,4 +48,5 @@ body[data-theme="dark"]
line-height normal
border-radius 2px
opacity 0
transition 0.1s
transition 0.1s
white-space nowrap

View File

@@ -44,7 +44,7 @@
height 36px
padding-left 25px
padding-right 15px
line-height 22px
line-height 36px
cursor pointer
font-size 14px
border none
@@ -147,7 +147,7 @@ body[data-theme="dark"]
background-color $ui-dark-button--active-backgroundColor
&:active
color $ui-dark-text-color
background-color $ui-dark-button--active-backgroundColor
background-color $ui-dark-button--active-backgroundColor
.header--active
.header-addFolderButton
@@ -180,7 +180,7 @@ body[data-theme="dark"]
&:active, &:active:hover
color $ui-dark-text-color
background-color $ui-dark-button--active-backgroundColor

View File

@@ -29,6 +29,7 @@
border-radius 2px
opacity 0
transition 0.1s
white-space nowrap
body[data-theme="white"]
.non-active-button

View File

@@ -1,6 +1,7 @@
import _ from 'lodash'
import RcParser from 'browser/lib/RcParser'
import i18n from 'browser/lib/i18n'
import ee from 'browser/main/lib/eventEmitter'
const OSX = global.process.platform === 'darwin'
const win = global.process.platform === 'win32'
@@ -20,7 +21,8 @@ export const DEFAULT_CONFIG = {
listStyle: 'DEFAULT', // 'DEFAULT', 'SMALL'
amaEnabled: true,
hotkey: {
toggleMain: OSX ? 'Cmd + Alt + L' : 'Super + Alt + E'
toggleMain: OSX ? 'Cmd + Alt + L' : 'Super + Alt + E',
toggleMode: OSX ? 'Cmd + M' : 'Ctrl + M'
},
ui: {
language: 'en',
@@ -167,6 +169,7 @@ function set (updates) {
ipcRenderer.send('config-renew', {
config: get()
})
ee.emit('config-renew')
}
function assignConfigValues (originalConfig, rcConfig) {

View File

@@ -42,7 +42,7 @@ function copyAttachment (sourceFilePath, storageKey, noteKey, useRandomName = tr
const targetStorage = findStorage.findStorage(storageKey)
const inputFile = fs.createReadStream(sourceFilePath)
const inputFileStream = fs.createReadStream(sourceFilePath)
let destinationName
if (useRandomName) {
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)
createAttachmentDestinationFolder(targetStorage.path, noteKey)
const outputFile = fs.createWriteStream(path.join(destinationDir, destinationName))
inputFile.pipe(outputFile)
resolve(destinationName)
inputFileStream.pipe(outputFile)
inputFileStream.on('end', () => {
resolve(destinationName)
})
} catch (e) {
return reject(e)
}
@@ -149,7 +151,7 @@ function handlePastImageEvent (codeEditor, storageKey, noteKey, dataTransferItem
base64data = reader.result.replace(/^data:image\/png;base64,/, '')
base64data += base64data.replace('+', ' ')
const binaryData = new Buffer(base64data, 'base64').toString('binary')
fs.writeFile(imagePath, binaryData, 'binary')
fs.writeFileSync(imagePath, binaryData, 'binary')
const imageMd = generateAttachmentMarkdown(imageName, imagePath, true)
codeEditor.insertAttachmentMd(imageMd)
}
@@ -174,7 +176,7 @@ function getAttachmentsInContent (markdownContent) {
* @returns {String[]} Absolute paths of the referenced attachments
*/
function getAbsolutePathsOfAttachmentsInContent (markdownContent, storagePath) {
const temp = getAttachmentsInContent(markdownContent)
const temp = getAttachmentsInContent(markdownContent) || []
const result = []
for (const relativePath of temp) {
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)) {
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) {
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
}
@@ -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 = {
copyAttachment,
fixLocalURLS,
@@ -282,6 +322,7 @@ module.exports = {
deleteAttachmentFolder,
deleteAttachmentsNotPresentInNote,
moveAttachments,
cloneAttachments,
STORAGE_FOLDER_PLACEHOLDER,
DESTINATION_FOLDER
}

View File

@@ -5,6 +5,7 @@ const resolveStorageNotes = require('./resolveStorageNotes')
const CSON = require('@rokt33r/season')
const sander = require('sander')
const { findStorage } = require('browser/lib/findStorage')
const deleteSingleNote = require('./deleteNote')
/**
* @param {String} storageKey
@@ -49,11 +50,7 @@ function deleteFolder (storageKey, folderKey) {
const deleteAllNotes = targetNotes
.map(function deleteNote (note) {
const notePath = path.join(storage.path, 'notes', note.key + '.cson')
return sander.unlink(notePath)
.catch(function (err) {
console.warn('Failed to delete', notePath, err)
})
return deleteSingleNote(storageKey, note.key)
})
return Promise.all(deleteAllNotes)
.then(() => storage)

View File

@@ -0,0 +1,7 @@
import ee from 'browser/main/lib/eventEmitter'
module.exports = {
'toggleMode': () => {
ee.emit('topbar:togglemodebutton')
}
}

View 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

View File

@@ -67,7 +67,8 @@ class HotkeyTab extends React.Component {
handleHotkeyChange (e) {
const { config } = this.state
config.hotkey = {
toggleMain: this.refs.toggleMain.value
toggleMain: this.refs.toggleMain.value,
toggleMode: this.refs.toggleMode.value
}
this.setState({
config
@@ -115,6 +116,17 @@ class HotkeyTab extends React.Component {
/>
</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'>
<button styleName='group-control-leftButton'
onClick={(e) => this.handleHintToggleButtonClick(e)}

View File

@@ -182,7 +182,7 @@ class StoragesTab extends React.Component {
<div styleName='addStorage-body-section-path'>
<input styleName='addStorage-body-section-path-input'
ref='addStoragePath'
placeholder='Select Folder'
placeholder={i18n.__('Select Folder')}
value={this.state.newStorage.path}
onChange={(e) => this.handleAddStorageChange(e)}
/>

View File

@@ -234,7 +234,7 @@ class UiTab extends React.Component {
disabled={OSX}
type='checkbox'
/>&nbsp;
Disable Direct Write(It will be applied after restarting)
{i18n.__('Disable Direct Write (It will be applied after restarting)')}
</label>
</div>
: null
@@ -474,7 +474,7 @@ class UiTab extends React.Component {
ref='previewSmartQuotes'
type='checkbox'
/>&nbsp;
Enable smart quotes
{i18n.__('Enable smart quotes')}
</label>
</div>
<div styleName='group-checkBoxSection'>
@@ -484,7 +484,7 @@ class UiTab extends React.Component {
ref='previewBreaks'
type='checkbox'
/>&nbsp;
Render newlines in Markdown paragraphs as &lt;br&gt;
{i18n.__('Render newlines in Markdown paragraphs as <br>')}
</label>
</div>

View File

@@ -38,29 +38,13 @@ function data (state = defaultDataMap(), action) {
if (note.isTrashed) {
state.trashedSet.add(uniqueKey)
}
let storageNoteList = state.storageNoteMap.get(note.storage)
if (storageNoteList == null) {
storageNoteList = new Set(storageNoteList)
state.storageNoteMap.set(note.storage, storageNoteList)
}
const storageNoteList = getOrInitItem(state.storageNoteMap, note.storage)
storageNoteList.add(uniqueKey)
let folderNoteSet = state.folderNoteMap.get(folderKey)
if (folderNoteSet == null) {
folderNoteSet = new Set(folderNoteSet)
state.folderNoteMap.set(folderKey, folderNoteSet)
}
const folderNoteSet = getOrInitItem(state.folderNoteMap, folderKey)
folderNoteSet.add(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)
})
assignToTags(note.tags, state, uniqueKey)
})
return state
case 'UPDATE_NOTE':
@@ -74,40 +58,18 @@ function data (state = defaultDataMap(), action) {
state.noteMap = new Map(state.noteMap)
state.noteMap.set(uniqueKey, note)
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)
}
}
updateStarredChange(oldNote, note, state, uniqueKey)
if (oldNote == null || oldNote.isTrashed !== note.isTrashed) {
state.trashedSet = new Set(state.trashedSet)
if (note.isTrashed) {
state.trashedSet.add(uniqueKey)
state.starredSet.delete(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)
}
})
removeFromTags(note.tags, state, uniqueKey)
} else {
state.trashedSet.delete(uniqueKey)
note.tags.forEach(tag => {
let tagNoteList = state.tagNoteMap.get(tag)
if (tagNoteList != null) {
tagNoteList = new Set(tagNoteList)
tagNoteList.add(uniqueKey)
state.tagNoteMap.set(tag, tagNoteList)
}
})
assignToTags(note.tags, state, uniqueKey)
if (note.isStarred) {
state.starredSet.add(uniqueKey)
@@ -125,54 +87,12 @@ function data (state = defaultDataMap(), action) {
}
// Update foldermap if folder changed or post created
if (oldNote == null || oldNote.folder !== note.folder) {
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) {
const oldFolderKey = oldNote.storage + '-' + oldNote.folder
let oldFolderNoteList = state.folderNoteMap.get(oldFolderKey)
oldFolderNoteList = new Set(oldFolderNoteList)
oldFolderNoteList.delete(uniqueKey)
state.folderNoteMap.set(oldFolderKey, oldFolderNoteList)
}
}
updateFolderChange(oldNote, note, state, folderKey, uniqueKey)
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)
})
}
updateTagChanges(oldNote, note, state, uniqueKey)
} else {
state.tagNoteMap = new Map(state.tagNoteMap)
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)
})
assignToTags(note.tags, state, uniqueKey)
}
return state
@@ -220,26 +140,10 @@ function data (state = defaultDataMap(), action) {
originFolderList.delete(originKey)
state.folderNoteMap.set(originFolderKey, originFolderList)
// From tagMap
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)
})
}
removeFromTags(originNote.tags, state, originKey)
}
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)
}
}
updateStarredChange(oldNote, note, state, uniqueKey)
if (oldNote == null || oldNote.isTrashed !== note.isTrashed) {
state.trashedSet = new Set(state.trashedSet)
@@ -260,55 +164,13 @@ function data (state = defaultDataMap(), action) {
}
// Update foldermap if folder changed or post created
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)
}
}
updateFolderChange(oldNote, note, state, folderKey, uniqueKey)
// Remove from old folder map
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)
})
}
updateTagChanges(oldNote, note, state, uniqueKey)
} else {
state.tagNoteMap = new Map(state.tagNoteMap)
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)
})
assignToTags(note.tags, state, uniqueKey)
}
return state
@@ -347,16 +209,7 @@ function data (state = defaultDataMap(), action) {
folderSet.delete(uniqueKey)
state.folderNoteMap.set(folderKey, folderSet)
// From tagMap
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)
})
}
removeFromTags(targetNote.tags, state, uniqueKey)
}
state.noteMap = new Map(state.noteMap)
state.noteMap.delete(uniqueKey)
@@ -420,9 +273,7 @@ function data (state = defaultDataMap(), action) {
// Delete key from tag map
state.tagNoteMap = new Map(state.tagNoteMap)
note.tags.forEach((tag) => {
let tagNoteSet = state.tagNoteMap.get(tag)
tagNoteSet = new Set(tagNoteSet)
state.tagNoteMap.set(tag, tagNoteSet)
const tagNoteSet = getOrInitItem(state.tagNoteMap, tag)
tagNoteSet.delete(noteKey)
})
}
@@ -449,11 +300,7 @@ function data (state = defaultDataMap(), action) {
state.starredSet.add(uniqueKey)
}
let storageNoteList = state.storageNoteMap.get(note.storage)
if (storageNoteList == null) {
storageNoteList = new Set(storageNoteList)
state.storageNoteMap.set(note.storage, storageNoteList)
}
const storageNoteList = getOrInitItem(state.tagNoteMap, note.storage)
storageNoteList.add(uniqueKey)
let folderNoteSet = state.folderNoteMap.get(folderKey)
@@ -464,11 +311,7 @@ function data (state = defaultDataMap(), action) {
folderNoteSet.add(uniqueKey)
note.tags.forEach((tag) => {
let tagNoteSet = state.tagNoteMap.get(tag)
if (tagNoteSet == null) {
tagNoteSet = new Set(tagNoteSet)
state.tagNoteMap.set(tag, tagNoteSet)
}
const tagNoteSet = getOrInitItem(state.tagNoteMap, tag)
tagNoteSet.add(uniqueKey)
})
})
@@ -559,6 +402,73 @@ function status (state = defaultStatus, action) {
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({
data,
config,

View File

@@ -4,9 +4,10 @@
"Preferences": "Preferences",
"Make a note": "Make a note",
"Ctrl": "Ctrl",
"Ctrl(^)": "Ctrl",
"Ctrl(^)": "Ctrl(^)",
"to create a new note": "to create a new note",
"Toggle Mode": "Toggle Mode",
"Add tag...": "Add tag...",
"Trash": "Trash",
"MODIFICATION DATE": "MODIFICATION DATE",
"Words": "Words",
@@ -20,9 +21,12 @@
".html": ".html",
"Print": "Print",
"Your preferences for Boostnote": "Your preferences for Boostnote",
"Help": "Help",
"Hide Help": "Hide Help",
"Storages": "Storages",
"Add Storage Location": "Add Storage Location",
"Add Folder": "Add Folder",
"Select Folder": "Select Folder",
"Open Storage folder": "Open Storage folder",
"Unlink": "Unlink",
"Edit": "Edit",
@@ -34,6 +38,8 @@
"Solarized Dark": "Solarized Dark",
"Dark": "Dark",
"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 Font Size": "Editor Font Size",
"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",
"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",
"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",
"Preview": "Preview",
"Preview Font Size": "Preview Font Size",
@@ -127,6 +134,7 @@
"Storage": "Storage",
"Hotkeys": "Hotkeys",
"Show/Hide Boostnote": "Show/Hide Boostnote",
"Toggle editor mode": "Toggle editor mode",
"Restore": "Restore",
"Permanent Delete": "Permanent Delete",
"Confirm note deletion": "Confirm note deletion",
@@ -146,12 +154,25 @@
"UserName": "UserName",
"Password": "Password",
"Russian": "Russian",
"Hungarian": "Hungarian",
"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",
"Enable": "Enable",
"Disable": "Disable",
"Sanitization": "Sanitization",
"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 dangerous html tags": "Allow dangerous html tags"
}

View File

@@ -7,6 +7,7 @@
"Ctrl(^)": "Ctrl",
"to create a new note": "hogy létrehozz egy jegyzetet",
"Toggle Mode": "Mód Váltás",
"Add tag...": "Tag hozzáadása...",
"Trash": "Lomtár",
"MODIFICATION DATE": "MÓDOSÍTÁS DÁTUMA",
"Words": "Szó",
@@ -20,9 +21,12 @@
".html": ".html",
"Print": "Nyomtatás",
"Your preferences for Boostnote": "Boostnote beállításaid",
"Help": "Súgó",
"Hide Help": "Súgó Elrejtése",
"Storages": "Tárolók",
"Add Storage Location": "Tároló 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",
"Unlink": "Tároló Leválasztása",
"Edit": "Szerkesztés",
@@ -34,6 +38,8 @@
"Solarized Dark": "Solarized Dark",
"Dark": "Sötét",
"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 Font Size": "Szerkesztő Betűmérete",
"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",
"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",
"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",
"Preview": "Megtekintés",
"Preview Font Size": "Megtekintés Betűmérete",
@@ -127,6 +134,7 @@
"Storage": "Tároló",
"Hotkeys": "Gyorsbillentyűk",
"Show/Hide Boostnote": "Boostnote Megjelenítése/Elrejtése",
"Toggle editor mode": "Szerkesztő mód váltása",
"Restore": "Visszaállítás",
"Permanent Delete": "Végleges Törlés",
"Confirm note deletion": "Törlés megerősítése",
@@ -146,8 +154,8 @@
"UserName": "FelhasznaloNev",
"Password": "Jelszo",
"Russian": "Russian",
"Command(⌘)": "Command(⌘)",
"Hungarian": "Hungarian",
"Command(⌘)": "Command(⌘)",
"Add Storage": "Tároló hozzáadása",
"Name": "Név",
"Type": "Típus",
@@ -156,6 +164,15 @@
"Cloud-Syncing-and-Backup": "Cloud-Syncing-and-Backup",
"Location": "Hely",
"Add": "Hozzáadás",
"Select Folder": "Könyvtár Kivá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"
}

View File

@@ -144,7 +144,7 @@
"You have to save!": "저장해주세요!",
"Russian": "Russian",
"Command(⌘)": "Command(⌘)",
"Delete Folder": "폴더 삭",
"Delete Folder": "폴더 삭",
"This will delete all notes in the folder and can not be undone.": "폴더의 모든 노트를 지우게 되고, 되돌릴 수 없습니다.",
"UserName": "유저명",
"Password": "패스워드",

View File

@@ -70,6 +70,7 @@
"lodash": "^4.11.1",
"lodash-move": "^1.1.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-emoji": "^1.1.1",
"markdown-it-footnote": "^3.0.0",
@@ -81,6 +82,8 @@
"md5": "^2.0.0",
"mdurl": "^1.0.1",
"moment": "^2.10.3",
"mousetrap": "^1.6.1",
"mousetrap-global-bind": "^1.1.0",
"node-ipc": "^8.1.0",
"raphael": "^2.2.7",
"react": "^15.5.4",

View File

@@ -25,7 +25,7 @@ Boostnote is an open source project. It's an independent project with its ongoin
## Community
- [Facebook Group](https://www.facebook.com/groups/boostnote/)
- [Twitter](https://twitter.com/boostnoteapp)
- [Slack Group](https://join.slack.com/t/boostnote-group/shared_invite/enQtMzUxODgwMTc2MDg3LTgwZjA2Zjg3NjFlMzczNTVjNGMzZTk0MmIyNmE3ZjEwYTNhMTA0Y2Y4NDNlNWU4YjZlNmJiNGZhNDViOTA1ZjM)
- [Slack Group](https://join.slack.com/t/boostnote-group/shared_invite/enQtMzcwNDU3NDU3ODI0LTU1ZDgwZDNiZTNmN2RhOTY4OTM5ODY0ODUzMTRiNmQ0ZDMzZDRiYzg2YmQ5ZDYzZTQxYjMxYzBlNTM4NjcyYjM)
- [Blog](https://boostlog.io/tags/boostnote)
- [Reddit](https://www.reddit.com/r/Boostnote/)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -8,6 +8,7 @@ jest.mock('unique-slug')
const uniqueSlug = require('unique-slug')
const mdurl = require('mdurl')
const fse = require('fs-extra')
jest.mock('sander')
const sander = require('sander')
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 dummyUniquePath = 'dummyPath'
const dummyStorage = {path: 'dummyStoragePath'}
const dummyReadStream = {}
dummyReadStream.pipe = jest.fn()
dummyReadStream.on = jest.fn((event, callback) => { callback() })
fs.existsSync = jest.fn()
fs.existsSync.mockReturnValue(true)
fs.createReadStream = jest.fn()
fs.createReadStream.mockReturnValue({pipe: jest.fn()})
fs.createReadStream = jest.fn(() => dummyReadStream)
fs.createWriteStream = 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 attachmentFolderPath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER)
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.mockReturnValueOnce(true)
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 () {
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.mockReturnValueOnce(true)
fs.existsSync.mockReturnValueOnce(false)
@@ -384,6 +395,7 @@ it('should test that moveAttachments returns a correct modified content version'
expect(actualContent).toBe(expectedOutput)
})
it('should test that deleteAttachmentsNotPresentInNote does nothing if noteKey, storageKey or noteContent was null', function () {
const noteKey = null
const storageKey = null
@@ -414,4 +426,76 @@ it('should test that deleteAttachmentsNotPresentInNote does nothing if noteKey,
expect(fs.existsSync).not.toHaveBeenCalled()
expect(fs.readdir).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' +
'![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + oldNote.key + path.sep + 'image.jpg](imageName}) \n' +
'[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + oldNote.key + path.sep + 'pdf.pdf](pdf})'
newNote.content = testInput
const expectedOutput =
'Test input' +
'![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNote.key + path.sep + 'image.jpg](imageName}) \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' +
'![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + oldNote.key + path.sep + 'image.jpg](imageName}) \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()
})

View File

@@ -1,5 +1,9 @@
const test = require('ava')
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.window = document.defaultView
@@ -24,8 +28,32 @@ test.beforeEach((t) => {
test.serial('Delete a folder', (t) => {
const storageKey = t.context.storage.cache.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()
.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 () {
return deleteFolder(storageKey, folderKey)
})
@@ -36,6 +64,9 @@ test.serial('Delete a folder', (t) => {
t.true(_.find(jsonData.folders, {key: folderKey}) == null)
const notePaths = sander.readdirSync(data.storage.path, 'notes')
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))
})
})

View File

@@ -13,6 +13,7 @@ const TestDummy = require('../fixtures/TestDummy')
const os = require('os')
const faker = require('faker')
const fs = require('fs')
const sander = require('sander')
const storagePath = path.join(os.tmpdir(), 'test/export-note')
@@ -60,3 +61,8 @@ test.serial('Export a folder', (t) => {
t.false(fs.existsSync(filePath))
})
})
test.after.always(function after () {
localStorage.clear()
sander.rimrafSync(storagePath)
})

View File

@@ -40,6 +40,7 @@ var config = {
'markdown-it-checkbox',
'markdown-it-kbd',
'markdown-it-plantuml',
'markdown-it-admonition',
'devtron',
'@rokt33r/season',
{

2551
yarn.lock

File diff suppressed because it is too large Load Diff