1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-11-27 08:21:03 +02:00

Electron: Resolves #200, Resolves #416: Allow attaching images by pasting them in. Allow attaching files by drag and dropping them. Insert attachement at cursor position.

This commit is contained in:
Laurent Cozic 2018-05-10 10:45:44 +01:00
parent 5e3063abe0
commit ef2ffd4e52
4 changed files with 102 additions and 20 deletions

View File

@ -20,6 +20,9 @@ const MenuItem = bridge().MenuItem;
const { shim } = require('lib/shim.js');
const eventManager = require('../eventManager');
const fs = require('fs-extra');
const {clipboard} = require('electron')
const md5 = require('md5');
const mimeUtils = require('lib/mime-utils.js').mime;
require('brace/mode/markdown');
// https://ace.c9.io/build/kitchen-sink.html
@ -72,6 +75,62 @@ class NoteTextComponent extends React.Component {
this.onAlarmChange_ = (event) => { if (event.noteId === this.props.noteId) this.reloadNote(this.props); }
this.onNoteTypeToggle_ = (event) => { if (event.noteId === this.props.noteId) this.reloadNote(this.props); }
this.onTodoToggle_ = (event) => { if (event.noteId === this.props.noteId) this.reloadNote(this.props); }
this.onEditorPaste_ = async (event) => {
const formats = clipboard.availableFormats();
for (let i = 0; i < formats.length; i++) {
const format = formats[i].toLowerCase();
const formatType = format.split('/')[0]
if (formatType === 'image') {
event.preventDefault();
const image = clipboard.readImage();
const fileExt = mimeUtils.toFileExtension(format);
const filePath = Setting.value('tempDir') + '/' + md5(Date.now()) + '.' + fileExt;
await shim.writeImageToFile(image, format, filePath);
await this.commandAttachFile([filePath]);
await shim.fsDriver().remove(filePath);
}
}
}
this.onDrop_ = async (event) => {
const files = event.dataTransfer.files;
if (!files || !files.length) return;
const filesToAttach = [];
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (!file.path) continue;
filesToAttach.push(file.path);
}
await this.commandAttachFile(filesToAttach);
}
}
cursorPosition() {
if (!this.editor_ || !this.editor_.editor || !this.state.note || !this.state.note.body) return 0;
const cursorPos = this.editor_.editor.getCursorPosition();
const noteLines = this.state.note.body.split('\n');
let pos = 0;
for (let i = 0; i < noteLines.length; i++) {
if (i > 0) pos++; // Need to add the newline that's been removed in the split() call above
if (i === cursorPos.row) {
pos += cursorPos.column;
break;
} else {
pos += noteLines[i].length;
}
}
return pos;
}
mdToHtml() {
@ -421,6 +480,7 @@ class NoteTextComponent extends React.Component {
if (this.editor_) {
this.editor_.editor.renderer.off('afterRender', this.onAfterEditorRender_);
document.querySelector('#note-editor').removeEventListener('paste', this.onEditorPaste_, true);
}
this.editor_ = element;
@ -446,6 +506,8 @@ class NoteTextComponent extends React.Component {
throw new Error('HACK: Overriding Ace Editor shortcut: ' + k);
});
}
document.querySelector('#note-editor').addEventListener('paste', this.onEditorPaste_, true);
}
}
@ -516,20 +578,24 @@ class NoteTextComponent extends React.Component {
}
}
async commandAttachFile() {
const filePaths = bridge().showOpenDialog({
properties: ['openFile', 'createDirectory', 'multiSelections'],
});
if (!filePaths || !filePaths.length) return;
async commandAttachFile(filePaths = null) {
if (!filePaths) {
filePaths = bridge().showOpenDialog({
properties: ['openFile', 'createDirectory', 'multiSelections'],
});
if (!filePaths || !filePaths.length) return;
}
await this.saveIfNeeded(true);
let note = await Note.load(this.state.note.id);
const position = this.cursorPosition();
for (let i = 0; i < filePaths.length; i++) {
const filePath = filePaths[i];
try {
reg.logger().info('Attaching ' + filePath);
note = await shim.attachFileToNote(note, filePath);
note = await shim.attachFileToNote(note, filePath, position);
reg.logger().info('File was attached.');
this.setState({
note: Object.assign({}, note),
@ -801,7 +867,7 @@ class NoteTextComponent extends React.Component {
/>
return (
<div style={rootStyle}>
<div style={rootStyle} onDrop={this.onDrop_}>
<div style={titleBarStyle}>
{ titleEditor }
{ titleBarDate }

View File

@ -183,6 +183,8 @@ For a more technical description, mostly relevant for development or to review t
Any kind of file can be attached to a note. In Markdown, links to these files are represented as a simple ID to the resource. In the note viewer, these files, if they are images, will be displayed or, if they are other files (PDF, text files, etc.) they will be displayed as links. Clicking on this link will open the file in the default application.
On the **desktop application**, images can be attached either by clicking on "Attach file" or by pasting (with Ctrl+V) an image directly in the editor, or by drag and dropping an image.
Resources that are not attached to any note will be automatically deleted after a day or two.
**Important:** Resources larger than 10 MB are not currently supported on mobile. They will crash the application when synchronising so it is recommended not to attach such resources at the moment. The issue is being looked at.

View File

@ -35,6 +35,23 @@ function shimInit() {
return locale;
}
// For Electron only
shim.writeImageToFile = async function(nativeImage, mime, targetPath) {
let buffer = null;
mime = mime.toLowerCase();
if (mime === 'image/png') {
buffer = nativeImage.toPNG();
} else if (mime === 'image/jpg' || mime === 'image/jpeg') {
buffer = nativeImage.toJPEG(90);
}
if (!buffer) throw new Error('Cannot reisze image because mime type "' + mime + '" is not supported: ' + targetPath);
await shim.fsDriver().writeFile(targetPath, buffer, 'buffer');
}
const resizeImage_ = async function(filePath, targetPath, mime) {
if (shim.isElectron()) { // For Electron
const nativeImage = require('electron').nativeImage;
@ -58,17 +75,7 @@ function shimInit() {
image = image.resize(options);
let buffer = null;
if (mime === 'image/png') {
buffer = image.toPNG();
} else if (mime === 'image/jpg' || mime === 'image/jpeg') {
buffer = image.toJPEG(90);
}
if (!buffer) throw new Error('Cannot reisze image because mime type "' + mime + '" is not supported: ' + targetPath);
await shim.fsDriver().writeFile(targetPath, buffer, 'buffer');
await shim.writeImageToFile(image, mime, targetPath);
} else { // For the CLI tool
const sharp = require('sharp');
const Resource = require('lib/models/Resource.js');
@ -89,7 +96,7 @@ function shimInit() {
}
}
shim.attachFileToNote = async function(note, filePath) {
shim.attachFileToNote = async function(note, filePath, position = null) {
const Resource = require('lib/models/Resource.js');
const { uuid } = require('lib/uuid.js');
const { basename, fileExtension, safeFileExtension } = require('lib/path-utils.js');
@ -120,8 +127,14 @@ function shimInit() {
await Resource.save(resource, { isNew: true });
const newBody = [];
if (note.body) newBody.push(note.body);
if (position === null) {
position = note.body ? note.body.length : 0;
}
if (note.body && position) newBody.push(note.body.substr(0, position));
newBody.push(Resource.markdownTag(resource));
newBody.push(note.body.substr(position));
const newNote = Object.assign({}, note, {
body: newBody.join('\n\n'),

View File

@ -409,6 +409,7 @@
<p>For a more technical description, mostly relevant for development or to review the method being used, please see the <a href="https://joplin.cozic.net/spec">Encryption specification</a>.</p>
<h1 id="attachments-resources">Attachments / Resources</h1>
<p>Any kind of file can be attached to a note. In Markdown, links to these files are represented as a simple ID to the resource. In the note viewer, these files, if they are images, will be displayed or, if they are other files (PDF, text files, etc.) they will be displayed as links. Clicking on this link will open the file in the default application.</p>
<p>On the <strong>desktop application</strong>, images can be attached either by clicking on &quot;Attach file&quot; or by pasting (with Ctrl+V) an image directly in the editor, or by drag and dropping an image.</p>
<p>Resources that are not attached to any note will be automatically deleted after a day or two.</p>
<p><strong>Important:</strong> Resources larger than 10 MB are not currently supported on mobile. They will crash the application when synchronising so it is recommended not to attach such resources at the moment. The issue is being looked at.</p>
<h1 id="notifications">Notifications</h1>