From d79b6e094af5648d8584deaab554c9636943ebe6 Mon Sep 17 00:00:00 2001 From: ehhc Date: Sun, 9 Sep 2018 17:41:51 +0200 Subject: [PATCH 01/17] export folder should also export the attachments -> fixes #2374 --- browser/components/MarkdownPreview.js | 39 ++---------------------- browser/main/SideNav/StorageItem.js | 14 +++++++++ browser/main/lib/dataApi/exportFolder.js | 8 ++--- browser/main/lib/dataApi/exportNote.js | 26 +++++++++++++--- package.json | 2 +- yarn.lock | 6 ++-- 6 files changed, 46 insertions(+), 49 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 5376a773..ebf32ccd 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -251,26 +251,7 @@ export default class MarkdownPreview extends React.Component { } handleSaveAsMd () { - this.exportAsDocument('md', (noteContent, exportTasks) => { - let result = noteContent - if (this.props && this.props.storagePath && this.props.noteKey) { - const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent( - noteContent, - this.props.storagePath - ) - attachmentsAbsolutePaths.forEach(attachment => { - exportTasks.push({ - src: attachment, - dst: attachmentManagement.DESTINATION_FOLDER - }) - }) - result = attachmentManagement.removeStorageAndNoteReferences( - noteContent, - this.props.noteKey - ) - } - return result - }) + this.exportAsDocument('md') } handleSaveAsHtml () { @@ -301,11 +282,6 @@ export default class MarkdownPreview extends React.Component { escapeHtmlCharacters(noteContent, { detectCodeBlock: true }) ) const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES] - const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent( - noteContent, - this.props.storagePath - ) - files.forEach(file => { if (global.process.platform === 'win32') { file = file.replace('file:///', '') @@ -317,16 +293,6 @@ export default class MarkdownPreview extends React.Component { dst: 'css' }) }) - attachmentsAbsolutePaths.forEach(attachment => { - exportTasks.push({ - src: attachment, - dst: attachmentManagement.DESTINATION_FOLDER - }) - }) - body = attachmentManagement.removeStorageAndNoteReferences( - body, - this.props.noteKey - ) let styles = '' files.forEach(file => { @@ -359,8 +325,9 @@ export default class MarkdownPreview extends React.Component { if (filename) { const content = this.props.value const storage = this.props.storagePath + const nodeKey = this.props.noteKey - exportNote(storage, content, filename, contentFormatter) + exportNote(nodeKey, storage, content, filename, contentFormatter) .then(res => { dialog.showMessageBox(remote.getCurrentWindow(), { type: 'info', diff --git a/browser/main/SideNav/StorageItem.js b/browser/main/SideNav/StorageItem.js index d72f0a8f..242bf328 100644 --- a/browser/main/SideNav/StorageItem.js +++ b/browser/main/SideNav/StorageItem.js @@ -164,6 +164,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 }) } }) diff --git a/browser/main/lib/dataApi/exportFolder.js b/browser/main/lib/dataApi/exportFolder.js index 3e998f15..771f77dc 100644 --- a/browser/main/lib/dataApi/exportFolder.js +++ b/browser/main/lib/dataApi/exportFolder.js @@ -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 { diff --git a/browser/main/lib/dataApi/exportNote.js b/browser/main/lib/dataApi/exportNote.js index e4fec5f4..b358e548 100755 --- a/browser/main/lib/dataApi/exportNote.js +++ b/browser/main/lib/dataApi/exportNote.js @@ -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) diff --git a/package.json b/package.json index c9e22164..dd793f9e 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "electron-gh-releases": "^2.0.2", "escape-string-regexp": "^1.0.5", "file-url": "^2.0.2", - "filenamify": "^2.0.0", + "filenamify": "^2.1.0", "flowchart.js": "^1.6.5", "font-awesome": "^4.3.0", "fs-extra": "^5.0.0", diff --git a/yarn.lock b/yarn.lock index 4ecfa51b..0d919cf3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3514,9 +3514,9 @@ filename-reserved-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229" -filenamify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-2.0.0.tgz#bd162262c0b6e94bfbcdcf19a3bbb3764f785695" +filenamify@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-2.1.0.tgz#88faf495fb1b47abfd612300002a16228c677ee9" dependencies: filename-reserved-regex "^2.0.0" strip-outer "^1.0.0" From 3414e2daf0f1e50ff580049134c9595886709484 Mon Sep 17 00:00:00 2001 From: Miguel Teixeira Date: Thu, 22 Nov 2018 12:22:49 +0000 Subject: [PATCH 02/17] Added warning when trying to print from InfoPanel --- browser/main/Detail/SnippetNoteDetail.js | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index afd81102..db862115 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -789,6 +789,7 @@ class SnippetNoteDetail extends React.Component { exportAsMd={this.showWarning} exportAsTxt={this.showWarning} type={note.type} + print={this.showWarning} /> From d95d282f393fe64ccfe0467cc6a2c8d5e6cb2fdd Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sun, 25 Nov 2018 17:09:54 +0100 Subject: [PATCH 03/17] disable editor's context menu when switch preview is using right click --- browser/components/CodeEditor.js | 18 ++++++++++++------ browser/components/MarkdownEditor.js | 1 + browser/components/MarkdownSplitEditor.js | 1 + 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 130cc86e..260b12f8 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -65,12 +65,16 @@ export default class CodeEditor extends React.Component { this.scrollToLineHandeler = this.scrollToLine.bind(this) this.formatTable = () => this.handleFormatTable() - this.contextMenuHandler = function (editor, event) { - const menu = buildEditorContextMenu(editor, event) - if (menu != null) { - setTimeout(() => menu.popup(remote.getCurrentWindow()), 30) + + if (props.switchPreview !== 'RIGHTCLICK') { + this.contextMenuHandler = function (editor, event) { + const menu = buildEditorContextMenu(editor, event) + if (menu != null) { + setTimeout(() => menu.popup(remote.getCurrentWindow()), 30) + } } } + this.editorActivityHandler = () => this.handleEditorActivity() this.turndownService = new TurndownService() @@ -137,7 +141,7 @@ export default class CodeEditor extends React.Component { } componentDidMount () { - const { rulers, enableRulers } = this.props + const { rulers, enableRulers, switchPreview } = this.props const expandSnippet = this.expandSnippet.bind(this) eventEmitter.on('line:jump', this.scrollToLineHandeler) @@ -241,7 +245,9 @@ export default class CodeEditor extends React.Component { this.editor.on('blur', this.blurHandler) this.editor.on('change', this.changeHandler) this.editor.on('paste', this.pasteHandler) - this.editor.on('contextmenu', this.contextMenuHandler) + if (switchPreview !== 'RIGHTCLICK') { + this.editor.on('contextmenu', this.contextMenuHandler) + } eventEmitter.on('top:search', this.searchHandler) eventEmitter.emit('code:init') diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index 20ce9451..0c2e291e 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -278,6 +278,7 @@ class MarkdownEditor extends React.Component { onChange={(e) => this.handleChange(e)} onBlur={(e) => this.handleBlur(e)} spellCheck={config.editor.spellcheck} + switchPreview={config.editor.switchPreview} />
this.handleMouseDown(e)} >
From c33058ae2bf324fc9cd1c4ec033a4ac850a958e2 Mon Sep 17 00:00:00 2001 From: Miguel Teixeira Date: Tue, 27 Nov 2018 17:42:04 +0000 Subject: [PATCH 04/17] Added custom warning messages. --- browser/main/Detail/InfoPanel.js | 8 ++++---- browser/main/Detail/InfoPanelTrashed.js | 6 +++--- browser/main/Detail/SnippetNoteDetail.js | 12 ++++++++++-- browser/main/NoteList/index.js | 17 ++++++++++++----- lib/main-menu.js | 8 ++++---- 5 files changed, 33 insertions(+), 18 deletions(-) diff --git a/browser/main/Detail/InfoPanel.js b/browser/main/Detail/InfoPanel.js index 4ce610fa..15535186 100644 --- a/browser/main/Detail/InfoPanel.js +++ b/browser/main/Detail/InfoPanel.js @@ -70,22 +70,22 @@ class InfoPanel extends React.Component {
- - - - diff --git a/browser/main/Detail/InfoPanelTrashed.js b/browser/main/Detail/InfoPanelTrashed.js index db64a284..d4c8045d 100644 --- a/browser/main/Detail/InfoPanelTrashed.js +++ b/browser/main/Detail/InfoPanelTrashed.js @@ -31,17 +31,17 @@ const InfoPanelTrashed = ({
- - - diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index db862115..08cd50f1 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -633,11 +633,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 is available only as a markdown note', + 'export-md': 'Markdown export is available only as a markdown note.', + 'export-html': 'HTML export is available only as a markdown note.', + 'print': 'Print is available only as a markdown note.' + })[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)), buttons: [i18n.__('OK')] }) } @@ -788,6 +795,7 @@ class SnippetNoteDetail extends React.Component { createdAt={formatDate(note.createdAt)} exportAsMd={this.showWarning} exportAsTxt={this.showWarning} + exportAsHtml={this.showWarning} type={note.type} print={this.showWarning} /> diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 7bb52ccd..02bdbcd2 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -64,8 +64,8 @@ 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) @@ -494,14 +494,21 @@ class NoteList extends React.Component { }) } - alertIfSnippet () { + alertIfSnippet (msg) { + const warningMessage = (msg) => ({ + 'export-txt': 'Text export is available only as a markdown note', + 'export-md': 'Markdown export is available only as a markdown note.', + 'export-html': 'HTML export is available only as a markdown note.', + 'print': 'Print is available only as a markdown note.' + })[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)), + buttons: [i18n.__('OK')] }) } } diff --git a/lib/main-menu.js b/lib/main-menu.js index fed5eb15..2782ea8b 100644 --- a/lib/main-menu.js +++ b/lib/main-menu.js @@ -99,21 +99,21 @@ const file = { { label: 'Plain Text (.txt)', click () { - mainWindow.webContents.send('list:isMarkdownNote') + mainWindow.webContents.send('list:isMarkdownNote', 'export-txt') mainWindow.webContents.send('export:save-text') } }, { label: 'MarkDown (.md)', click () { - mainWindow.webContents.send('list:isMarkdownNote') + mainWindow.webContents.send('list:isMarkdownNote', 'export-md') mainWindow.webContents.send('export:save-md') } }, { label: 'HTML (.html)', click () { - mainWindow.webContents.send('list:isMarkdownNote') + mainWindow.webContents.send('list:isMarkdownNote', 'export-html') mainWindow.webContents.send('export:save-html') } } @@ -159,7 +159,7 @@ const file = { label: 'Print', accelerator: 'CommandOrControl+P', click () { - mainWindow.webContents.send('list:isMarkdownNote') + mainWindow.webContents.send('list:isMarkdownNote', 'print') mainWindow.webContents.send('print') } }, From ef66e71febf98dbfcf827926e99e1105f5b4a891 Mon Sep 17 00:00:00 2001 From: Miguel Teixeira Date: Tue, 27 Nov 2018 18:02:59 +0000 Subject: [PATCH 05/17] Solved some errors in identation --- browser/main/Detail/SnippetNoteDetail.js | 10 +++++----- browser/main/NoteList/index.js | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 08cd50f1..473ce9d0 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -635,12 +635,12 @@ class SnippetNoteDetail extends React.Component { showWarning (e, msg) { const warningMessage = (msg) => ({ - 'export-txt': 'Text export is available only as a markdown note', - 'export-md': 'Markdown export is available only as a markdown note.', - 'export-html': 'HTML export is available only as a markdown note.', - 'print': 'Print is available only as a markdown note.' + 'export-txt': 'Text export is available only as a markdown note', + 'export-md': 'Markdown export is available only as a markdown note.', + 'export-html': 'HTML export is available only as a markdown note.', + 'print': 'Print is available only as a markdown note.' })[msg] - + dialog.showMessageBox(remote.getCurrentWindow(), { type: 'warning', message: i18n.__('Sorry!'), diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 02bdbcd2..49d063ca 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -496,11 +496,11 @@ class NoteList extends React.Component { alertIfSnippet (msg) { const warningMessage = (msg) => ({ - 'export-txt': 'Text export is available only as a markdown note', - 'export-md': 'Markdown export is available only as a markdown note.', - 'export-html': 'HTML export is available only as a markdown note.', - 'print': 'Print is available only as a markdown note.' - })[msg] + 'export-txt': 'Text export is available only as a markdown note', + 'export-md': 'Markdown export is available only as a markdown note.', + 'export-html': 'HTML export is available only as a markdown note.', + 'print': 'Print is available only as a markdown note.' + })[msg] const targetIndex = this.getTargetIndex() if (this.notes[targetIndex].type === 'SNIPPET_NOTE') { From c245855bbf996fbd23b63d8e0ff6b10af2826da6 Mon Sep 17 00:00:00 2001 From: Miguel Teixeira Date: Tue, 27 Nov 2018 20:28:57 +0000 Subject: [PATCH 06/17] Improved messages. --- browser/main/Detail/SnippetNoteDetail.js | 10 +++++----- browser/main/NoteList/index.js | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 473ce9d0..47e4de98 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -635,16 +635,16 @@ class SnippetNoteDetail extends React.Component { showWarning (e, msg) { const warningMessage = (msg) => ({ - 'export-txt': 'Text export is available only as a markdown note', - 'export-md': 'Markdown export is available only as a markdown note.', - 'export-html': 'HTML export is available only as a markdown note.', - 'print': 'Print is available only as a markdown note.' + '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.__(warningMessage(msg)), + detail: i18n.__(warningMessage(msg) + ' is available only as a markdown note.'), buttons: [i18n.__('OK')] }) } diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 49d063ca..51e7a41c 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -496,10 +496,10 @@ class NoteList extends React.Component { alertIfSnippet (msg) { const warningMessage = (msg) => ({ - 'export-txt': 'Text export is available only as a markdown note', - 'export-md': 'Markdown export is available only as a markdown note.', - 'export-html': 'HTML export is available only as a markdown note.', - 'print': 'Print is available only as a markdown note.' + 'export-txt': 'Text export', + 'export-md': 'Markdown export', + 'export-html': 'HTML export', + 'print': 'Print' })[msg] const targetIndex = this.getTargetIndex() @@ -507,7 +507,7 @@ class NoteList extends React.Component { dialog.showMessageBox(remote.getCurrentWindow(), { type: 'warning', message: i18n.__('Sorry!'), - detail: i18n.__(warningMessage(msg)), + detail: i18n.__(warningMessage(msg) + ' is available only as a markdown note.'), buttons: [i18n.__('OK')] }) } From a39da481e0e403b078b6ac03e462d97cbe8c2596 Mon Sep 17 00:00:00 2001 From: Miguel Teixeira Date: Wed, 28 Nov 2018 09:08:58 +0000 Subject: [PATCH 07/17] Changed message. --- browser/main/Detail/SnippetNoteDetail.js | 2 +- browser/main/NoteList/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 47e4de98..ddcc50bf 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -644,7 +644,7 @@ class SnippetNoteDetail extends React.Component { dialog.showMessageBox(remote.getCurrentWindow(), { type: 'warning', message: i18n.__('Sorry!'), - detail: i18n.__(warningMessage(msg) + ' is available only as a markdown note.'), + detail: i18n.__(warningMessage(msg) + ' is available only in markdown notes.'), buttons: [i18n.__('OK')] }) } diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 51e7a41c..3293787d 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -507,7 +507,7 @@ class NoteList extends React.Component { dialog.showMessageBox(remote.getCurrentWindow(), { type: 'warning', message: i18n.__('Sorry!'), - detail: i18n.__(warningMessage(msg) + ' is available only as a markdown note.'), + detail: i18n.__(warningMessage(msg) + ' is available only in markdown notes.'), buttons: [i18n.__('OK')] }) } From b8a295713c6ab5de1ae2f8dff140a8fdbc4633b2 Mon Sep 17 00:00:00 2001 From: Guilherme Silva Date: Fri, 30 Nov 2018 08:42:27 +0000 Subject: [PATCH 08/17] Dragged note highlighting --- browser/main/SideNav/StorageItem.js | 43 ++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/browser/main/SideNav/StorageItem.js b/browser/main/SideNav/StorageItem.js index d17314b3..3ac69818 100644 --- a/browser/main/SideNav/StorageItem.js +++ b/browser/main/SideNav/StorageItem.js @@ -25,7 +25,8 @@ class StorageItem extends React.Component { const { storage } = this.props this.state = { - isOpen: !!storage.isOpen + isOpen: !!storage.isOpen, + draggedOver: null } } @@ -231,14 +232,18 @@ 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) { + 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') + if (this.state.draggedOver === null) { return } + this.setState({ + draggedOver: null + }) } dropNote (storage, folder, dispatch, location, noteData) { @@ -263,8 +268,11 @@ class StorageItem extends React.Component { } handleDrop (e, storage, folder, dispatch, location) { - e.target.style.opacity = '1' - e.target.style.backgroundColor = e.dataTransfer.getData('defaultColor') + if (this.state.draggedOver !== null) { + this.setState({ + draggedOver: null + }) + } const noteData = JSON.parse(e.dataTransfer.getData('note')) this.dropNote(storage, folder, dispatch, location, noteData) } @@ -274,7 +282,7 @@ class StorageItem extends React.Component { const { folderNoteMap, trashedSet } = data const SortableStorageItemChild = SortableElement(StorageItemChild) const folderList = storage.folders.map((folder, index) => { - let folderRegex = new RegExp(escapeStringRegexp(path.sep) + 'storages' + escapeStringRegexp(path.sep) + storage.key + escapeStringRegexp(path.sep) + 'folders' + escapeStringRegexp(path.sep) + folder.key) + const folderRegex = new RegExp(escapeStringRegexp(path.sep) + 'storages' + escapeStringRegexp(path.sep) + storage.key + escapeStringRegexp(path.sep) + 'folders' + escapeStringRegexp(path.sep) + folder.key) const isActive = !!(location.pathname.match(folderRegex)) const noteSet = folderNoteMap.get(storage.key + '-' + folder.key) @@ -291,16 +299,25 @@ class StorageItem extends React.Component { 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) => { + e.preventDefault() + this.handleDrop(e, storage, folder, dispatch, location) + }} + handleDragEnter={(e) => { + e.preventDefault() + this.handleDragEnter(e, folder.key) + }} + handleDragLeave={(e) => { + e.preventDefault() + this.handleDragLeave(e, folder) + }} /> ) }) From 0f5d75391062348668c15bed0877003e6ec52797 Mon Sep 17 00:00:00 2001 From: William Grant Date: Sun, 2 Dec 2018 15:21:01 +0200 Subject: [PATCH 09/17] Removed the old ctrl+d shortcut for deleting a note since we now have the super+shift+backspace shortcut which can be changed in the hotkey settings --- browser/main/NoteList/index.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 08d7d2e2..730fbd1c 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -277,12 +277,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() From 5d54ad33420853f7a84eea69bb41e225f337f5e0 Mon Sep 17 00:00:00 2001 From: William Grant Date: Sun, 2 Dec 2018 15:59:47 +0200 Subject: [PATCH 10/17] Cleaned up menu items ordering and separators, added clone note menu item and shortcut, updated focus note shortcut to be mac friendly, made delete note shortcut use either command+backspace or ctrl+backspace and the delete key on windows and linux machines --- browser/main/NoteList/index.js | 3 ++ lib/main-menu.js | 68 ++++++++++++++++------------------ 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 730fbd1c..c72c9434 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -71,6 +71,7 @@ class NoteList extends React.Component { 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) diff --git a/lib/main-menu.js b/lib/main-menu.js index 91f3c8c6..6bd2bcab 100644 --- a/lib/main-menu.js +++ b/lib/main-menu.js @@ -85,14 +85,40 @@ const file = { }, { label: 'Focus Note', - accelerator: 'Control+E', + accelerator: macOS ? 'Command+E' : 'Control+E', click () { mainWindow.webContents.send('detail:focus') } }, + { + label: 'Delete Note', + accelerator: macOS ? '' : 'Delete', + accelerator: macOS ? 'Command+Backspace' : 'Control+Backspace', + click () { + mainWindow.webContents.send('detail:delete') + } + }, + { + label: 'Clone Note', + accelerator: macOS ? 'Command+D' : 'Control+D', + click () { + mainWindow.webContents.send('list:clone') + } + }, { type: 'separator' }, + { + label: 'Import from', + submenu: [ + { + label: 'Plain Text, MarkDown (.txt, .md)', + click () { + mainWindow.webContents.send('import:file') + } + } + ] + }, { label: 'Export as', submenu: [ @@ -123,18 +149,11 @@ const file = { type: 'separator' }, { - label: 'Import from', - submenu: [ - { - label: 'Plain Text, MarkDown (.txt, .md)', - click () { - mainWindow.webContents.send('import:file') - } - } - ] - }, - { - type: 'separator' + label: 'Generate/Update Markdown TOC', + accelerator: 'Shift+Ctrl+T', + click () { + mainWindow.webContents.send('code:generate-toc') + } }, { label: 'Format Table', @@ -145,16 +164,6 @@ const file = { { type: 'separator' }, - { - label: 'Generate/Update Markdown TOC', - accelerator: 'Shift+Ctrl+T', - click () { - mainWindow.webContents.send('code:generate-toc') - } - }, - { - type: 'separator' - }, { label: 'Print', accelerator: 'CommandOrControl+P', @@ -162,16 +171,6 @@ const file = { mainWindow.webContents.send('list:isMarkdownNote') mainWindow.webContents.send('print') } - }, - { - type: 'separator' - }, - { - label: 'Delete Note', - accelerator: macOS ? 'Control+Backspace' : 'Control+Delete', - click () { - mainWindow.webContents.send('detail:delete') - } } ] } @@ -296,9 +295,6 @@ const view = { mainWindow.setFullScreen(!mainWindow.isFullScreen()) } }, - { - type: 'separator' - }, { label: 'Toggle Side Bar', accelerator: 'CommandOrControl+B', From faf2aff959f1ecbca1cfe2dd69d0411277ac3008 Mon Sep 17 00:00:00 2001 From: William Grant Date: Sun, 2 Dec 2018 16:02:29 +0200 Subject: [PATCH 11/17] added in the shift key for deleting a note based on https://github.com/BoostIO/Boostnote/pull/2452 --- lib/main-menu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/main-menu.js b/lib/main-menu.js index 6bd2bcab..8317fb69 100644 --- a/lib/main-menu.js +++ b/lib/main-menu.js @@ -93,7 +93,7 @@ const file = { { label: 'Delete Note', accelerator: macOS ? '' : 'Delete', - accelerator: macOS ? 'Command+Backspace' : 'Control+Backspace', + accelerator: macOS ? 'Command+Shift+Backspace' : 'Control+Shift+Backspace', click () { mainWindow.webContents.send('detail:delete') } From 102d13bbae33b6dd47d81208ce7741bc486b3bfb Mon Sep 17 00:00:00 2001 From: William Grant Date: Sun, 2 Dec 2018 16:29:10 +0200 Subject: [PATCH 12/17] you can't have multiple accelerators on a single menu item. Bye bye delete key :( --- lib/main-menu.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/main-menu.js b/lib/main-menu.js index 8317fb69..0ec9e943 100644 --- a/lib/main-menu.js +++ b/lib/main-menu.js @@ -92,7 +92,6 @@ const file = { }, { label: 'Delete Note', - accelerator: macOS ? '' : 'Delete', accelerator: macOS ? 'Command+Shift+Backspace' : 'Control+Shift+Backspace', click () { mainWindow.webContents.send('detail:delete') From cf35dc5345e4628efd540e1ad7e6c9b99084028d Mon Sep 17 00:00:00 2001 From: Jinwoo Oh Date: Mon, 3 Dec 2018 14:04:13 +0900 Subject: [PATCH 13/17] Handle encoded uri path on `copyFile` fix #2578 --- browser/main/lib/dataApi/copyFile.js | 2 +- tests/dataApi/copyFile-test.js | 35 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 tests/dataApi/copyFile-test.js diff --git a/browser/main/lib/dataApi/copyFile.js b/browser/main/lib/dataApi/copyFile.js index 2dc66309..6f23aae2 100755 --- a/browser/main/lib/dataApi/copyFile.js +++ b/browser/main/lib/dataApi/copyFile.js @@ -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) diff --git a/tests/dataApi/copyFile-test.js b/tests/dataApi/copyFile-test.js new file mode 100644 index 00000000..412d510a --- /dev/null +++ b/tests/dataApi/copyFile-test.js @@ -0,0 +1,35 @@ +const test = require('ava') +const copyFile = require('browser/main/lib/dataApi/copyFile') + +const path = require('path') +const fs = require('fs') + +const testFile = 'test.txt' +const srcFolder = path.join(__dirname, '🤔') +const srcPath = path.join(srcFolder, testFile) +const dstFolder = path.join(__dirname, '😇') +const dstPath = path.join(dstFolder, testFile) + +test.before((t) => { + if (!fs.existsSync(srcFolder)) fs.mkdirSync(srcFolder) + + fs.writeFileSync(srcPath, 'test') +}) + +test('`copyFile` should handle encoded URI on src path', (t) => { + return copyFile(encodeURI(srcPath), dstPath) + .then(() => { + t.true(true) + }) + .catch(() => { + t.true(false) + }) +}) + +test.after((t) => { + fs.unlinkSync(srcPath) + fs.unlinkSync(dstPath) + fs.rmdirSync(srcFolder) + fs.rmdirSync(dstFolder) +}) + From 6d4aa27e15015d3d5eb0e9eaf2e553587e8c8ace Mon Sep 17 00:00:00 2001 From: Joshua Dela Cruz Date: Fri, 7 Dec 2018 17:22:01 +0800 Subject: [PATCH 14/17] Fixed Build Badge Alignment w/ Fixed link Related to PR #2584 --- readme.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index b3f2ec46..ef90dfcc 100644 --- a/readme.md +++ b/readme.md @@ -5,8 +5,11 @@

Note-taking app for programmers.

Apps available for Mac, Windows, Linux, Android, and iOS.
Built with Electron, React + Redux, Webpack, and CSSModules.
- -[![Build Status](https://travis-ci.org/BoostIO/Boostnote.svg?branch=master)](https://travis-ci.org/BoostIO/Boostnote) +

+ + Build Status + +

## Authors & Maintainers - [Rokt33r](https://github.com/rokt33r) From 406186604208fc7ea0b3be25548653b837407681 Mon Sep 17 00:00:00 2001 From: Guilherme Silva Date: Sat, 8 Dec 2018 09:06:55 +0000 Subject: [PATCH 15/17] Moving prevent default --- browser/main/SideNav/StorageItem.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/browser/main/SideNav/StorageItem.js b/browser/main/SideNav/StorageItem.js index 3ac69818..1e50d153 100644 --- a/browser/main/SideNav/StorageItem.js +++ b/browser/main/SideNav/StorageItem.js @@ -233,6 +233,7 @@ class StorageItem extends React.Component { } handleDragEnter (e, key) { + e.preventDefault() if (this.state.draggedOver === key) { return } this.setState({ draggedOver: key @@ -240,6 +241,7 @@ class StorageItem extends React.Component { } handleDragLeave (e) { + e.preventDefault() if (this.state.draggedOver === null) { return } this.setState({ draggedOver: null @@ -268,6 +270,7 @@ class StorageItem extends React.Component { } handleDrop (e, storage, folder, dispatch, location) { + e.preventDefault() if (this.state.draggedOver !== null) { this.setState({ draggedOver: null @@ -307,15 +310,12 @@ class StorageItem extends React.Component { isFolded={isFolded} noteCount={noteCount} handleDrop={(e) => { - e.preventDefault() this.handleDrop(e, storage, folder, dispatch, location) }} handleDragEnter={(e) => { - e.preventDefault() this.handleDragEnter(e, folder.key) }} handleDragLeave={(e) => { - e.preventDefault() this.handleDragLeave(e, folder) }} /> From 4488de9add3c608c85651a5ca1a7e11cd60aee99 Mon Sep 17 00:00:00 2001 From: vienai8d <31370233+vienai8d@users.noreply.github.com> Date: Sun, 9 Dec 2018 10:58:06 +0900 Subject: [PATCH 16/17] add translation about hotkeys in preferences --- locales/ja.json | 1 + 1 file changed, 1 insertion(+) diff --git a/locales/ja.json b/locales/ja.json index e33bbaa6..d1abd8b9 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -135,6 +135,7 @@ "Hotkeys": "ホットキー", "Show/Hide Boostnote": "Boostnote の表示/非表示", "Toggle Editor Mode": "エディタモードの切替", + "Delete Note": "ノート削除", "Restore": "リストア", "Permanent Delete": "永久に削除", "Confirm note deletion": "ノート削除確認", From d44a76bae308ab8d689e4b6c24e648a6eb5a9ba7 Mon Sep 17 00:00:00 2001 From: vienai8d <31370233+vienai8d@users.noreply.github.com> Date: Mon, 10 Dec 2018 00:51:43 +0900 Subject: [PATCH 17/17] add translation about interface in preferences --- locales/ja.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/locales/ja.json b/locales/ja.json index d1abd8b9..9cfc2986 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -37,9 +37,15 @@ "White": "白", "Solarized Dark": "明灰", "Dark": "暗灰", + "Default New Note": "新規ノートの形式", + "Always Ask": "作成時に聞く", "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)", + "Save tags of a note in alphabetical order": "ノートのタグをアルファベット順に保存する", + "Show tags of a note in alphabetical order": "ノートのタグをアルファベット順に表示する", "Show only related tags": "関連するタグのみ表示する", + "Enable live count of notes": "タグ選択時にノート数を再計算して表示する", + "New notes are tagged with the filtering tags": "新規ノートに選択中のタグを付与する", "Editor Theme": "エディタのテーマ", "Editor Font Size": "エディタのフォントサイズ", "Editor Font Family": "エディタのフォント", @@ -55,21 +61,28 @@ "vim": "vim", "emacs": "emacs", "⚠️ Please restart boostnote after you change the keymap": "⚠️ キーマップ変更後は Boostnote を再起動してください", + "Snippet Default Language": "スニペットのデフォルト言語", + "Extract title from front matter": "Front matterからタイトルを抽出する", "Show line numbers in the editor": "エディタ内に行番号を表示", "Allow editor to scroll past the last line": "エディタが最終行以降にスクロールできるようにする", "Enable smart quotes": "スマートクォートを有効にする", "Bring in web page title when pasting URL on editor": "URLを貼り付けた時にWebページのタイトルを取得する", + "Enable smart table editor": "スマートテーブルエディタを有効にする", "Preview": "プレビュー", "Preview Font Size": "プレビュー時フォントサイズ", "Preview Font Family": "プレビュー時フォント", "Code Block Theme": "コードブロックのテーマ", + "Allow line through checkbox": "チェック済みチェックボックスのテキストに取り消し線を付与する", "Allow preview to scroll past the last line": "プレビュー時に最終行以降にスクロールできるようにする", + "When scrolling, synchronize preview with editor": "エディタとプレビューのスクロールを同期する", "Show line numbers for preview code blocks": "プレビュー時のコードブロック内に行番号を表示する", "LaTeX Inline Open Delimiter": "LaTeX 開始デリミタ(インライン)", "LaTeX Inline Close Delimiter": "LaTeX 終了デリミタ(インライン)", "LaTeX Block Open Delimiter": "LaTeX 開始デリミタ(ブロック)", "LaTeX Block Close Delimiter": "LaTeX 終了デリミタ(ブロック)", "PlantUML Server": "PlantUML サーバー", + "Custom CSS": "カスタムCSS", + "Allow custom CSS for preview": "プレビュー用のカスタムCSSを許可する", "Community": "コミュニティ", "Subscribe to Newsletter": "ニュースレターを購読する", "GitHub": "GitHub",