import React, { useEffect, useState, useReducer, useRef } from 'react'; import PropTypes from 'prop-types'; import { get, isEmpty } from 'lodash'; import { Modal, ModalFooter, PopUpWarning, useGlobalContext, request } from 'strapi-helper-plugin'; import { Button } from '@buffetjs/core'; import pluginId from '../../pluginId'; import { getTrad } from '../../utils'; import ModalHeader from '../../components/ModalHeader'; import stepper from './stepper'; import init from './init'; import reducer, { initialState } from './reducer'; const ModalStepper = ({ initialFileToEdit, initialStep, isOpen, onClosed, onDeleteMedia, onToggle, }) => { const { formatMessage } = useGlobalContext(); const [isWarningDeleteOpen, setIsWarningDeleteOpen] = useState(false); const [shouldDeleteFile, setShouldDeleteFile] = useState(false); const [isFormDisabled, setIsFormDisabled] = useState(false); const [displayNextButton, setDisplayNextButton] = useState(false); const [reducerState, dispatch] = useReducer(reducer, initialState, init); const { currentStep, fileToEdit, filesToDownload, filesToUpload } = reducerState.toJS(); const { Component, components, headerBreadcrumbs, next, prev, withBackButton } = stepper[ currentStep ]; const filesToUploadLength = filesToUpload.length; const toggleRef = useRef(onToggle); const editModalRef = useRef(); useEffect(() => { // if (currentStep === 'upload' && filesToUploadLength === 0) { // // Passing true to the onToggle prop will refetch the data when the modal closes // toggleRef.current(true); // } }, [filesToUploadLength, currentStep]); useEffect(() => { if (isOpen) { goTo(initialStep); if (initialFileToEdit) { dispatch({ type: 'INIT_FILE_TO_EDIT', fileToEdit: initialFileToEdit, }); } } // Disabling the rule because we just want to let the ability to open the modal // at a specific step then we will let the stepper handle the navigation // eslint-disable-next-line react-hooks/exhaustive-deps }, [isOpen]); const addFilesToUpload = ({ target: { value } }) => { dispatch({ type: 'ADD_FILES_TO_UPLOAD', filesToUpload: value, }); goTo(next); }; const handleAbortUpload = () => { const { abortController } = fileToEdit; abortController.abort(); dispatch({ type: 'ON_ABORT_UPLOAD', }); }; const handleCancelFileToUpload = fileOriginalIndex => { const fileToCancel = filesToUpload.find(file => file.originalIndex === fileOriginalIndex); // Cancel upload fileToCancel.abortController.abort(); dispatch({ type: 'REMOVE_FILE_TO_UPLOAD', fileIndex: fileOriginalIndex, }); }; const handleChange = ({ target: { name, value } }) => { let val = value; let type = 'ON_CHANGE'; if (name === 'url') { val = value.split('\n'); type = 'ON_CHANGE_URLS_TO_DOWNLOAD'; } dispatch({ type, keys: name, value: val, }); }; const handleConfirmDeleteFile = () => { setShouldDeleteFile(true); toggleModalWarning(); }; const handleClickNextButton = () => { // Navigate to next step // goNext(); // validate the form }; const handleClickDeleteFile = async () => { toggleModalWarning(); }; const handleClickDeleteFileToUpload = fileIndex => { dispatch({ type: 'REMOVE_FILE_TO_UPLOAD', fileIndex, }); if (currentStep === 'edit-new') { dispatch({ type: 'RESET_FILE_TO_EDIT', }); goNext(); } }; const handleClose = () => { onClosed(); setIsFormDisabled(false); setDisplayNextButton(false); dispatch({ type: 'RESET_PROPS', }); }; const handleCloseModalWarning = async () => { if (shouldDeleteFile) { const { id } = fileToEdit; onDeleteMedia(id); } }; const handleGoToEditNewFile = fileIndex => { dispatch({ type: 'SET_FILE_TO_EDIT', fileIndex, }); goTo('edit-new'); }; const handleGoToAddBrowseFiles = () => { dispatch({ type: 'CLEAN_FILES_ERROR', }); goBack(); }; const handleSetCropResult = blob => { dispatch({ type: 'SET_CROP_RESULT', blob, }); }; const handleSubmitEditNewFile = e => { e.preventDefault(); dispatch({ type: 'ON_SUBMIT_EDIT_NEW_FILE', }); goNext(); }; const handleSubmitEditExistingFile = async ( e, shouldDuplicateMedia = false, file = fileToEdit.file ) => { e.preventDefault(); dispatch({ type: 'ON_SUBMIT_EDIT_EXISTING_FILE', }); const headers = {}; const formData = new FormData(); // If the file has been cropped we need to add it to the formData // otherwise we just don't send it const didCropFile = file instanceof File; const { abortController, id, fileInfo } = fileToEdit; const requestURL = shouldDuplicateMedia ? `/${pluginId}` : `/${pluginId}?id=${id}`; if (didCropFile) { formData.append('files', file); } formData.append('fileInfo', JSON.stringify(fileInfo)); try { await request( requestURL, { method: 'POST', headers, body: formData, signal: abortController.signal, }, false, false ); // Close the modal and refetch data toggleRef.current(true); } catch (err) { console.log(err); } }; const handleReplaceMedia = () => { editModalRef.current.click(); }; const handleToggle = () => { if (filesToUploadLength > 0) { // eslint-disable-next-line no-alert const confirm = window.confirm(formatMessage({ id: getTrad('window.confirm.close-modal') })); if (!confirm) { return; } } onToggle(); }; const handleUploadFiles = async () => { dispatch({ type: 'SET_FILES_UPLOADING_STATE', }); const requests = filesToUpload.map( async ({ file, fileInfo, originalIndex, abortController }) => { const formData = new FormData(); const headers = {}; formData.append('files', file); formData.append('fileInfo', JSON.stringify(fileInfo)); try { await request( `/${pluginId}`, { method: 'POST', headers, body: formData, signal: abortController.signal, }, false, false ); dispatch({ type: 'REMOVE_FILE_TO_UPLOAD', fileIndex: originalIndex, }); } catch (err) { const errorMessage = get( err, ['response', 'payload', 'message', '0', 'messages', '0', 'message'], get(err, ['response', 'payload', 'message'], null) ); if (errorMessage) { dispatch({ type: 'SET_FILE_ERROR', fileIndex: originalIndex, errorMessage, }); } } } ); await Promise.all(requests); }; const goBack = () => { goTo(prev); }; // FIXME: when back button needed // eslint-disable-next-line no-unused-vars const goNext = () => { if (next === null) { onToggle(); return; } goTo(next); }; const goTo = to => { dispatch({ type: 'GO_TO', to, }); }; const toggleModalWarning = () => { setIsWarningDeleteOpen(prev => !prev); }; const shouldDisplayNextButton = currentStep === 'browse' && displayNextButton; return ( <> {/* header title */} {/* body of the modal */} {Component && ( )}
{shouldDisplayNextButton && ( )} {currentStep === 'upload' && ( )} {currentStep === 'edit-new' && ( )} {currentStep === 'edit' && (
)}
); }; ModalStepper.defaultProps = { initialFileToEdit: null, initialStep: 'browse', onClosed: () => {}, onDeleteMedia: () => {}, onToggle: () => {}, }; ModalStepper.propTypes = { initialFileToEdit: PropTypes.object, initialStep: PropTypes.string, isOpen: PropTypes.bool.isRequired, onClosed: PropTypes.func, onDeleteMedia: PropTypes.func, onToggle: PropTypes.func, }; export default ModalStepper;