mirror of
https://github.com/BoostIo/Boostnote
synced 2026-02-18 02:11:01 +00:00
Merge branch 'master' into Issue_1900
This commit is contained in:
@@ -58,7 +58,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.note.key !== this.props.note.key && !this.isMovingNote) {
|
||||
if (nextProps.note.key !== this.props.note.key && !this.state.isMovingNote) {
|
||||
if (this.saveQueue != null) this.saveNow()
|
||||
this.setState({
|
||||
note: Object.assign({}, nextProps.note)
|
||||
|
||||
@@ -18,6 +18,7 @@ import context from 'browser/lib/context'
|
||||
import ConfigManager from 'browser/main/lib/ConfigManager'
|
||||
import _ from 'lodash'
|
||||
import {findNoteTitle} from 'browser/lib/findNoteTitle'
|
||||
import convertModeName from 'browser/lib/convertModeName'
|
||||
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
|
||||
import TrashButton from './TrashButton'
|
||||
import RestoreButton from './RestoreButton'
|
||||
@@ -29,21 +30,6 @@ import { formatDate } from 'browser/lib/date-formatter'
|
||||
import i18n from 'browser/lib/i18n'
|
||||
import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote'
|
||||
|
||||
function pass (name) {
|
||||
switch (name) {
|
||||
case 'ejs':
|
||||
return 'Embedded Javascript'
|
||||
case 'html_ruby':
|
||||
return 'Embedded Ruby'
|
||||
case 'objectivec':
|
||||
return 'Objective C'
|
||||
case 'text':
|
||||
return 'Plain Text'
|
||||
default:
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
const electron = require('electron')
|
||||
const { remote } = electron
|
||||
const { Menu, MenuItem, dialog } = remote
|
||||
@@ -82,7 +68,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.note.key !== this.props.note.key && !this.isMovingNote) {
|
||||
if (nextProps.note.key !== this.props.note.key && !this.state.isMovingNote) {
|
||||
if (this.saveQueue != null) this.saveNow()
|
||||
const nextNote = Object.assign({
|
||||
description: ''
|
||||
@@ -382,11 +368,11 @@ class SnippetNoteDetail extends React.Component {
|
||||
name: mode
|
||||
})
|
||||
}
|
||||
this.setState({note: Object.assign(this.state.note, {snippets: snippets})})
|
||||
this.setState(state => ({note: Object.assign(state.note, {snippets: snippets})}))
|
||||
|
||||
this.setState({
|
||||
note: this.state.note
|
||||
}, () => {
|
||||
this.setState(state => ({
|
||||
note: state.note
|
||||
}), () => {
|
||||
this.save()
|
||||
})
|
||||
}
|
||||
@@ -395,11 +381,11 @@ class SnippetNoteDetail extends React.Component {
|
||||
return (e) => {
|
||||
const snippets = this.state.note.snippets.slice()
|
||||
snippets[index].mode = name
|
||||
this.setState({note: Object.assign(this.state.note, {snippets: snippets})})
|
||||
this.setState(state => ({note: Object.assign(state.note, {snippets: snippets})}))
|
||||
|
||||
this.setState({
|
||||
note: this.state.note
|
||||
}, () => {
|
||||
this.setState(state => ({
|
||||
note: state.note
|
||||
}), () => {
|
||||
this.save()
|
||||
})
|
||||
|
||||
@@ -413,10 +399,10 @@ class SnippetNoteDetail extends React.Component {
|
||||
return (e) => {
|
||||
const snippets = this.state.note.snippets.slice()
|
||||
snippets[index].content = this.refs['code-' + index].value
|
||||
this.setState({note: Object.assign(this.state.note, {snippets: snippets})})
|
||||
this.setState({
|
||||
note: this.state.note
|
||||
}, () => {
|
||||
this.setState(state => ({note: Object.assign(state.note, {snippets: snippets})}))
|
||||
this.setState(state => ({
|
||||
note: state.note
|
||||
}), () => {
|
||||
this.save()
|
||||
})
|
||||
}
|
||||
@@ -611,17 +597,17 @@ class SnippetNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
jumpNextTab () {
|
||||
this.setState({
|
||||
snippetIndex: (this.state.snippetIndex + 1) % this.state.note.snippets.length
|
||||
}, () => {
|
||||
this.setState(state => ({
|
||||
snippetIndex: (state.snippetIndex + 1) % state.note.snippets.length
|
||||
}), () => {
|
||||
this.focusEditor()
|
||||
})
|
||||
}
|
||||
|
||||
jumpPrevTab () {
|
||||
this.setState({
|
||||
snippetIndex: (this.state.snippetIndex - 1 + this.state.note.snippets.length) % this.state.note.snippets.length
|
||||
}, () => {
|
||||
this.setState(state => ({
|
||||
snippetIndex: (state.snippetIndex - 1 + state.note.snippets.length) % state.note.snippets.length
|
||||
}), () => {
|
||||
this.focusEditor()
|
||||
})
|
||||
}
|
||||
@@ -677,7 +663,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
const viewList = note.snippets.map((snippet, index) => {
|
||||
const isActive = this.state.snippetIndex === index
|
||||
|
||||
let syntax = CodeMirror.findModeByName(pass(snippet.mode))
|
||||
let syntax = CodeMirror.findModeByName(convertModeName(snippet.mode))
|
||||
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
|
||||
|
||||
return <div styleName='tabView'
|
||||
|
||||
@@ -53,6 +53,7 @@ export const DEFAULT_CONFIG = {
|
||||
latexInlineClose: '$',
|
||||
latexBlockOpen: '$$',
|
||||
latexBlockClose: '$$',
|
||||
plantUMLServerAddress: 'http://www.plantuml.com/plantuml',
|
||||
scrollPastEnd: false,
|
||||
smartQuotes: true,
|
||||
sanitize: 'STRICT' // 'STRICT', 'ALLOW_STYLES', 'NONE'
|
||||
|
||||
@@ -158,7 +158,7 @@ function handlePastImageEvent (codeEditor, storageKey, noteKey, dataTransferItem
|
||||
/**
|
||||
* @description Returns all attachment paths of the given markdown
|
||||
* @param {String} markdownContent content in which the attachment paths should be found
|
||||
* @returns {String[]} Array of the relativ paths (starting with :storage) of the attachments of the given markdown
|
||||
* @returns {String[]} Array of the relative paths (starting with :storage) of the attachments of the given markdown
|
||||
*/
|
||||
function getAttachmentsInContent (markdownContent) {
|
||||
const preparedInput = markdownContent.replace(new RegExp(mdurl.encode(path.sep), 'g'), path.sep)
|
||||
@@ -202,6 +202,49 @@ function deleteAttachmentFolder (storageKey, noteKey) {
|
||||
sander.rimrafSync(noteAttachmentPath)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Deletes all attachments stored in the attachment folder of the give not that are not referenced in the markdownContent
|
||||
* @param markdownContent Content of the note. All unreferenced notes will be deleted
|
||||
* @param storageKey StorageKey of the current note. Is used to determine the belonging attachment folder.
|
||||
* @param noteKey NoteKey of the current note. Is used to determine the belonging attachment folder.
|
||||
*/
|
||||
function deleteAttachmentsNotPresentInNote (markdownContent, storageKey, noteKey) {
|
||||
const targetStorage = findStorage.findStorage(storageKey)
|
||||
const attachmentFolder = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey)
|
||||
const attachmentsInNote = getAttachmentsInContent(markdownContent)
|
||||
const attachmentsInNoteOnlyFileNames = []
|
||||
if (attachmentsInNote) {
|
||||
for (let i = 0; i < attachmentsInNote.length; i++) {
|
||||
attachmentsInNoteOnlyFileNames.push(attachmentsInNote[i].replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + escapeStringRegexp(path.sep) + noteKey + escapeStringRegexp(path.sep), 'g'), ''))
|
||||
}
|
||||
}
|
||||
|
||||
if (fs.existsSync(attachmentFolder)) {
|
||||
fs.readdir(attachmentFolder, (err, files) => {
|
||||
if (err) {
|
||||
console.error("Error reading directory '" + attachmentFolder + "'. Error:")
|
||||
console.error(err)
|
||||
return
|
||||
}
|
||||
files.forEach(file => {
|
||||
if (!attachmentsInNoteOnlyFileNames.includes(file)) {
|
||||
const absolutePathOfFile = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey, file)
|
||||
fs.unlink(absolutePathOfFile, (err) => {
|
||||
if (err) {
|
||||
console.error("Could not delete '%s'", absolutePathOfFile)
|
||||
console.error(err)
|
||||
return
|
||||
}
|
||||
console.info("File '" + absolutePathOfFile + "' deleted because it was not included in the content of the note")
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
console.info("Attachment folder ('" + attachmentFolder + "') did not exist..")
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
copyAttachment,
|
||||
fixLocalURLS,
|
||||
@@ -212,6 +255,7 @@ module.exports = {
|
||||
getAbsolutePathsOfAttachmentsInContent,
|
||||
removeStorageAndNoteReferences,
|
||||
deleteAttachmentFolder,
|
||||
deleteAttachmentsNotPresentInNote,
|
||||
STORAGE_FOLDER_PLACEHOLDER,
|
||||
DESTINATION_FOLDER
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ const CSON = require('@rokt33r/season')
|
||||
const keygen = require('browser/lib/keygen')
|
||||
const sander = require('sander')
|
||||
const { findStorage } = require('browser/lib/findStorage')
|
||||
const copyImage = require('./copyImage')
|
||||
|
||||
function moveNote (storageKey, noteKey, newStorageKey, newFolderKey) {
|
||||
let oldStorage, newStorage
|
||||
@@ -70,16 +69,17 @@ function moveNote (storageKey, noteKey, newStorageKey, newFolderKey) {
|
||||
.then(function moveImages (noteData) {
|
||||
if (oldStorage.path === newStorage.path) return noteData
|
||||
|
||||
const searchImagesRegex = /!\[.*?]\(\s*?\/:storage\/(.*\.\S*?)\)/gi
|
||||
const searchImagesRegex = /!\[.*\]\(:storage\/(.+)\)/gi
|
||||
let match = searchImagesRegex.exec(noteData.content)
|
||||
|
||||
const moveTasks = []
|
||||
while (match != null) {
|
||||
const [, filename] = match
|
||||
const oldPath = path.join(oldStorage.path, 'images', filename)
|
||||
const oldPath = path.join(oldStorage.path, 'attachments', filename)
|
||||
const newPath = path.join(newStorage.path, 'attachments', filename)
|
||||
// TODO: ehhc: attachmentManagement
|
||||
moveTasks.push(
|
||||
copyImage(oldPath, noteData.storage, false)
|
||||
sander.copyFile(oldPath).to(newPath)
|
||||
.then(() => {
|
||||
fs.unlinkSync(oldPath)
|
||||
})
|
||||
|
||||
@@ -94,6 +94,7 @@ class UiTab extends React.Component {
|
||||
latexInlineClose: this.refs.previewLatexInlineClose.value,
|
||||
latexBlockOpen: this.refs.previewLatexBlockOpen.value,
|
||||
latexBlockClose: this.refs.previewLatexBlockClose.value,
|
||||
plantUMLServerAddress: this.refs.previewPlantUMLServerAddress.value,
|
||||
scrollPastEnd: this.refs.previewScrollPastEnd.checked,
|
||||
smartQuotes: this.refs.previewSmartQuotes.checked,
|
||||
sanitize: this.refs.previewSanitize.value
|
||||
@@ -544,6 +545,19 @@ class UiTab extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div styleName='group-section'>
|
||||
<div styleName='group-section-label'>
|
||||
{i18n.__('PlantUML Server')}
|
||||
</div>
|
||||
<div styleName='group-section-control'>
|
||||
<input styleName='group-section-control-input'
|
||||
ref='previewPlantUMLServerAddress'
|
||||
value={config.preview.plantUMLServerAddress}
|
||||
onChange={(e) => this.handleUIChange(e)}
|
||||
type='text'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div styleName='group-control'>
|
||||
<button styleName='group-control-rightButton'
|
||||
|
||||
@@ -256,7 +256,7 @@ function data (state = defaultDataMap(), action) {
|
||||
let noteSet = state.storageNoteMap.get(note.storage)
|
||||
noteSet = new Set(noteSet)
|
||||
noteSet.add(uniqueKey)
|
||||
state.folderNoteMap.set(folderKey, noteSet)
|
||||
state.storageNoteMap.set(folderKey, noteSet)
|
||||
}
|
||||
|
||||
// Update foldermap if folder changed or post created
|
||||
|
||||
Reference in New Issue
Block a user