UNPKG

@craftercms/studio-ui

Version:

Services, components, models & utils to build CrafterCMS authoring extensions.

212 lines (210 loc) 8.54 kB
/* * 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;