Skip to content

Commit

Permalink
feat: added custom downloader
Browse files Browse the repository at this point in the history
  • Loading branch information
slaveeks committed Jul 4, 2024
1 parent 25d46cd commit c7303e1
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 4 deletions.
52 changes: 51 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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.<string>}` | 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.<string>} - valid url
*/
uploadByU
download(fileData) {
// your ajax request for downloading
return MyAjax.download(fileData).then((data) => {
return URL.createObjectURL(data);
})
}
}
}
}
}

...
});
```
51 changes: 51 additions & 0 deletions src/downloader.js
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
42 changes: 40 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -41,6 +42,7 @@
* @property {string} file.url — image URL
*/

import Downloader from './downloader';
import './index.css';

import Ui from './ui';
Expand All @@ -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.<UploadResponseFormat>} [uploader.uploadByFile] - method that upload image by File
* @property {function(string): Promise.<UploadResponseFormat>} [uploader.uploadByUrl] - method that upload image by URL
*/
Expand All @@ -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 {
/**
Expand Down Expand Up @@ -152,6 +155,7 @@ export default class ImageTool {
buttonContent: config.buttonContent || '',
uploader: config.uploader || undefined,
actions: config.actions || [],
downloader: config.downloader || undefined
};

/**
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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
*
Expand Down
1 change: 0 additions & 1 deletion src/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,6 @@ export default class Ui {
*/
eventName = 'loadeddata';
}

/**
* Compose tag with defined attributes
*
Expand Down

0 comments on commit c7303e1

Please sign in to comment.