diff --git a/app/javascript/article-form/actions.js b/app/javascript/article-form/actions.js index 494dfa92f..3e06c9272 100644 --- a/app/javascript/article-form/actions.js +++ b/app/javascript/article-form/actions.js @@ -1,3 +1,5 @@ +import { validateFileInputs } from '../packs/validateFileInputs'; + export function previewArticle(payload, successCb, failureCb) { fetch('/articles/preview', { method: 'POST', @@ -97,3 +99,23 @@ export function generateMainImage(payload, successCb, failureCb) { }) .catch(failureCb); } + +/** + * Processes images for upload. + * + * @param {FileList} images Images to be uploaded. + * @param {Function} handleImageSuccess The handler that runs when the image is uploaded successfully. + * @param {Function} handleImageFailure The handler that runs when the image upload fails. + */ +export function processImageUpload( + images, + handleImageSuccess, + handleImageFailure, +) { + // Currently only one image is supported for upload. + if (images.length > 0 && validateFileInputs()) { + const payload = { image: images }; + + generateMainImage(payload, handleImageSuccess, handleImageFailure); + } +} diff --git a/app/javascript/article-form/components/ArticleCoverImage.jsx b/app/javascript/article-form/components/ArticleCoverImage.jsx index 8caa0db7b..76c6cc2d3 100644 --- a/app/javascript/article-form/components/ArticleCoverImage.jsx +++ b/app/javascript/article-form/components/ArticleCoverImage.jsx @@ -1,9 +1,12 @@ import { h, Component, Fragment } from 'preact'; import PropTypes from 'prop-types'; +import { addSnackbarItem } from '../../Snackbar'; import { generateMainImage } from '../actions'; import { validateFileInputs } from '../../packs/validateFileInputs'; +import { onDragOver, onDragExit } from './dragAndDropHelpers'; import { Button } from '@crayons'; import { Spinner } from '@crayons/Spinner/Spinner'; +import { DragAndDropZone } from '@utilities/dragAndDrop'; export class ArticleCoverImage extends Component { state = { @@ -17,14 +20,16 @@ export class ArticleCoverImage extends Component { this.setState({ uploadingImage: false }); }; - handleMainImageUpload = (e) => { - e.preventDefault(); + handleMainImageUpload = (event) => { + event.preventDefault(); this.setState({ uploadingImage: true }); this.clearUploadError(); if (validateFileInputs()) { - const payload = { image: e.target.files, wrap_cloudinary: true }; + const { files: image } = + event instanceof DragEvent ? event.dataTransfer : event.target; + const payload = { image, wrap_cloudinary: true }; generateMainImage(payload, this.onImageUploadSuccess, this.onUploadError); } @@ -53,55 +58,75 @@ export class ArticleCoverImage extends Component { }); }; + onDropImage = (event) => { + onDragExit(event); + + if (event.dataTransfer.files.length > 1) { + addSnackbarItem({ + message: 'Only one image can be dropped at a time.', + addCloseButton: true, + }); + return; + } + + this.handleMainImageUpload(event); + }; + render() { const { mainImage } = this.props; const { uploadError, uploadErrorMessage, uploadingImage } = this.state; const uploadLabel = mainImage ? 'Change' : 'Add a cover image'; return ( -