diff --git a/ElectronClient/gui/NoteText.jsx b/ElectronClient/gui/NoteText.jsx index a1461ff8ba..04d3516dff 100644 --- a/ElectronClient/gui/NoteText.jsx +++ b/ElectronClient/gui/NoteText.jsx @@ -1225,7 +1225,14 @@ class NoteTextComponent extends React.Component { const filePath = filePaths[i]; try { reg.logger().info(`Attaching ${filePath}`); - note = await shim.attachFileToNote(note, filePath, position, createFileURL); + note = await shim.attachFileToNote(note, filePath, position, { + createFileURL: createFileURL, + resizeLargeImages: 'ask', + }); + if (!note) { + reg.logger().info('File attachment was cancelled'); + continue; + } reg.logger().info('File was attached.'); this.setState({ note: Object.assign({}, note), diff --git a/ReactNativeClient/lib/components/screens/note.js b/ReactNativeClient/lib/components/screens/note.js index 16060c9b73..713e7deff7 100644 --- a/ReactNativeClient/lib/components/screens/note.js +++ b/ReactNativeClient/lib/components/screens/note.js @@ -419,27 +419,47 @@ class NoteScreenComponent extends BaseScreenComponent { const dimensions = await this.imageDimensions(localFilePath); reg.logger().info('Original dimensions ', dimensions); - if (dimensions.width > maxSize || dimensions.height > maxSize) { + + let mustResize = dimensions.width > maxSize || dimensions.height > maxSize; + + if (mustResize) { + const buttonId = await dialogs.pop(this, _('You are about to attach a large image (%dx%d pixels). Would you like to resize it down to %d pixels before attaching it?', dimensions.width, dimensions.height, maxSize), [ + { text: _('Yes'), id: 'yes' }, + { text: _('No'), id: 'no' }, + { text: _('Cancel'), id: 'cancel' }, + ]); + + if (buttonId === 'cancel') return false; + + mustResize = buttonId === 'yes'; + } + + if (mustResize) { dimensions.width = maxSize; dimensions.height = maxSize; + + reg.logger().info('New dimensions ', dimensions); + + const format = mimeType == 'image/png' ? 'PNG' : 'JPEG'; + reg.logger().info(`Resizing image ${localFilePath}`); + const resizedImage = await ImageResizer.createResizedImage(localFilePath, dimensions.width, dimensions.height, format, 85); // , 0, targetPath); + + const resizedImagePath = resizedImage.uri; + reg.logger().info('Resized image ', resizedImagePath); + reg.logger().info(`Moving ${resizedImagePath} => ${targetPath}`); + + await RNFS.copyFile(resizedImagePath, targetPath); + + try { + await RNFS.unlink(resizedImagePath); + } catch (error) { + reg.logger().warn('Error when unlinking cached file: ', error); + } + } else { + await RNFS.copyFile(localFilePath, targetPath); } - reg.logger().info('New dimensions ', dimensions); - const format = mimeType == 'image/png' ? 'PNG' : 'JPEG'; - reg.logger().info(`Resizing image ${localFilePath}`); - const resizedImage = await ImageResizer.createResizedImage(localFilePath, dimensions.width, dimensions.height, format, 85); // , 0, targetPath); - - const resizedImagePath = resizedImage.uri; - reg.logger().info('Resized image ', resizedImagePath); - reg.logger().info(`Moving ${resizedImagePath} => ${targetPath}`); - - await RNFS.copyFile(resizedImagePath, targetPath); - - try { - await RNFS.unlink(resizedImagePath); - } catch (error) { - reg.logger().warn('Error when unlinking cached file: ', error); - } + return true; } async attachFile(pickerResponse, fileType) { @@ -494,7 +514,8 @@ class NoteScreenComponent extends BaseScreenComponent { try { if (mimeType == 'image/jpeg' || mimeType == 'image/jpg' || mimeType == 'image/png') { - await this.resizeImage(localFilePath, targetPath, pickerResponse.mime); + const done = await this.resizeImage(localFilePath, targetPath, pickerResponse.mime); + if (!done) return; } else { if (fileType === 'image') { dialogs.error(this, _('Unsupported image type: %s', mimeType)); diff --git a/ReactNativeClient/lib/shim-init-node.js b/ReactNativeClient/lib/shim-init-node.js index 123dc5217a..6d6984f07c 100644 --- a/ReactNativeClient/lib/shim-init-node.js +++ b/ReactNativeClient/lib/shim-init-node.js @@ -64,7 +64,16 @@ function shimInit() { } }; - const resizeImage_ = async function(filePath, targetPath, mime) { + shim.showMessageBox = (message, options = null) => { + if (shim.isElectron()) { + const { bridge } = require('electron').remote.require('./bridge'); + return bridge().showMessageBox(message, options); + } else { + throw new Error('Not implemented'); + } + }; + + const handleResizeImage_ = async function(filePath, targetPath, mime, resizeLargeImages) { const maxDim = Resource.IMAGE_MAX_DIMENSION; if (shim.isElectron()) { @@ -75,9 +84,21 @@ function shimInit() { const size = image.getSize(); - if (size.width <= maxDim && size.height <= maxDim) { + let mustResize = size.width > maxDim || size.height > maxDim; + + if (mustResize && resizeLargeImages === 'ask') { + const answer = shim.showMessageBox(_('You are about to attach a large image (%dx%d pixels). Would you like to resize it down to %d pixels before attaching it?', size.width, size.height, maxDim), { + buttons: [_('Yes'), _('No'), _('Cancel')], + }); + + if (answer === 2) return false; + + mustResize = answer === 0; + } + + if (!mustResize) { shim.fsDriver().copy(filePath, targetPath); - return; + return true; } const options = {}; @@ -99,7 +120,7 @@ function shimInit() { if (md.width <= maxDim && md.height <= maxDim) { shim.fsDriver().copy(filePath, targetPath); - return; + return true; } return new Promise((resolve, reject) => { @@ -117,9 +138,15 @@ function shimInit() { }); }); } + + return true; }; - shim.createResourceFromPath = async function(filePath, defaultProps = null) { + shim.createResourceFromPath = async function(filePath, defaultProps = null, options = null) { + options = Object.assign({ + resizeLargeImages: 'always', // 'always' or 'ask' + }, options); + const readChunk = require('read-chunk'); const imageType = require('image-type'); @@ -155,8 +182,9 @@ function shimInit() { const targetPath = Resource.fullPath(resource); - if (resource.mime == 'image/jpeg' || resource.mime == 'image/jpg' || resource.mime == 'image/png') { - await resizeImage_(filePath, targetPath, resource.mime); + if (['image/jpeg', 'image/jpg', 'image/png'].includes(resource.mime)) { + const ok = await handleResizeImage_(filePath, targetPath, resource.mime, options.resizeLargeImages); + if (!ok) return null; } else { // const stat = await shim.fsDriver().stat(filePath); // if (stat.size >= 10000000) throw new Error('Resources larger than 10 MB are not currently supported as they may crash the mobile applications. The issue is being investigated and will be fixed at a later time.'); @@ -177,16 +205,22 @@ function shimInit() { return Resource.save(resource, { isNew: true }); }; - shim.attachFileToNote = async function(note, filePath, position = null, createFileURL = false) { + shim.attachFileToNote = async function(note, filePath, position = null, options = null) { + options = Object.assign({}, { + createFileURL: false, + }, options); + const { basename } = require('path'); const { escapeLinkText } = require('lib/markdownUtils'); const { toFileProtocolPath } = require('lib/path-utils'); - let resource = []; - if (!createFileURL) { - resource = await shim.createResourceFromPath(filePath); + let resource = null; + if (!options.createFileURL) { + resource = await shim.createResourceFromPath(filePath, null, options); } + if (!resource) return null; + const newBody = []; if (position === null) { @@ -195,7 +229,7 @@ function shimInit() { if (note.body && position) newBody.push(note.body.substr(0, position)); - if (!createFileURL) { + if (!options.createFileURL) { newBody.push(Resource.markdownTag(resource)); } else { const filename = escapeLinkText(basename(filePath)); // to get same filename as standard drag and drop diff --git a/ReactNativeClient/lib/shim.js b/ReactNativeClient/lib/shim.js index 9912c5ad11..c5ab7ec332 100644 --- a/ReactNativeClient/lib/shim.js +++ b/ReactNativeClient/lib/shim.js @@ -220,4 +220,8 @@ shim.pathRelativeToCwd = (path) => { throw new Error('Not implemented'); }; +shim.showMessageBox = (message, options = null) => { + throw new Error('Not implemented'); +}; + module.exports = { shim }; diff --git a/gulpfile.js b/gulpfile.js index 860609d803..8ef6c55749 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -44,7 +44,7 @@ gulp.task('watch', function() { // For watching, we use the actual tsc tool because it's more robust and // doesn't crash when there's an error - const promise = execa('node', ['node_modules/typescript/bin/tsc', '--project', 'tsconfig.json'], { cwd: `${__dirname}` }); + const promise = execa('node', ['node_modules/typescript/bin/tsc', '--watch', '--project', 'tsconfig.json'], { cwd: `${__dirname}` }); promise.stdout.pipe(process.stdout); });