import { h, Fragment } from 'preact'; import { useState } from 'preact/hooks'; 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'; import { isNativeIOS } from '@utilities/runtime'; const NativeIosImageUpload = ({ extraProps, uploadLabel, isUploadingImage, }) => ( {isUploadingImage ? null : ( {uploadLabel} )} ); const StandardImageUpload = ({ uploadLabel, handleImageUpload, isUploadingImage, }) => isUploadingImage ? null : ( {uploadLabel} Use a ratio of 100:42 for best results. ); export const ArticleCoverImage = ({ onMainImageUrlChange, mainImage }) => { const [uploadError, setUploadError] = useState(false); const [uploadErrorMessage, setUploadErrorMessage] = useState(null); const [uploadingImage, setUploadingImage] = useState(false); const onImageUploadSuccess = (...args) => { onMainImageUrlChange(...args); setUploadingImage(false); }; const handleMainImageUpload = (event) => { event.preventDefault(); setUploadingImage(true); clearUploadError(); if (validateFileInputs()) { const { files: image } = event.dataTransfer || event.target; const payload = { image }; generateMainImage({ payload, successCb: onImageUploadSuccess, failureCb: onUploadError, }); } else { setUploadingImage(false); } }; const clearUploadError = () => { setUploadError(false); setUploadErrorMessage(null); }; const onUploadError = (error) => { setUploadingImage(false); setUploadError(true); setUploadErrorMessage(error.message); }; const useNativeUpload = () => { return isNativeIOS('imageUpload'); }; const initNativeImagePicker = (e) => { e.preventDefault(); window.ForemMobile?.injectNativeMessage('coverUpload', { action: 'coverImageUpload', ratio: `${100.0 / 42.0}`, }); }; const handleNativeMessage = (e) => { const message = JSON.parse(e.detail); if (message.namespace !== 'coverUpload') { return; } /* eslint-disable no-case-declarations */ switch (message.action) { case 'uploading': setUploadingImage(true); clearUploadError(); break; case 'error': setUploadingImage(false); setUploadError(true); setUploadErrorMessage(message.error); break; case 'success': onMainImageUrlChange({ links: [message.link], }); setUploadingImage(false); break; } /* eslint-enable no-case-declarations */ }; const triggerMainImageRemoval = (e) => { e.preventDefault(); onMainImageUrlChange({ links: [null], }); }; const onDropImage = (event) => { onDragExit(event); if (event.dataTransfer.files.length > 1) { addSnackbarItem({ message: 'Only one image can be dropped at a time.', addCloseButton: true, }); return; } handleMainImageUpload(event); }; const uploadLabel = mainImage ? 'Change' : 'Add a cover image'; // When the component is rendered in an environment that supports a native // image picker for image upload we want to add the aria-label attr and the // onClick event to the UI button. This event will kick off the native UX. // The props are unwrapped (using spread operator) in the button below const extraProps = useNativeUpload() ? { onClick: initNativeImagePicker, 'aria-label': 'Upload cover image', } : {}; // Native Bridge messages come through ForemMobile events document.addEventListener('ForemMobile', handleNativeMessage); return ( {!uploadingImage && mainImage && ( )} {uploadingImage && ( Uploading... )} {useNativeUpload() ? ( ) : ( )} {mainImage && !uploadingImage && ( Remove )} {uploadError && ( {uploadErrorMessage} )} ); }; ArticleCoverImage.propTypes = { mainImage: PropTypes.string, onMainImageUrlChange: PropTypes.func.isRequired, }; ArticleCoverImage.displayName = 'ArticleCoverImage';
{uploadErrorMessage}