import React, { useEffect, useState, useReducer, useRef } from 'react'; import PropTypes from 'prop-types'; import { get } from 'lodash'; import { Modal, ModalFooter, PopUpWarning, useGlobalContext, request } from 'strapi-helper-plugin'; import { Button } from '@buffetjs/core'; import pluginId from '../../pluginId'; import { getRequestUrl, 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, onToggle }) => { const { formatMessage } = useGlobalContext(); const [isWarningDeleteOpen, setIsWarningDeleteOpen] = useState(false); const [shouldDeleteFile, setShouldDeleteFile] = useState(false); const [reducerState, dispatch] = useReducer(reducer, initialState, init); const { currentStep, fileToEdit, filesToUpload } = reducerState.toJS(); const { Component, 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 handleCancelFileToUpload = fileIndex => { const fileToCancel = get(filesToUpload, fileIndex, {}); // Cancel upload fileToCancel.abortController.abort(); dispatch({ type: 'REMOVE_FILE_TO_UPLOAD', fileIndex, }); }; const handleChange = ({ target: { name, value } }) => { dispatch({ type: 'ON_CHANGE', keys: name, value, }); }; const handleConfirmDeleteFile = () => { toggleModalWarning(); setShouldDeleteFile(true); // const { id } = fileToEdit; // try { // const requestURL = getRequestUrl(`files/${id}`); // await request(requestURL, { method: 'DELETE' }); // toggleRef.current(true); // } catch (err) { // console.log(err); // } }; 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(); dispatch({ type: 'RESET_PROPS', }); }; const handleCloseModalWarning = async () => { if (shouldDeleteFile) { const { id } = fileToEdit; try { const requestURL = getRequestUrl(`files/${id}`); await request(requestURL, { method: 'DELETE' }); setShouldDeleteFile(false); toggleRef.current(true); } catch (err) { console.log(err); } } }; 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) => { e.preventDefault(); dispatch({ type: 'ON_SUBMIT_EDIT_EXISTING_FILE', }); const headers = {}; const formData = new FormData(); // The endpoints are different when we want to just update the file infos const didCropFile = fileToEdit.file instanceof File; const { abortController, id, file, fileInfo } = fileToEdit; let 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); } } else { requestURL = `/${pluginId}/files/${id}`; // The following will not work waiting for the back-end to be ready try { await request(requestURL, { method: 'PUT', body: fileInfo }); // Do something } catch (err) { // Do something with error 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'], null ); 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); }; return ( <> {/* header title */} {/* body of the modal */} {Component && ( )} {formatMessage({ id: 'app.components.Button.cancel' })} {currentStep === 'upload' && ( {formatMessage( { id: getTrad( `modal.upload-list.footer.button.${ filesToUploadLength > 1 ? 'plural' : 'singular' }` ), }, { number: filesToUploadLength } )} )} {currentStep === 'edit-new' && ( {formatMessage({ id: 'form.button.finish' })} )} {currentStep === 'edit' && ( Replace media {formatMessage({ id: 'form.button.finish' })} )} > ); }; ModalStepper.defaultProps = { initialFileToEdit: null, initialStep: 'browse', onClosed: () => {}, onToggle: () => {}, }; ModalStepper.propTypes = { initialFileToEdit: PropTypes.object, initialStep: PropTypes.string, isOpen: PropTypes.bool.isRequired, onClosed: PropTypes.func, onToggle: PropTypes.func, }; export default ModalStepper;