mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 17:56:25 +00:00
Merge branch 'master' into fix-paste-code
This commit is contained in:
@@ -70,22 +70,22 @@ class InfoPanel extends React.Component {
|
||||
<hr />
|
||||
|
||||
<div id='export-wrap'>
|
||||
<button styleName='export--enable' onClick={(e) => exportAsMd(e)}>
|
||||
<button styleName='export--enable' onClick={(e) => exportAsMd(e, 'export-md')}>
|
||||
<i className='fa fa-file-code-o' />
|
||||
<p>{i18n.__('.md')}</p>
|
||||
</button>
|
||||
|
||||
<button styleName='export--enable' onClick={(e) => exportAsTxt(e)}>
|
||||
<button styleName='export--enable' onClick={(e) => exportAsTxt(e, 'export-txt')}>
|
||||
<i className='fa fa-file-text-o' />
|
||||
<p>{i18n.__('.txt')}</p>
|
||||
</button>
|
||||
|
||||
<button styleName='export--enable' onClick={(e) => exportAsHtml(e)}>
|
||||
<button styleName='export--enable' onClick={(e) => exportAsHtml(e, 'export-html')}>
|
||||
<i className='fa fa-html5' />
|
||||
<p>{i18n.__('.html')}</p>
|
||||
</button>
|
||||
|
||||
<button styleName='export--enable' onClick={(e) => print(e)}>
|
||||
<button styleName='export--enable' onClick={(e) => print(e, 'print')}>
|
||||
<i className='fa fa-print' />
|
||||
<p>{i18n.__('Print')}</p>
|
||||
</button>
|
||||
|
||||
@@ -31,17 +31,17 @@ const InfoPanelTrashed = ({
|
||||
</div>
|
||||
|
||||
<div id='export-wrap'>
|
||||
<button styleName='export--enable' onClick={(e) => exportAsMd(e)}>
|
||||
<button styleName='export--enable' onClick={(e) => exportAsMd(e, 'export-md')}>
|
||||
<i className='fa fa-file-code-o' />
|
||||
<p>.md</p>
|
||||
</button>
|
||||
|
||||
<button styleName='export--enable' onClick={(e) => exportAsTxt(e)}>
|
||||
<button styleName='export--enable' onClick={(e) => exportAsTxt(e, 'export-txt')}>
|
||||
<i className='fa fa-file-text-o' />
|
||||
<p>.txt</p>
|
||||
</button>
|
||||
|
||||
<button styleName='export--enable' onClick={(e) => exportAsHtml(e)}>
|
||||
<button styleName='export--enable' onClick={(e) => exportAsHtml(e, 'export-html')}>
|
||||
<i className='fa fa-html5' />
|
||||
<p>.html</p>
|
||||
</button>
|
||||
|
||||
@@ -645,11 +645,18 @@ class SnippetNoteDetail extends React.Component {
|
||||
if (infoPanel.style) infoPanel.style.display = infoPanel.style.display === 'none' ? 'inline' : 'none'
|
||||
}
|
||||
|
||||
showWarning () {
|
||||
showWarning (e, msg) {
|
||||
const warningMessage = (msg) => ({
|
||||
'export-txt': 'Text export',
|
||||
'export-md': 'Markdown export',
|
||||
'export-html': 'HTML export',
|
||||
'print': 'Print'
|
||||
})[msg]
|
||||
|
||||
dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
type: 'warning',
|
||||
message: i18n.__('Sorry!'),
|
||||
detail: i18n.__('md/text import is available only a markdown note.'),
|
||||
detail: i18n.__(warningMessage(msg) + ' is available only in markdown notes.'),
|
||||
buttons: [i18n.__('OK')]
|
||||
})
|
||||
}
|
||||
@@ -802,7 +809,9 @@ class SnippetNoteDetail extends React.Component {
|
||||
createdAt={formatDate(note.createdAt)}
|
||||
exportAsMd={this.showWarning}
|
||||
exportAsTxt={this.showWarning}
|
||||
exportAsHtml={this.showWarning}
|
||||
type={note.type}
|
||||
print={this.showWarning}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -64,13 +64,14 @@ class NoteList extends React.Component {
|
||||
this.focusHandler = () => {
|
||||
this.refs.list.focus()
|
||||
}
|
||||
this.alertIfSnippetHandler = () => {
|
||||
this.alertIfSnippet()
|
||||
this.alertIfSnippetHandler = (event, msg) => {
|
||||
this.alertIfSnippet(msg)
|
||||
}
|
||||
this.importFromFileHandler = this.importFromFile.bind(this)
|
||||
this.jumpNoteByHash = this.jumpNoteByHashHandler.bind(this)
|
||||
this.handleNoteListKeyUp = this.handleNoteListKeyUp.bind(this)
|
||||
this.getNoteKeyFromTargetIndex = this.getNoteKeyFromTargetIndex.bind(this)
|
||||
this.cloneNote = this.cloneNote.bind(this)
|
||||
this.deleteNote = this.deleteNote.bind(this)
|
||||
this.focusNote = this.focusNote.bind(this)
|
||||
this.pinToTop = this.pinToTop.bind(this)
|
||||
@@ -96,6 +97,7 @@ class NoteList extends React.Component {
|
||||
this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000)
|
||||
ee.on('list:next', this.selectNextNoteHandler)
|
||||
ee.on('list:prior', this.selectPriorNoteHandler)
|
||||
ee.on('list:clone', this.cloneNote)
|
||||
ee.on('list:focus', this.focusHandler)
|
||||
ee.on('list:isMarkdownNote', this.alertIfSnippetHandler)
|
||||
ee.on('import:file', this.importFromFileHandler)
|
||||
@@ -118,6 +120,7 @@ class NoteList extends React.Component {
|
||||
|
||||
ee.off('list:next', this.selectNextNoteHandler)
|
||||
ee.off('list:prior', this.selectPriorNoteHandler)
|
||||
ee.off('list:clone', this.cloneNote)
|
||||
ee.off('list:focus', this.focusHandler)
|
||||
ee.off('list:isMarkdownNote', this.alertIfSnippetHandler)
|
||||
ee.off('import:file', this.importFromFileHandler)
|
||||
@@ -173,16 +176,15 @@ class NoteList extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
focusNote (selectedNoteKeys, noteKey) {
|
||||
focusNote (selectedNoteKeys, noteKey, pathname) {
|
||||
const { router } = this.context
|
||||
const { location } = this.props
|
||||
|
||||
this.setState({
|
||||
selectedNoteKeys
|
||||
})
|
||||
|
||||
router.push({
|
||||
pathname: location.pathname,
|
||||
pathname,
|
||||
query: {
|
||||
key: noteKey
|
||||
}
|
||||
@@ -201,6 +203,7 @@ class NoteList extends React.Component {
|
||||
}
|
||||
let { selectedNoteKeys } = this.state
|
||||
const { shiftKeyDown } = this.state
|
||||
const { location } = this.props
|
||||
|
||||
let targetIndex = this.getTargetIndex()
|
||||
|
||||
@@ -217,7 +220,7 @@ class NoteList extends React.Component {
|
||||
selectedNoteKeys.push(priorNoteKey)
|
||||
}
|
||||
|
||||
this.focusNote(selectedNoteKeys, priorNoteKey)
|
||||
this.focusNote(selectedNoteKeys, priorNoteKey, location.pathname)
|
||||
|
||||
ee.emit('list:moved')
|
||||
}
|
||||
@@ -228,6 +231,7 @@ class NoteList extends React.Component {
|
||||
}
|
||||
let { selectedNoteKeys } = this.state
|
||||
const { shiftKeyDown } = this.state
|
||||
const { location } = this.props
|
||||
|
||||
let targetIndex = this.getTargetIndex()
|
||||
const isTargetLastNote = targetIndex === this.notes.length - 1
|
||||
@@ -250,7 +254,7 @@ class NoteList extends React.Component {
|
||||
selectedNoteKeys.push(nextNoteKey)
|
||||
}
|
||||
|
||||
this.focusNote(selectedNoteKeys, nextNoteKey)
|
||||
this.focusNote(selectedNoteKeys, nextNoteKey, location.pathname)
|
||||
|
||||
ee.emit('list:moved')
|
||||
}
|
||||
@@ -262,7 +266,7 @@ class NoteList extends React.Component {
|
||||
}
|
||||
|
||||
const selectedNoteKeys = [noteHash]
|
||||
this.focusNote(selectedNoteKeys, noteHash)
|
||||
this.focusNote(selectedNoteKeys, noteHash, '/home')
|
||||
|
||||
ee.emit('list:moved')
|
||||
}
|
||||
@@ -276,12 +280,6 @@ class NoteList extends React.Component {
|
||||
ee.emit('top:new-note')
|
||||
}
|
||||
|
||||
// D key
|
||||
if (e.keyCode === 68) {
|
||||
e.preventDefault()
|
||||
this.deleteNote()
|
||||
}
|
||||
|
||||
// E key
|
||||
if (e.keyCode === 69) {
|
||||
e.preventDefault()
|
||||
@@ -494,14 +492,21 @@ class NoteList extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
alertIfSnippet () {
|
||||
alertIfSnippet (msg) {
|
||||
const warningMessage = (msg) => ({
|
||||
'export-txt': 'Text export',
|
||||
'export-md': 'Markdown export',
|
||||
'export-html': 'HTML export',
|
||||
'print': 'Print'
|
||||
})[msg]
|
||||
|
||||
const targetIndex = this.getTargetIndex()
|
||||
if (this.notes[targetIndex].type === 'SNIPPET_NOTE') {
|
||||
dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
type: 'warning',
|
||||
message: i18n.__('Sorry!'),
|
||||
detail: i18n.__('md/text import is available only a markdown note.'),
|
||||
buttons: [i18n.__('OK'), i18n.__('Cancel')]
|
||||
detail: i18n.__(warningMessage(msg) + ' is available only in markdown notes.'),
|
||||
buttons: [i18n.__('OK')]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ class StorageItem extends React.Component {
|
||||
const { storage } = this.props
|
||||
|
||||
this.state = {
|
||||
isOpen: !!storage.isOpen
|
||||
isOpen: !!storage.isOpen,
|
||||
draggedOver: null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +205,20 @@ class StorageItem extends React.Component {
|
||||
folderKey: data.folderKey,
|
||||
fileType: data.fileType
|
||||
})
|
||||
return data
|
||||
})
|
||||
.then(data => {
|
||||
dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
type: 'info',
|
||||
message: 'Exported to "' + data.exportDir + '"'
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
dialog.showErrorBox(
|
||||
'Export error',
|
||||
err ? err.message || err : 'Unexpected error during export'
|
||||
)
|
||||
throw err
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -231,14 +246,20 @@ class StorageItem extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleDragEnter (e) {
|
||||
e.dataTransfer.setData('defaultColor', e.target.style.backgroundColor)
|
||||
e.target.style.backgroundColor = 'rgba(129, 130, 131, 0.08)'
|
||||
handleDragEnter (e, key) {
|
||||
e.preventDefault()
|
||||
if (this.state.draggedOver === key) { return }
|
||||
this.setState({
|
||||
draggedOver: key
|
||||
})
|
||||
}
|
||||
|
||||
handleDragLeave (e) {
|
||||
e.target.style.opacity = '1'
|
||||
e.target.style.backgroundColor = e.dataTransfer.getData('defaultColor')
|
||||
e.preventDefault()
|
||||
if (this.state.draggedOver === null) { return }
|
||||
this.setState({
|
||||
draggedOver: null
|
||||
})
|
||||
}
|
||||
|
||||
dropNote (storage, folder, dispatch, location, noteData) {
|
||||
@@ -263,8 +284,12 @@ class StorageItem extends React.Component {
|
||||
}
|
||||
|
||||
handleDrop (e, storage, folder, dispatch, location) {
|
||||
e.target.style.opacity = '1'
|
||||
e.target.style.backgroundColor = e.dataTransfer.getData('defaultColor')
|
||||
e.preventDefault()
|
||||
if (this.state.draggedOver !== null) {
|
||||
this.setState({
|
||||
draggedOver: null
|
||||
})
|
||||
}
|
||||
const noteData = JSON.parse(e.dataTransfer.getData('note'))
|
||||
this.dropNote(storage, folder, dispatch, location, noteData)
|
||||
}
|
||||
@@ -291,16 +316,22 @@ class StorageItem extends React.Component {
|
||||
<SortableStorageItemChild
|
||||
key={folder.key}
|
||||
index={index}
|
||||
isActive={isActive}
|
||||
isActive={isActive || folder.key === this.state.draggedOver}
|
||||
handleButtonClick={(e) => this.handleFolderButtonClick(folder.key)(e)}
|
||||
handleContextMenu={(e) => this.handleFolderButtonContextMenu(e, folder)}
|
||||
folderName={folder.name}
|
||||
folderColor={folder.color}
|
||||
isFolded={isFolded}
|
||||
noteCount={noteCount}
|
||||
handleDrop={(e) => this.handleDrop(e, storage, folder, dispatch, location)}
|
||||
handleDragEnter={this.handleDragEnter}
|
||||
handleDragLeave={this.handleDragLeave}
|
||||
handleDrop={(e) => {
|
||||
this.handleDrop(e, storage, folder, dispatch, location)
|
||||
}}
|
||||
handleDragEnter={(e) => {
|
||||
this.handleDragEnter(e, folder.key)
|
||||
}}
|
||||
handleDragLeave={(e) => {
|
||||
this.handleDragLeave(e, folder)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -16,7 +16,7 @@ function copyFile (srcPath, dstPath) {
|
||||
const dstFolder = path.dirname(dstPath)
|
||||
if (!fs.existsSync(dstFolder)) fs.mkdirSync(dstFolder)
|
||||
|
||||
const input = fs.createReadStream(srcPath)
|
||||
const input = fs.createReadStream(decodeURI(srcPath))
|
||||
const output = fs.createWriteStream(dstPath)
|
||||
|
||||
output.on('error', reject)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { findStorage } from 'browser/lib/findStorage'
|
||||
import resolveStorageData from './resolveStorageData'
|
||||
import resolveStorageNotes from './resolveStorageNotes'
|
||||
import exportNote from './exportNote'
|
||||
import filenamify from 'filenamify'
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs'
|
||||
|
||||
/**
|
||||
* @param {String} storageKey
|
||||
@@ -45,9 +45,9 @@ function exportFolder (storageKey, folderKey, fileType, exportDir) {
|
||||
|
||||
notes
|
||||
.filter(note => note.folder === folderKey && note.isTrashed === false && note.type === 'MARKDOWN_NOTE')
|
||||
.forEach(snippet => {
|
||||
const notePath = path.join(exportDir, `${filenamify(snippet.title, {replacement: '_'})}.${fileType}`)
|
||||
fs.writeFileSync(notePath, snippet.content)
|
||||
.forEach(note => {
|
||||
const notePath = path.join(exportDir, `${filenamify(note.title, {replacement: '_'})}.${fileType}`)
|
||||
exportNote(note.key, storage.path, note.content, notePath, null)
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
@@ -4,27 +4,43 @@ import { findStorage } from 'browser/lib/findStorage'
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const attachmentManagement = require('./attachmentManagement')
|
||||
|
||||
/**
|
||||
* Export note together with images
|
||||
* Export note together with attachments
|
||||
*
|
||||
* If images is stored in the storage, creates 'images' subfolder in target directory
|
||||
* and copies images to it. Changes links to images in the content of the note
|
||||
* If attachments are stored in the storage, creates 'attachments' subfolder in target directory
|
||||
* and copies attachments to it. Changes links to images in the content of the note
|
||||
*
|
||||
* @param {String} nodeKey key of the node that should be exported
|
||||
* @param {String} storageKey or storage path
|
||||
* @param {String} noteContent Content to export
|
||||
* @param {String} targetPath Path to exported file
|
||||
* @param {function} outputFormatter
|
||||
* @return {Promise.<*[]>}
|
||||
*/
|
||||
function exportNote (storageKey, noteContent, targetPath, outputFormatter) {
|
||||
function exportNote (nodeKey, storageKey, noteContent, targetPath, outputFormatter) {
|
||||
const storagePath = path.isAbsolute(storageKey) ? storageKey : findStorage(storageKey).path
|
||||
const exportTasks = []
|
||||
|
||||
if (!storagePath) {
|
||||
throw new Error('Storage path is not found')
|
||||
}
|
||||
const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent(
|
||||
noteContent,
|
||||
storagePath
|
||||
)
|
||||
attachmentsAbsolutePaths.forEach(attachment => {
|
||||
exportTasks.push({
|
||||
src: attachment,
|
||||
dst: attachmentManagement.DESTINATION_FOLDER
|
||||
})
|
||||
})
|
||||
|
||||
let exportedData = noteContent
|
||||
let exportedData = attachmentManagement.removeStorageAndNoteReferences(
|
||||
noteContent,
|
||||
nodeKey
|
||||
)
|
||||
|
||||
if (outputFormatter) {
|
||||
exportedData = outputFormatter(exportedData, exportTasks)
|
||||
|
||||
Reference in New Issue
Block a user