@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
292 lines (290 loc) • 9.52 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 React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { getEditFormSrc } from '../../utils/path';
import { useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { fromEvent } from 'rxjs';
import { filter } from 'rxjs/operators';
import {
EMBEDDED_LEGACY_CHANGE_TO_EDIT_MODE,
EMBEDDED_LEGACY_FORM_CLOSE,
EMBEDDED_LEGACY_FORM_DISABLE_HEADER,
EMBEDDED_LEGACY_FORM_DISABLE_ON_CLOSE,
EMBEDDED_LEGACY_FORM_ENABLE_HEADER,
EMBEDDED_LEGACY_FORM_ENABLE_ON_CLOSE,
EMBEDDED_LEGACY_FORM_FAILURE,
EMBEDDED_LEGACY_FORM_PENDING_CHANGES,
EMBEDDED_LEGACY_FORM_RENDER_FAILED,
EMBEDDED_LEGACY_FORM_RENDERED,
EMBEDDED_LEGACY_FORM_SAVE,
EMBEDDED_LEGACY_FORM_SAVE_END,
EMBEDDED_LEGACY_FORM_SAVE_START,
EMBEDDED_LEGACY_FORM_SUCCESS,
EMBEDDED_LEGACY_MINIMIZE_REQUEST,
reloadRequest
} from '../../state/actions/preview';
import { getHostToGuestBus } from '../../utils/subjects';
import { updateEditDialogConfig } from '../../state/actions/dialogs';
import { showErrorDialog } from '../../state/reducers/dialogs/error';
import { useUnmount } from '../../hooks/useUnmount';
import LoadingState from '../LoadingState/LoadingState';
import ErrorDialog from '../ErrorDialog/ErrorDialog';
import { translations } from './translations';
import { useStyles } from './styles';
import { hasEditAction } from '../../utils/content';
import { nnou } from '../../utils/object';
import { useFetchItem } from '../../hooks/useFetchItem';
export const EmbeddedLegacyContainer = React.forwardRef(function EmbeddedLegacyEditor(props, ref) {
const {
path,
selectedFields,
authoringBase,
readonly,
site,
isHidden,
modelId,
contentTypeId,
isNewContent,
changeTemplate,
inProgress,
onSaveSuccess,
onMinimize,
onClose,
onClosed,
iceGroupId,
newEmbedded,
index,
setIframeLoaded
} = props;
const { formatMessage } = useIntl();
const { classes, cx: clsx } = useStyles();
const iframeRef = useRef(null);
const dispatch = useDispatch();
const [error, setError] = useState(null);
// When filename, path prop will still be the previous one, and useDetailedItem will try to re-fetch the
// non-existing item (old filename path), so we will only re-fetch when the actual path prop of the component
// changes (useDetailedItemNoState).
const item = useFetchItem(path);
const availableActions = item?.availableActions;
let fieldsIndexes;
if (selectedFields && index) {
fieldsIndexes = {};
selectedFields.forEach((id) => {
fieldsIndexes[id] = index;
});
}
const src = useMemo(
() =>
getEditFormSrc({
path,
site,
authoringBase,
readonly,
isHidden,
modelId,
changeTemplate,
contentTypeId,
isNewContent,
iceGroupId,
...(nnou(availableActions) && !isNewContent ? { canEdit: hasEditAction(availableActions) } : {}),
...(selectedFields && selectedFields.length ? { selectedFields: JSON.stringify(selectedFields) } : {}),
...(newEmbedded ? { newEmbedded: JSON.stringify(newEmbedded) } : {}),
...(fieldsIndexes ? { fieldsIndexes: JSON.stringify(fieldsIndexes) } : {})
}),
[
path,
site,
authoringBase,
readonly,
isHidden,
modelId,
changeTemplate,
contentTypeId,
isNewContent,
iceGroupId,
selectedFields,
newEmbedded,
availableActions,
fieldsIndexes
]
);
const messages = fromEvent(window, 'message').pipe(filter((e) => e.data && e.data.type));
const onErrorClose = () => {
setError(null);
onClose();
};
const onSave = useCallback(
(data) => {
onSaveSuccess?.(data);
},
[onSaveSuccess]
);
useEffect(() => {
const messagesSubscription = messages.subscribe((e) => {
switch (e.data.type) {
case EMBEDDED_LEGACY_FORM_SUCCESS: {
onSave(e.data);
getHostToGuestBus().next({ type: reloadRequest.type });
dispatch(updateEditDialogConfig({ pendingChanges: false }));
switch (e.data.action) {
case 'save': {
break;
}
case 'saveAndMinimize': {
onMinimize();
break;
}
case 'saveAndPreview':
case 'saveAndClose': {
onClose();
break;
}
}
break;
}
case EMBEDDED_LEGACY_FORM_CLOSE: {
if (e.data.close) {
onClose();
}
if (e.data.refresh) {
getHostToGuestBus().next({ type: reloadRequest.type });
}
break;
}
case EMBEDDED_LEGACY_FORM_RENDERED: {
setIframeLoaded(true);
if (inProgress) {
dispatch(updateEditDialogConfig({ inProgress: false }));
}
break;
}
case EMBEDDED_LEGACY_FORM_ENABLE_ON_CLOSE: {
dispatch(updateEditDialogConfig({ isSubmitting: false }));
break;
}
case EMBEDDED_LEGACY_FORM_DISABLE_ON_CLOSE: {
dispatch(updateEditDialogConfig({ isSubmitting: true }));
break;
}
case EMBEDDED_LEGACY_FORM_ENABLE_HEADER: {
dispatch(updateEditDialogConfig({ disableHeader: false }));
break;
}
case EMBEDDED_LEGACY_FORM_DISABLE_HEADER: {
dispatch(updateEditDialogConfig({ disableHeader: true }));
break;
}
case EMBEDDED_LEGACY_FORM_RENDER_FAILED: {
onClose();
dispatch(showErrorDialog({ error: { message: formatMessage(translations.error) } }));
break;
}
case EMBEDDED_LEGACY_FORM_SAVE: {
onSave(e.data);
dispatch(updateEditDialogConfig({ pendingChanges: false }));
if (e.data.refresh) {
getHostToGuestBus().next({ type: reloadRequest.type });
}
switch (e.data.action) {
case 'save': {
break;
}
case 'saveAndMinimize': {
onMinimize();
break;
}
case 'saveAndClose':
case 'saveAndPreview': {
onClose();
break;
}
}
break;
}
case EMBEDDED_LEGACY_FORM_FAILURE: {
setError({
message: e.data.message
});
break;
}
case EMBEDDED_LEGACY_FORM_PENDING_CHANGES: {
dispatch(updateEditDialogConfig({ pendingChanges: true }));
break;
}
case EMBEDDED_LEGACY_MINIMIZE_REQUEST: {
onMinimize();
break;
}
case EMBEDDED_LEGACY_CHANGE_TO_EDIT_MODE: {
dispatch(updateEditDialogConfig({ readonly: false }));
break;
}
case EMBEDDED_LEGACY_FORM_SAVE_START: {
dispatch(updateEditDialogConfig({ isSubmitting: true }));
break;
}
case EMBEDDED_LEGACY_FORM_SAVE_END: {
dispatch(updateEditDialogConfig({ isSubmitting: false }));
break;
}
}
});
return () => {
messagesSubscription.unsubscribe();
};
}, [inProgress, onSave, messages, dispatch, onClose, formatMessage, onMinimize, setIframeLoaded]);
useUnmount(onClosed);
return React.createElement(
React.Fragment,
null,
(inProgress || !item) &&
!isNewContent &&
React.createElement(LoadingState, {
title: formatMessage(translations.loadingForm),
classes: { root: classes.loadingRoot }
}),
(item || isNewContent) &&
React.createElement('iframe', {
ref: (e) => {
iframeRef.current = e;
if (ref) {
typeof ref === 'function' ? ref(e) : (ref.current = e);
}
},
src: src,
title: 'Embedded Legacy Form',
className: clsx(classes.iframe, !inProgress && 'complete')
}),
React.createElement(ErrorDialog, { open: Boolean(error), error: error, onDismiss: onErrorClose })
);
});
export default EmbeddedLegacyContainer;