Skip to content

Commit 3fb3ef5

Browse files
authored
Merge pull request #3909 from mhsdesign/feature/simplify-publish-workflow
FEATURE: Simplify publish workflow
2 parents da19324 + 6189127 commit 3fb3ef5

File tree

9 files changed

+114
-59
lines changed

9 files changed

+114
-59
lines changed

Tests/IntegrationTests/Fixtures/1Dimension/syncing.e2e.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ test('Publish + Syncing: Create a conflict state between two editors, then try t
8989

9090
test('Publish + Syncing: Create a conflict state between two editors, then try to publish the document and choose "Drop conflicting changes" as a resolution strategy during automatic rebase', async t => {
9191
await prepareDocumentConflictBetweenAdminAndEditor(t);
92-
await startPublishDocument(t);
92+
await publishDocument(t);
9393
await assertThatConflictResolutionHasStarted(t);
9494
await chooseDropConflictingChangesAsResolutionStrategy(t);
9595
await performResolutionStrategy(t);
@@ -323,9 +323,8 @@ async function startPublishAll(t) {
323323
await t.click(Selector('#neos-PublishDialog-Confirm'));
324324
}
325325

326-
async function startPublishDocument(t) {
326+
async function publishDocument(t) {
327327
await t.click(Selector('#neos-PublishDropDown-Publish'))
328-
await t.click(Selector('#neos-PublishDialog-Confirm'));
329328
}
330329

331330
async function finishPublish(t) {

packages/neos-ui-redux-store/src/CR/Publishing/index.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ export type State = null | {
3535
scope: PublishingScope;
3636
process:
3737
| { phase: PublishingPhase.START }
38-
| { phase: PublishingPhase.ONGOING }
38+
| {
39+
phase: PublishingPhase.ONGOING,
40+
autoConfirmed: boolean
41+
}
3942
| { phase: PublishingPhase.CONFLICTS }
4043
| {
4144
phase: PublishingPhase.ERROR;
@@ -65,8 +68,8 @@ export enum actionTypes {
6568
/**
6669
* Publishes or discards all changes in the given scope
6770
*/
68-
const start = (mode: PublishingMode, scope: PublishingScope) =>
69-
createAction(actionTypes.STARTED, {mode, scope});
71+
const start = (mode: PublishingMode, scope: PublishingScope, requireConfirmation: boolean) =>
72+
createAction(actionTypes.STARTED, {mode, scope, requireConfirmation});
7073

7174
/**
7275
* Cancel the ongoing publish/discard workflow
@@ -142,8 +145,11 @@ export const reducer = (state: State = defaultState, action: Action): State => {
142145
return {
143146
mode: action.payload.mode,
144147
scope: action.payload.scope,
145-
process: {
148+
process: action.payload.requireConfirmation ? {
146149
phase: PublishingPhase.START
150+
} : {
151+
phase: PublishingPhase.ONGOING,
152+
autoConfirmed: true
147153
}
148154
};
149155
}
@@ -166,7 +172,8 @@ export const reducer = (state: State = defaultState, action: Action): State => {
166172
return {
167173
...state,
168174
process: {
169-
phase: PublishingPhase.ONGOING
175+
phase: PublishingPhase.ONGOING,
176+
autoConfirmed: false
170177
}
171178
};
172179
case actionTypes.CONFLICTS_OCCURRED:
@@ -180,7 +187,8 @@ export const reducer = (state: State = defaultState, action: Action): State => {
180187
return {
181188
...state,
182189
process: {
183-
phase: PublishingPhase.ONGOING
190+
phase: PublishingPhase.ONGOING,
191+
autoConfirmed: false
184192
}
185193
};
186194
case actionTypes.FAILED:
@@ -195,7 +203,8 @@ export const reducer = (state: State = defaultState, action: Action): State => {
195203
return {
196204
...state,
197205
process: {
198-
phase: PublishingPhase.ONGOING
206+
phase: PublishingPhase.ONGOING,
207+
autoConfirmed: false
199208
}
200209
};
201210
case actionTypes.SUCEEDED:

packages/neos-ui-sagas/src/Publish/index.ts

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*/
1010
import {put, call, select, takeEvery, take, race, all} from 'redux-saga/effects';
1111

12-
import {AnyError} from '@neos-project/neos-ui-error';
12+
import {AnyError, showFlashMessage} from '@neos-project/neos-ui-error';
1313
import {DimensionCombination, NodeContextPath, WorkspaceName} from '@neos-project/neos-ts-interfaces';
1414
import {actionTypes, actions, selectors} from '@neos-project/neos-ui-redux-store';
1515
import {GlobalState} from '@neos-project/neos-ui-redux-store/src/System';
@@ -21,6 +21,7 @@ import backend, {Routes} from '@neos-project/neos-ui-backend-connector';
2121
import {makeReloadNodes} from '../CR/NodeOperations/reloadNodes';
2222
import {updateWorkspaceInfo} from '../CR/Workspaces';
2323
import {makeResolveConflicts, makeSyncPersonalWorkspace} from '../Sync';
24+
import {translate} from '@neos-project/neos-ui-i18n';
2425

2526
const handleWindowBeforeUnload = (event: BeforeUnloadEvent) => {
2627
event.preventDefault();
@@ -37,6 +38,21 @@ type PublishingResponse =
3738
| { conflicts: Conflict[] }
3839
| { error: AnyError };
3940

41+
const PUBLISH_SUCCESS_TRANSLATIONS = {
42+
[PublishingScope.ALL]: {
43+
id: 'Neos.Neos.Ui:PublishingDialog:publish.all.success.message',
44+
fallback: 'All {numberOfChanges} change(s) in workspace "{scopeTitle}" were successfully published to workspace "{targetWorkspaceName}".'
45+
},
46+
[PublishingScope.SITE]: {
47+
id: 'Neos.Neos.Ui:PublishingDialog:publish.site.success.message',
48+
fallback: '{numberOfChanges} change(s) in site "{scopeTitle}" were successfully published to workspace "{targetWorkspaceName}".'
49+
},
50+
[PublishingScope.DOCUMENT]: {
51+
id: 'Neos.Neos.Ui:PublishingDialog:publish.document.success.message',
52+
fallback: '{numberOfChanges} change(s) in document "{scopeTitle}" were sucessfully published to workspace "{targetWorkspaceName}".'
53+
}
54+
}
55+
4056
export function * watchPublishing({routes}: {routes: Routes}) {
4157
const {endpoints} = backend.get();
4258
const ENDPOINT_BY_MODE_AND_SCOPE = {
@@ -74,7 +90,7 @@ export function * watchPublishing({routes}: {routes: Routes}) {
7490
const resolveConflicts = makeResolveConflicts({syncPersonalWorkspace});
7591

7692
yield takeEvery(actionTypes.CR.Publishing.STARTED, function * publishingWorkflow(action: ReturnType<typeof actions.CR.Publishing.start>) {
77-
const confirmed = yield * waitForConfirmation();
93+
const confirmed = action.payload.requireConfirmation ? yield * waitForConfirmation() : true;
7894
if (!confirmed) {
7995
return;
8096
}
@@ -99,7 +115,33 @@ export function * watchPublishing({routes}: {routes: Routes}) {
99115
: yield call(endpoint!, ancestorId, workspaceName, dimensionSpacePoint);
100116

101117
if ('success' in result) {
102-
yield put(actions.CR.Publishing.succeed(result.success.numberOfAffectedChanges));
118+
if (action.payload.requireConfirmation) {
119+
yield put(actions.CR.Publishing.succeed(result.success.numberOfAffectedChanges));
120+
} else {
121+
// fixme, this translation logic is duplicated from the PublishingDialog component
122+
let scopeTitle = 'N/A';
123+
if (scope === PublishingScope.ALL) {
124+
scopeTitle = yield select(selectors.CR.Workspaces.personalWorkspaceNameSelector);
125+
} else if (scope === PublishingScope.SITE) {
126+
scopeTitle = (yield select(selectors.CR.Nodes.siteNodeSelector))?.label ?? scopeTitle;
127+
} else if (scope === PublishingScope.DOCUMENT) {
128+
scopeTitle = (yield select(selectors.CR.Nodes.documentNodeSelector))?.label ?? scopeTitle;
129+
}
130+
131+
const parameters = {
132+
numberOfChanges: result.success.numberOfAffectedChanges,
133+
scopeTitle,
134+
targetWorkspaceName: yield select(selectors.CR.Workspaces.baseWorkspaceSelector)
135+
};
136+
137+
showFlashMessage({
138+
id: 'publishing',
139+
severity: 'success',
140+
message: translate(PUBLISH_SUCCESS_TRANSLATIONS[scope].id, PUBLISH_SUCCESS_TRANSLATIONS[scope].fallback, parameters),
141+
timeout: 2000
142+
});
143+
yield put(actions.CR.Publishing.finish());
144+
}
103145
yield * reloadAfterPublishing();
104146
} else if ('conflicts' in result) {
105147
yield put(actions.CR.Publishing.conflicts());

packages/neos-ui-sagas/src/Sync/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,10 @@ const makeDiscardAll = () => {
164164
function * discardAll() {
165165
yield put(actions.CR.Publishing.start(
166166
PublishingMode.DISCARD,
167-
PublishingScope.ALL
167+
PublishingScope.ALL,
168+
true
168169
));
169-
yield put(actions.CR.Publishing.confirm()); // todo auto-confirm this case
170+
yield put(actions.CR.Publishing.confirm()); // we auto-confirm this case but do want to display a full result dialog
170171
yield put(actions.CR.Syncing.finish()); // stop syncing as discarding takes now over
171172
const {cancelled}: {
172173
cancelled: null | ReturnType<typeof actions.CR.Publishing.cancel>;

packages/neos-ui/src/Containers/Modals/PublishingDialog/ConfirmationDialog.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ const ConfirmationDialogVariants = {
6969
}
7070
}
7171
},
72+
// NOTE that with https://github.com/neos/neos-ui/pull/3909 this variant is currently effectively not used as confirmation is not required
7273
[PublishingScope.DOCUMENT]: {
7374
label: {
7475
title: {

packages/neos-ui/src/Containers/Modals/PublishingDialog/PublishingDialog.tsx

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* information, please view the LICENSE file which was distributed with this
88
* source code.
99
*/
10-
import React from 'react';
10+
import React, {useEffect, useState} from 'react';
1111
// @ts-ignore
1212
import {connect} from 'react-redux';
1313

@@ -27,9 +27,10 @@ import {ResultDialog} from './ResultDialog';
2727
const {
2828
publishableNodesSelector,
2929
publishableNodesInDocumentSelector,
30-
personalWorkspaceNameSelector
31-
} = (selectors as any).CR.Workspaces;
32-
const {siteNodeSelector, documentNodeSelector} = (selectors as any).CR.Nodes;
30+
personalWorkspaceNameSelector,
31+
baseWorkspaceSelector
32+
} = selectors.CR.Workspaces;
33+
const {siteNodeSelector, documentNodeSelector} = selectors.CR.Nodes;
3334

3435
type PublishingDialogProperties =
3536
| { publishingState: null }
@@ -84,8 +85,8 @@ const PublishingDialog: React.FC<PublishingDialogProps> = (props) => {
8485
/>
8586
);
8687

87-
case PublishingPhase.ONGOING:
88-
return (
88+
case PublishingPhase.ONGOING: {
89+
const ongoingScreen = (
8990
<ProcessIndicator
9091
mode={props.publishingState.mode}
9192
scope={props.publishingState.scope}
@@ -96,6 +97,9 @@ const PublishingDialog: React.FC<PublishingDialogProps> = (props) => {
9697
/>
9798
);
9899

100+
return props.publishingState.process.autoConfirmed ? <DelayedDisplay component={ongoingScreen} delay={750} /> : ongoingScreen;
101+
}
102+
99103
case PublishingPhase.CONFLICTS:
100104
return null;
101105

@@ -117,14 +121,30 @@ const PublishingDialog: React.FC<PublishingDialogProps> = (props) => {
117121
}
118122
};
119123

124+
const DelayedDisplay = (props: {component: React.ReactElement, delay: number}) => {
125+
const [enable, setEnable] = useState(false);
126+
127+
useEffect(() => {
128+
const id = setTimeout(() => {
129+
setEnable(true);
130+
}, props.delay)
131+
return () => {
132+
clearTimeout(id)
133+
}
134+
}, [props.delay])
135+
136+
return enable ? props.component : null;
137+
}
138+
120139
export default connect((state: GlobalState): PublishingDialogProperties => {
121140
const {publishing: publishingState} = state.cr;
122141
if (publishingState === null) {
123142
return {publishingState};
124143
}
125144

126145
const {scope} = publishingState;
127-
const {name: sourceWorkspaceName, baseWorkspace} = state.cr.workspaces.personalWorkspace;
146+
const sourceWorkspaceName = personalWorkspaceNameSelector(state);
147+
const baseWorkspace = baseWorkspaceSelector(state);
128148
const targetWorkspaceName = publishingState.mode === PublishingMode.PUBLISH
129149
? baseWorkspace
130150
: null;
@@ -142,11 +162,11 @@ export default connect((state: GlobalState): PublishingDialogProperties => {
142162

143163
let scopeTitle = 'N/A';
144164
if (scope === PublishingScope.ALL) {
145-
scopeTitle = personalWorkspaceNameSelector(state);
165+
scopeTitle = sourceWorkspaceName;
146166
} else if (scope === PublishingScope.SITE) {
147-
scopeTitle = siteNodeSelector(state).label;
167+
scopeTitle = siteNodeSelector(state)?.label ?? scopeTitle;
148168
} else if (scope === PublishingScope.DOCUMENT) {
149-
scopeTitle = documentNodeSelector(state).label;
169+
scopeTitle = documentNodeSelector(state)?.label ?? scopeTitle;
150170
}
151171

152172
return {

packages/neos-ui/src/Containers/Modals/PublishingDialog/ResultDialog.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ const ResultDialogVariants = {
6060
}
6161
}
6262
},
63+
// NOTE that with https://github.com/neos/neos-ui/pull/3909 this variant is currently effectively not used as confirmation is not required
6364
[PublishingScope.DOCUMENT]: {
6465
label: {
6566
title: {

0 commit comments

Comments
 (0)