diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index d1c8d14a..78748a7f 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -22,6 +22,7 @@ import Markdown from '../../lib/markdown' import i18n from 'browser/lib/i18n' import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote' import context from 'browser/lib/context' +import filenamify from 'filenamify' const { remote } = require('electron') const { dialog } = remote @@ -527,6 +528,38 @@ class NoteList extends React.Component { this.selectNextNote() } + handleExportClick (e, note, fileType) { + const options = { + defaultPath: filenamify(note.title, { + replacement: '_' + }), + filters: [{ name: 'Documents', extensions: [fileType] }], + properties: ['openFile', 'createDirectory'] + } + + dialog.showSaveDialog(remote.getCurrentWindow(), options, filename => { + if (filename) { + const { config } = this.props + + dataApi + .exportNoteAs(note, filename, fileType, config) + .then(res => { + dialog.showMessageBox(remote.getCurrentWindow(), { + type: 'info', + message: `Exported to ${filename}` + }) + }) + .catch(err => { + dialog.showErrorBox( + 'Export error', + err ? err.message || err : 'Unexpected error during export' + ) + throw err + }) + } + }) + } + handleNoteContextMenu (e, uniqueKey) { const { location } = this.props const { selectedNoteKeys } = this.state @@ -572,10 +605,30 @@ class NoteList extends React.Component { }, { label: copyNoteLink, click: this.copyNoteLink.bind(this, note) + }, { + type: 'separator' + }, { + label: i18n.__('Export Note'), + submenu: [ + { + label: i18n.__('Export as Plain Text (.txt)'), + click: e => this.handleExportClick(e, note, 'txt') + }, + { + label: i18n.__('Export as Markdown (.md)'), + click: e => this.handleExportClick(e, note, 'md') + }, + { + label: i18n.__('Export as HTML (.html)'), + click: e => this.handleExportClick(e, note, 'html') + } + ] }) if (note.type === 'MARKDOWN_NOTE') { if (note.blog && note.blog.blogLink && note.blog.blogId) { templates.push({ + type: 'separator' + }, { label: updateLabel, click: this.publishMarkdown.bind(this) }, { @@ -584,6 +637,8 @@ class NoteList extends React.Component { }) } else { templates.push({ + type: 'separator' + }, { label: publishLabel, click: this.publishMarkdown.bind(this) }) diff --git a/browser/main/lib/dataApi/exportNoteAs.js b/browser/main/lib/dataApi/exportNoteAs.js new file mode 100644 index 00000000..87c7f117 --- /dev/null +++ b/browser/main/lib/dataApi/exportNoteAs.js @@ -0,0 +1,45 @@ +import { findStorage } from 'browser/lib/findStorage' +import exportNote from './exportNote' +import formatMarkdown from './formatMarkdown' +import formatHTML from './formatHTML' + +/** + * @param {Object} note + * @param {String} filename + * @param {String} fileType + * @param {Object} config + */ + +function exportNoteAs (note, filename, fileType, config) { + const storage = findStorage(note.storage) + + let contentFormatter = null + if (fileType === 'md') { + contentFormatter = formatMarkdown({ + storagePath: storage.path, + export: config.export + }) + } else if (fileType === 'html') { + contentFormatter = formatHTML({ + theme: config.ui.theme, + fontSize: config.preview.fontSize, + fontFamily: config.preview.fontFamily, + codeBlockTheme: config.preview.codeBlockTheme, + codeBlockFontFamily: config.editor.fontFamily, + lineNumber: config.preview.lineNumber, + indentSize: config.editor.indentSize, + scrollPastEnd: config.preview.scrollPastEnd, + smartQuotes: config.preview.smartQuotes, + breaks: config.preview.breaks, + sanitize: config.preview.sanitize, + customCSS: config.preview.customCSS, + allowCustomCSS: config.preview.allowCustomCSS, + storagePath: storage.path, + export: config.export + }) + } + + return exportNote(storage.key, note, filename, contentFormatter) +} + +module.exports = exportNoteAs diff --git a/browser/main/lib/dataApi/index.js b/browser/main/lib/dataApi/index.js index 92be6b93..b6e53078 100644 --- a/browser/main/lib/dataApi/index.js +++ b/browser/main/lib/dataApi/index.js @@ -14,6 +14,7 @@ const dataApi = { updateNote: require('./updateNote'), deleteNote: require('./deleteNote'), moveNote: require('./moveNote'), + exportNoteAs: require('./exportNoteAs'), migrateFromV5Storage: require('./migrateFromV5Storage'), createSnippet: require('./createSnippet'), deleteSnippet: require('./deleteSnippet'),