From fc0d227396652eb539bba7ec09288d50fe66a98c Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Wed, 7 Feb 2018 20:23:17 +0000 Subject: [PATCH] Electron: Allowing opening and saving resource images --- ElectronClient/app/gui/NoteText.jsx | 29 ++++++++++++++++++- ElectronClient/app/gui/note-viewer/index.html | 10 +++++++ ReactNativeClient/lib/MdToHtml.js | 2 +- .../components/shared/note-screen-shared.js | 1 - 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/ElectronClient/app/gui/NoteText.jsx b/ElectronClient/app/gui/NoteText.jsx index b17c1e31f7..da786e5415 100644 --- a/ElectronClient/app/gui/NoteText.jsx +++ b/ElectronClient/app/gui/NoteText.jsx @@ -16,6 +16,7 @@ const Menu = bridge().Menu; const MenuItem = bridge().MenuItem; const { shim } = require('lib/shim.js'); const eventManager = require('../eventManager'); +const fs = require('fs-extra'); require('brace/mode/markdown'); // https://ace.c9.io/build/kitchen-sink.html @@ -264,7 +265,7 @@ class NoteTextComponent extends React.Component { shared.showMetadata_onPress(this); } - webview_ipcMessage(event) { + async webview_ipcMessage(event) { const msg = event.channel ? event.channel : ''; const args = event.args; const arg0 = args && args.length >= 1 ? args[0] : null; @@ -286,6 +287,32 @@ class NoteTextComponent extends React.Component { } else if (msg === 'percentScroll') { this.ignoreNextEditorScroll_ = true; this.setEditorPercentScroll(arg0); + } else if (msg === 'contextMenu') { + const itemType = arg0 && arg0.type; + + const menu = new Menu() + + if (itemType === 'image') { + const resource = await Resource.load(arg0.resourceId); + const resourcePath = Resource.fullPath(resource); + + menu.append(new MenuItem({label: _('Open...'), click: async () => { + bridge().openExternal(resourcePath); + }})); + + menu.append(new MenuItem({label: _('Save as...'), click: async () => { + const filePath = bridge().showSaveDialog({ + defaultPath: resource.filename ? resource.filename : resource.title, + }); + if (!filePath) return; + await fs.copy(resourcePath, filePath); + }})); + } else { + reg.logger().error('Unhandled item type: ' + itemType); + return; + } + + menu.popup(bridge().window()); } else if (msg.indexOf('joplin://') === 0) { const resourceId = msg.substr('joplin://'.length); Resource.load(resourceId).then((resource) => { diff --git a/ElectronClient/app/gui/note-viewer/index.html b/ElectronClient/app/gui/note-viewer/index.html index 5d5213e3e3..80884613b4 100644 --- a/ElectronClient/app/gui/note-viewer/index.html +++ b/ElectronClient/app/gui/note-viewer/index.html @@ -180,6 +180,16 @@ ipcRenderer.sendToHost('percentScroll', percent); }); + document.addEventListener('contextmenu', function(event) { + const element = event.target; + if (element && element.getAttribute('data-resource-id')) { + ipcRenderer.sendToHost('contextMenu', { + type: element.getAttribute('src') ? 'image' : 'link', + resourceId: element.getAttribute('data-resource-id'), + }); + } + }); + // Disable drag and drop otherwise it's possible to drop a URL // on it and it will open in the view as a website. document.addEventListener('drop', function(e) { diff --git a/ReactNativeClient/lib/MdToHtml.js b/ReactNativeClient/lib/MdToHtml.js index 9c4f11bf0f..f838558ce8 100644 --- a/ReactNativeClient/lib/MdToHtml.js +++ b/ReactNativeClient/lib/MdToHtml.js @@ -117,7 +117,7 @@ class MdToHtml { if (mime == 'image/png' || mime == 'image/jpg' || mime == 'image/jpeg' || mime == 'image/gif') { let src = './' + Resource.filename(resource); if (this.resourceBaseUrl_ !== null) src = this.resourceBaseUrl_ + src; - let output = ''; + let output = ''; return output; } diff --git a/ReactNativeClient/lib/components/shared/note-screen-shared.js b/ReactNativeClient/lib/components/shared/note-screen-shared.js index 096cd54d86..3dc314f33e 100644 --- a/ReactNativeClient/lib/components/shared/note-screen-shared.js +++ b/ReactNativeClient/lib/components/shared/note-screen-shared.js @@ -30,7 +30,6 @@ shared.saveNoteButton_press = async function(comp) { options.fields = BaseModel.diffObjectsFields(comp.state.lastSavedNote, note); } - const hasAutoTitle = comp.state.newAndNoTitleChangeNoteId || (isNew && !note.title); if (hasAutoTitle) { note.title = Note.defaultTitle(note);