/* global Runtime */ import { Fragment, h } from 'preact'; import { useReducer } from 'preact/hooks'; import { generateMainImage } from '../actions'; import { validateFileInputs } from '../../packs/validateFileInputs'; import { addSnackbarItem } from '../../Snackbar'; import { ClipboardButton } from './ClipboardButton'; import { Button, Spinner } from '@crayons'; const ImageIcon = () => ( ); ImageIcon.displayName = 'ImageIcon'; function imageUploaderReducer(state, action) { const { type, payload } = action; switch (type) { case 'uploading_image': return { ...state, uploadError: false, uploadingErrorMessage: null, uploadingImage: true, insertionImageUrls: [], showImageCopiedMessage: false, }; case 'upload_error': return { ...state, insertionImageUrls: [], uploadError: true, uploadErrorMessage: payload.errorMessage, uploadingImage: false, }; case 'show_copied_image_message': return { ...state, showImageCopiedMessage: true, }; case 'upload_image_success': return { ...state, insertionImageUrls: payload.insertionImageUrls, uploadingImage: false, }; default: return state; } } const NativeIosImageUpload = ({ uploadingImage, extraProps, handleNativeMessage, }) => ( {!uploadingImage && ( )} ); const StandardImageUpload = ({ handleInsertionImageUpload, uploadingImage }) => uploadingImage ? null : ( ); export const ImageUploader = () => { const [state, dispatch] = useReducer(imageUploaderReducer, { insertionImageUrls: [], uploadError: false, uploadErrorMessage: null, showImageCopiedMessage: false, uploadingImage: false, }); const { uploadingImage, showImageCopiedMessage, uploadErrorMessage, uploadError, insertionImageUrls, } = state; let imageMarkdownInput = null; function onUploadError(error) { dispatch({ type: 'upload_error', payload: { errorMessage: error.message }, }); } function copyText() { imageMarkdownInput = document.getElementById( 'image-markdown-copy-link-input', ); Runtime.copyToClipboard(imageMarkdownInput.value) .then(() => { dispatch({ type: 'show_copied_image_message', }); }) .catch((error) => { addSnackbarItem({ message: error, addCloseButton: true, }); Honeybadger.notify(error); }); } function handleInsertionImageUpload(e) { const { files } = e.target; if (files.length > 0 && validateFileInputs()) { const payload = { image: files }; dispatch({ type: 'uploading_image', }); generateMainImage(payload, handleInsertImageUploadSuccess, onUploadError); } } function handleInsertImageUploadSuccess(response) { dispatch({ type: 'upload_image_success', payload: { insertionImageUrls: response.links }, }); } function handleNativeMessage(e) { const message = JSON.parse(e.target.value); switch (message.action) { case 'uploading': dispatch({ type: 'uploading_image', }); break; case 'error': dispatch({ type: 'upload_error', payload: { errorMessage: message.error }, }); break; case 'success': dispatch({ type: 'upload_image_success', payload: { insertionImageUrls: [message.link] }, }); break; } } function initNativeImagePicker(e) { e.preventDefault(); window.webkit.messageHandlers.imageUpload.postMessage({ id: 'native-image-upload-message', }); } // 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 useNativeUpload = Runtime.isNativeIOS('imageUpload'); const extraProps = useNativeUpload ? { onClick: initNativeImagePicker, 'aria-label': 'Upload an image' } : { tabIndex: -1 }; return (
{uploadingImage && ( Uploading... )} {useNativeUpload ? ( ) : ( )} {insertionImageUrls.length > 0 && ( )} {uploadError && ( {uploadErrorMessage} )}
); }; ImageUploader.displayName = 'ImageUploader';