@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
212 lines (210 loc) • 8.54 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, { useEffect } from 'react';
import DialogBody from '../../DialogBody/DialogBody';
import { EnhancedDialog } from '../../EnhancedDialog';
import PublishOnDemandForm from '../../PublishOnDemandForm';
import SecondaryButton from '../../SecondaryButton';
import { FormattedMessage } from 'react-intl';
import PrimaryButton from '../../PrimaryButton';
import DialogFooter from '../../DialogFooter';
import { fetchPublishingTargets, publishByCommits } from '../../../services/publishing';
import useSpreadState from '../../../hooks/useSpreadState';
import useSelection from '../../../hooks/useSelection';
import useActiveSiteId from '../../../hooks/useActiveSiteId';
import { showSystemNotification } from '../../../state/actions/system';
import { useDispatch } from 'react-redux';
import CheckCircleOutlineRoundedIcon from '@mui/icons-material/CheckCircleOutlineRounded';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
import { isBlank } from '../../../utils/string';
import useUpdateRefs from '../../../hooks/useUpdateRefs';
import useWithPendingChangesCloseRequest from '../../../hooks/useWithPendingChangesCloseRequest';
export function PublishCommitDialog(props) {
const { commitId, onSubmittingAndOrPendingChange, ...dialogProps } = props;
const site = useActiveSiteId();
const dispatch = useDispatch();
const initialState = {
publishSuccessful: false,
loadingPublishingTargets: false,
isSubmitting: false,
publishingTargets: null,
path: '',
commitIds: '',
comment: '',
publishingTarget: ''
};
const [state, setState] = useSpreadState(initialState);
const { loadingPublishingTargets, isSubmitting, publishingTargets, publishSuccessful, ...data } = state;
const { publishByCommitCommentRequired } = useSelection((state) => state.uiConfig.publishing);
const isInvalid = (publishByCommitCommentRequired && isBlank(data.comment)) || isBlank(data.commitIds);
const open = Boolean(dialogProps?.open);
const pendingChangesCloseRequest = useWithPendingChangesCloseRequest(dialogProps.onClose);
const fnRefs = useUpdateRefs({ onSubmittingAndOrPendingChange });
const onCancel = (e) =>
dialogProps.hasPendingChanges ? pendingChangesCloseRequest(e, null) : dialogProps.onClose(e, null);
const onPublish = () => {
if (!isInvalid) {
setState({ isSubmitting: true });
publishByCommits(
site,
data.commitIds.replace(/\s/g, '').split(',').filter(Boolean),
data.publishingTarget,
data.comment
).subscribe({
next() {
setState({ isSubmitting: false, publishSuccessful: true });
fnRefs.current.onSubmittingAndOrPendingChange({ hasPendingChanges: false });
},
error({ response }) {
setState({ isSubmitting: false });
dispatch(
showSystemNotification({
message: response.message,
options: { variant: 'error' }
})
);
}
});
}
};
useEffect(() => {
if (open) {
setState({ loadingPublishingTargets: true });
const sub = fetchPublishingTargets(site).subscribe({
next({ publishingTargets }) {
const newData = { publishingTargets, loadingPublishingTargets: false };
// Set pre-selected publishing target.
if (publishingTargets.length > 0) {
const stagingEnv = publishingTargets.find((target) => target.name === 'staging');
newData.publishingTarget = stagingEnv?.name ?? publishingTargets[0].name;
}
setState(newData);
}
});
return () => {
sub.unsubscribe();
};
}
}, [setState, site, open]);
useEffect(() => {
// Since the form will have a commitId from the beginning, the 'hasPendingChanges' flag will be true.
fnRefs.current.onSubmittingAndOrPendingChange({ hasPendingChanges: true });
setState({ commitIds: commitId });
}, [commitId, setState, fnRefs]);
return React.createElement(
EnhancedDialog,
{
...dialogProps,
onWithPendingChangesCloseRequest: pendingChangesCloseRequest,
onClosed: () => {
setState({ ...initialState, publishingTarget: state.publishingTarget, publishingTargets: publishingTargets });
},
isSubmitting: isSubmitting,
title: React.createElement(FormattedMessage, {
id: 'publishCommitDialog.title',
defaultMessage: 'Publish Commit'
})
},
publishSuccessful
? React.createElement(
React.Fragment,
null,
React.createElement(
DialogBody,
null,
React.createElement(
Box,
{ display: 'flex', flexDirection: 'column', alignItems: 'center', margin: 2 },
React.createElement(CheckCircleOutlineRoundedIcon, {
sx: { mb: 1, color: 'success.main', width: 50, height: 50 }
}),
React.createElement(
Typography,
null,
React.createElement(FormattedMessage, {
id: 'publishCommitDialog.successMessage',
defaultMessage: 'Publish completed successfully'
})
)
)
),
React.createElement(
DialogFooter,
null,
React.createElement(
SecondaryButton,
{ onClick: onCancel },
React.createElement(FormattedMessage, { id: 'words.done', defaultMessage: 'Done' })
)
)
)
: React.createElement(
React.Fragment,
null,
React.createElement(
DialogBody,
null,
React.createElement(PublishOnDemandForm, {
mode: 'git',
formData: data,
setFormData: (newData) => {
setState(newData);
const mergedData = { ...data, ...newData };
fnRefs.current.onSubmittingAndOrPendingChange({
hasPendingChanges: !isBlank(mergedData.comment) || !isBlank(mergedData.commitIds)
});
},
publishingTargets: state.publishingTargets,
publishingTargetsError: null,
bulkPublishCommentRequired: false,
publishByCommitCommentRequired: publishByCommitCommentRequired,
disabled: !state.publishingTargets || isSubmitting
})
),
React.createElement(
DialogFooter,
null,
React.createElement(
SecondaryButton,
{ onClick: onCancel, disabled: isSubmitting },
React.createElement(FormattedMessage, { id: 'words.cancel', defaultMessage: 'Cancel' })
),
React.createElement(
PrimaryButton,
{ onClick: onPublish, disabled: isSubmitting || loadingPublishingTargets || isInvalid },
React.createElement(FormattedMessage, { id: 'words.publish', defaultMessage: 'Publish' })
)
)
)
);
}
export default PublishCommitDialog;