From c7303e16ed6bbdf8942d624303cd47feab9a4151 Mon Sep 17 00:00:00 2001 From: slaveeks Date: Thu, 4 Jul 2024 19:25:32 +0300 Subject: [PATCH] feat: added custom downloader --- README.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++- src/downloader.js | 51 ++++++++++++++++++++++++++++++++++++++++++++++ src/index.js | 42 ++++++++++++++++++++++++++++++++++++-- src/ui.js | 1 - 4 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 src/downloader.js diff --git a/README.md b/README.md index a0d59f39..e186a86a 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ Image Tool supports these configuration parameters: | captionPlaceholder | `string` | (default: `Caption`) Placeholder for Caption input | | buttonContent | `string` | Allows to override HTML content of «Select file» button | | uploader | `{{uploadByFile: function, uploadByUrl: function}}` | Optional custom uploading methods. See details below. | +| downloader | `{{download: function}}` | Optional custom downloading method. See details below. | | actions | `array` | Array with custom actions to show in the tool's settings menu. See details below. | Note that if you don't implement your custom uploader methods, the `endpoints` param is required. @@ -178,7 +179,7 @@ The response of your uploader **should** cover the following format: **success** - uploading status. 1 for successful, 0 for failed -**file** - uploaded file data. **Must** contain an `url` field with full public path to the uploaded image. +**file** - uploaded file data. **Must** contain an `url` field with full public path to the uploaded image or data key for using it in custom downloader. Also, can contain any additional fields you want to store. For example, width, height, id etc. All additional fields will be saved at the `file` object of output data. @@ -281,3 +282,52 @@ var editor = EditorJS({ ... }); ``` + +## Providing custom downloading method + +As mentioned at the Config Params section, you have an ability to provide own custom downloading method. +It is a quite simple: implement `download` method and pass it via `downloader` config param. +Method must return a Promise that resolves with url, which we can pass to the element and show it + + +| Method | Arguments | Return value | Description | +| -------------- | --------- | ------------- | ------------| +| download | `fileData`| `{Promise.}` | Download file by file data convert it and return valid file url | + +Example: + +```js +import ImageTool from '@editorjs/image'; + +var editor = EditorJS({ + ... + + tools: { + ... + image: { + class: ImageTool, + config: { + /** + * Custom downloader + */ + downloader: { + /** + * Download file from the server using file data. + * @param {string} fileData - data required for downloading + * @return {Promise.} - valid url + */ + uploadByU + download(fileData) { + // your ajax request for downloading + return MyAjax.download(fileData).then((data) => { + return URL.createObjectURL(data); + }) + } + } + } + } + } + + ... +}); +``` \ No newline at end of file diff --git a/src/downloader.js b/src/downloader.js new file mode 100644 index 00000000..c1020c4b --- /dev/null +++ b/src/downloader.js @@ -0,0 +1,51 @@ +import isPromise from './utils/isPromise'; + +/** + * Module for file downloading. Handle case, when you have to use custom downloading method + */ +export default class Downloader { + /** + * @param {object} params - downloader module params + * @param {ImageConfig} params.config - image tool config + * @param {Function} params.onDownload - callback which is called, when file is downloaded + * @param {Function} params.onError - callback for downloading errors + */ + constructor({ config, onDownload, onError }) { + this.config = config; + this.onDownload = onDownload + this.onError = onError; + } + + /** + * Try to download file data and fill it using stored data + * + * @param {string} fileData - may be some key for custom downloading or url + */ + download(fileData) { + /** + * Check that custom downloader passed + */ + if (this.config.downloader && typeof this.config.downloader.download === 'function') { + const downloadData = this.config.downloader.download(fileData); + + if (!isPromise(downloadData)) { + console.warn('Custom downloader method download should return a Promise'); + } + + downloadData.then((response) => { + /** + * Call callback for successful downloading with url + */ + this.onDownload(response); + }).catch((error) => { + this.onError(error); + }); + } else { + /** + * If there is no custom download method, fileData is correct url + * We only need to call callback + */ + this.onDownload(fileData); + } + } +} diff --git a/src/index.js b/src/index.js index ff00b214..c4273c10 100644 --- a/src/index.js +++ b/src/index.js @@ -11,6 +11,7 @@ * 2) uploader.js — module that has methods for sending files via AJAX: from device, by URL or File pasting * 3) ui.js — module for UI manipulations: render, showing preloader, etc * 4) tunes.js — working with Block Tunes: render buttons, handle clicks + * 5) downloader.js - module, which helps to use custom downloader using stored file data key * * For debug purposes there is a testing server * that can save uploaded files and return a Response {@link UploadResponseFormat} @@ -41,6 +42,7 @@ * @property {string} file.url — image URL */ +import Downloader from './downloader'; import './index.css'; import Ui from './ui'; @@ -61,6 +63,7 @@ import { IconAddBorder, IconStretch, IconAddBackground, IconPicture } from '@cod * @property {object} additionalRequestHeaders - allows to pass custom headers with Request * @property {string} buttonContent - overrides for Select File button * @property {object} [uploader] - optional custom uploader + * @property {object} [downloader] - optional custom downloader * @property {function(File): Promise.} [uploader.uploadByFile] - method that upload image by File * @property {function(string): Promise.} [uploader.uploadByUrl] - method that upload image by URL */ @@ -72,7 +75,7 @@ import { IconAddBorder, IconStretch, IconAddBackground, IconPicture } from '@cod * @property {object} file - Object with file data. * 'url' is required, * also can contain any additional data that will be saved and passed back - * @property {string} file.url - [Required] image source URL + * @property {string} file.url - [Required] image source URL or file data key to load it via custom downloader */ export default class ImageTool { /** @@ -152,6 +155,7 @@ export default class ImageTool { buttonContent: config.buttonContent || '', uploader: config.uploader || undefined, actions: config.actions || [], + downloader: config.downloader || undefined }; /** @@ -179,6 +183,15 @@ export default class ImageTool { readOnly, }); + /** + * Module for file downloading + */ + this.downloader = new Downloader({ + config: this.config, + onDownload: (url) => this.onDownload(url), + onError: this.downloadingFileError, + }) + /** * Set saved state */ @@ -383,7 +396,7 @@ export default class ImageTool { this._data.file = file || {}; if (file && file.url) { - this.ui.fillImage(file.url); + this.downloader.download(file.url) } } @@ -420,6 +433,31 @@ export default class ImageTool { this.ui.hidePreloader(); } + /** + * Handle downloader errors + * + * @private + * @param {string} errorText - downloading error text + * @returns {void} + */ + downloadingFileError(errorText) { + console.log('Image Tool: downloading failed because of', errorText); + + this.api.notifier.show({ + message: this.api.i18n.t('Couldn’t download image. Please try another.'), + style: 'error', + }); + } + + /** + * File downloading callback, fills file data into image + * + * @param {*} url - file url after downloading + */ + onDownload(url) { + this.ui.fillImage(url) + } + /** * Callback fired when Block Tune is activated * diff --git a/src/ui.js b/src/ui.js index 8609ef21..d63cfe08 100644 --- a/src/ui.js +++ b/src/ui.js @@ -188,7 +188,6 @@ export default class Ui { */ eventName = 'loadeddata'; } - /** * Compose tag with defined attributes *