@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
210 lines (208 loc) • 6.93 kB
JavaScript
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { getResponseError as getResponseErrorUtil } from './util';
import { useIntl } from 'react-intl';
import { useSelection } from '../../hooks/useSelection';
import React, { useEffect } from 'react';
import { Uppy } from '@uppy/core';
import { translations } from './translations';
import { XHRUpload } from '@craftercms/uppy';
import { getBulkUploadUrl } from '../../services/content';
import { getGlobalHeaders } from '../../utils/ajax';
import { useUnmount } from '../../hooks/useUnmount';
import { Button, IconButton } from '@mui/material';
import CloseIconRounded from '@mui/icons-material/CloseRounded';
import DialogBody from '../DialogBody/DialogBody';
import UppyDashboard from '../UppyDashboard';
import { makeStyles } from 'tss-react/mui';
import useSiteUIConfig from '../../hooks/useSiteUIConfig';
import useUpdateRefs from '../../hooks/useUpdateRefs';
const useStyles = makeStyles()(() => ({
rootTitle: {
paddingBottom: 0,
display: 'none'
},
subtitleWrapper: {
paddingBottom: 0,
display: 'flex',
alignItems: 'center',
width: '100%',
justifyContent: 'space-between'
},
dialogBody: {
minHeight: '60vh',
padding: 0
}
}));
const mixHeaders = (headers) => Object.assign({}, getGlobalHeaders(), headers);
export function UploadDialogContainer(props) {
const { formatMessage } = useIntl();
const expiresAt = useSelection((state) => state.auth.expiresAt);
const { upload } = useSiteUIConfig();
const { classes } = useStyles();
// region const { ... } = props
const {
site,
path,
onClose,
onClosed,
maxSimultaneousUploads,
onMinimized,
hasPendingChanges,
setPendingChanges,
headers,
method = 'post',
meta,
allowedMetaFields,
endpoint,
useFormData = true,
fieldName = 'file',
onFileAdded,
onUploadSuccess,
validateStatus,
getResponseData,
getResponseError,
successfulUploadButton,
showRemoveButtonAfterComplete = false,
autoProceed = true
} = props;
// endregion
const propRefs = useUpdateRefs({
headers,
meta,
allowedMetaFields,
onFileAdded,
onUploadSuccess,
validateStatus,
getResponseData,
getResponseError
});
// TODO: Currently unknown if recreating the Uppy instance works properly down the component tree.
const uppy = React.useMemo(() => {
// Want to avoid memo renewal on every render due to these various props not being memoized up in the tree.
const {
headers,
allowedMetaFields,
validateStatus,
getResponseData,
getResponseError,
onFileAdded,
onUploadSuccess,
meta
} = propRefs.current;
const xhrOptions = {
endpoint: endpoint ?? getBulkUploadUrl(site, path),
formData: useFormData,
fieldName,
limit: maxSimultaneousUploads ? maxSimultaneousUploads : upload.maxSimultaneousUploads,
timeout: upload.timeout,
headers: mixHeaders(headers),
method,
getResponseError: (responseText) => getResponseErrorUtil(responseText, formatMessage)
};
allowedMetaFields && (xhrOptions.allowedMetaFields = allowedMetaFields);
// These (validateStatus, getResponseData, getResponseError) are unlikely to have closures inside them that would go stale.
validateStatus && (xhrOptions.validateStatus = validateStatus);
getResponseData && (xhrOptions.getResponseData = getResponseData);
getResponseError && (xhrOptions.getResponseError = getResponseError);
const instance = new Uppy({
meta: Object.assign({ site }, meta),
locale: { strings: { noDuplicates: formatMessage(translations.noDuplicates) } }
}).use(XHRUpload, xhrOptions);
onFileAdded &&
instance.on('file-added', (file) => {
propRefs.current.onFileAdded({ file, uppy: instance });
});
onUploadSuccess &&
instance.on('upload-success', (file, response) => {
propRefs.current.onUploadSuccess({ file, response });
});
return instance;
}, [
propRefs,
endpoint,
site,
path,
useFormData,
fieldName,
maxSimultaneousUploads,
upload.maxSimultaneousUploads,
upload.timeout,
method,
formatMessage
]);
useUnmount(() => {
uppy.close();
onClosed?.();
});
useEffect(() => {
const handleBeforeUpload = () => {
return formatMessage(translations.uploadInProgress);
};
if (hasPendingChanges) {
window.onbeforeunload = handleBeforeUpload;
} else {
window.onbeforeunload = null;
}
return () => {
window.onbeforeunload = null;
};
}, [hasPendingChanges, formatMessage]);
useEffect(() => {
const plugin = uppy.getPlugin('XHRUpload');
plugin.setOptions({ headers: mixHeaders(headers) });
}, [expiresAt, uppy, headers]);
return React.createElement(
React.Fragment,
null,
React.createElement(Button, { style: { display: 'none' } }, 'test'),
React.createElement(
IconButton,
{ style: { display: 'none' }, size: 'large' },
React.createElement(CloseIconRounded, null)
),
React.createElement(
DialogBody,
{ className: classes.dialogBody },
React.createElement(UppyDashboard, {
uppy: uppy,
site: site,
path: path,
onMinimized: onMinimized,
onPendingChanges: setPendingChanges,
onClose: onClose,
title: formatMessage(translations.title),
maxActiveUploads: upload.maxActiveUploads,
options: { successfulUploadButton, showRemoveButtonAfterComplete, autoProceed }
})
)
);
}