Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Saving planning items #2179

Open
wants to merge 14 commits into
base: feature/embedded-events-editor
Choose a base branch
from
47 changes: 18 additions & 29 deletions client/components/LockContainer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
import React from 'react';
import PropTypes from 'prop-types';
import {get} from 'lodash';
import classNames from 'classnames';

import {UserAvatarWithMargin} from '../../components/UserAvatar';
import {UserAvatar} from '../../components/UserAvatar';
import {LockContainerPopup} from './LockContainerPopup';

import './style.scss';

export class LockContainer extends React.Component {
constructor(props) {
interface LockContainerProps {
lockedUser: any;
users: any[] | any;
displayText?: string;
showUnlock?: boolean;
onUnlock?: () => void;
noMargin?: boolean;
}

interface LockContainerState {
openUnlockPopup: boolean;
}

export class LockContainer extends React.Component<LockContainerProps, LockContainerState> {
constructor(props: LockContainerProps) {
super(props);
this.state = {openUnlockPopup: false};
this.toggleOpenUnlockPopup = this.toggleOpenUnlockPopup.bind(this);
Expand All @@ -24,10 +35,8 @@ export class LockContainer extends React.Component {
lockedUser,
users,
displayText,
showUnlock,
withLoggedInfo,
showUnlock = true,
onUnlock,
small,
noMargin,
} = this.props;

Expand All @@ -49,7 +58,7 @@ export class LockContainer extends React.Component {
)}
>
<a onClick={this.toggleOpenUnlockPopup}>
<UserAvatarWithMargin user={user} />
<UserAvatar user={user} />
</a>
</div>
{this.state.openUnlockPopup && (
Expand All @@ -66,23 +75,3 @@ export class LockContainer extends React.Component {
);
}
}

LockContainer.propTypes = {
lockedUser: PropTypes.object,
users: PropTypes.oneOfType([
PropTypes.array,
PropTypes.object,
]),
displayText: PropTypes.string,
showUnlock: PropTypes.bool,
withLoggedInfo: PropTypes.bool,
onUnlock: PropTypes.func,
small: PropTypes.bool,
noMargin: PropTypes.bool,
};

LockContainer.defaultProps = {
showUnlock: true,
withLoggedInfo: true,
small: true,
};
110 changes: 50 additions & 60 deletions client/components/Main/ItemEditor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {ItemManager} from './ItemManager';
import {AutoSave} from './AutoSave';
import {EditorHeader} from './EditorHeader';
import {pickRelatedEventsForPlanning} from './../../../utils/planning';
import {embeddedPlanningHasUnsavedChanges} from '../../../components/editor-standalone/save-handling';

export class EditorComponent extends React.Component<IEditorProps, IEditorState> {
autoSave: AutoSave;
Expand Down Expand Up @@ -58,7 +59,8 @@ export class EditorComponent extends React.Component<IEditorProps, IEditorState>
tabProps: {
forEditor: !this.props.inModalView,
forEditorModal: this.props.inModalView,
}},
}
},
];

if (this.props.addNewsItemToPlanning) {
Expand Down Expand Up @@ -195,68 +197,54 @@ export class EditorComponent extends React.Component<IEditorProps, IEditorState>
return;
}

this.autoSave.flushAutosave()
.then(() => {
const {
openCancelModal,
itemId,
itemType,
addNewsItemToPlanning,
} = this.props;
const {dirty, errorMessages, initialValues} = this.state;

this.setState({submitting: true});
this.autoSave.flushAutosave().then(() => {
const {openCancelModal, itemId, itemType, addNewsItemToPlanning} = this.props;
const {dirty, errorMessages, initialValues} = this.state;
const updateStates = !addNewsItemToPlanning;

const updateStates = !addNewsItemToPlanning;
this.setState({submitting: true});

if (!dirty) {
this.onCancel();
} else {
const hasErrors = !isEqual(errorMessages, []);
const isKilled = isItemKilled(initialValues);
if (!(dirty || embeddedPlanningHasUnsavedChanges())) {
this.onCancel();
return;
}

const onCancel = () => {
if (updateStates) {
this.setState({submitting: false});
}
};

const onIgnore = () => {
this.itemManager.unlockAndCancel();
};

const onSave = (isKilled || hasErrors) ? null :
(withConfirmation, updateMethod, planningUpdateMethods) => (
this.itemManager.save(
withConfirmation,
{name: updateMethod, value: updateMethod},
true,
updateStates,
planningUpdateMethods
)
);

const onSaveAndPost = (!isKilled || hasErrors) ? null :
(withConfirmation, updateMethod, planningUpdateMethods) => (
this.itemManager.saveAndPost(
withConfirmation,
updateMethod,
true,
updateStates,
planningUpdateMethods
)
);

openCancelModal({
itemId: itemId,
itemType: itemType,
onCancel: onCancel,
onIgnore: onIgnore,
onSave: onSave,
onSaveAndPost: onSaveAndPost,
});
}
const hasErrors = !isEqual(errorMessages, []);
const isKilled = isItemKilled(initialValues);
const onSave = (isKilled || hasErrors) ? null : (withConfirmation, updateMethod) => (
this.itemManager.save(
withConfirmation,
{name: updateMethod, value: updateMethod},
true,
updateStates,
)
);
const onSaveAndPost = (!isKilled || hasErrors) ? null : (withConfirmation, updateMethod) => (
this.itemManager.saveAndPost(
withConfirmation,
updateMethod,
true,
updateStates,
)
);

openCancelModal({
itemId: itemId,
itemType: itemType,
onCancel: () => {
if (updateStates) {
this.setState({submitting: false});
}
},
onIgnore: () => {
this.itemManager.unlockAndCancel(
embeddedPlanningHasUnsavedChanges() ? 'HANDLE_UNSAVED_CHANGES' : 'DISCARD',
);
},
onSave: onSave,
onSaveAndPost: onSaveAndPost,
});
});
}

onCancel(updateStates = true) {
Expand All @@ -276,7 +264,9 @@ export class EditorComponent extends React.Component<IEditorProps, IEditorState>
this.props.onCancel();
}

return this.itemManager.unlockAndCancel();
return this.itemManager.unlockAndCancel(
embeddedPlanningHasUnsavedChanges() ? 'HANDLE_UNSAVED_CHANGES' : 'DISCARD',
);
}

setActiveTab(tab) {
Expand Down
89 changes: 60 additions & 29 deletions client/components/Main/ItemEditor/EditorHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ import {StretchBar} from '../../UI/SubNav';
import {LockContainer, ItemIcon} from '../../index';
import {EditorItemActions} from './index';
import {ButtonGroup} from 'superdesk-ui-framework';
import {IEditorProps, IEditorState, ILockedItems, IPrivileges, ISession} from 'interfaces';
import {IEditorProps, IEditorState, IEventOrPlanningItem, ILockedItems, IPrivileges, ISession} from 'interfaces';
import {IUser} from 'superdesk-api';
import {ItemManager} from './ItemManager';
import {AutoSave} from './AutoSave';
import {IUIButtonProps} from 'components/UI/Button';

interface IProps {
diff: IEditorState['diff'];
initialValues: IEditorState['initialValues'];
diff: IEventOrPlanningItem;
initialValues: IEventOrPlanningItem;
cancel(): void;
minimize(): void;
submitting: boolean;
Expand Down Expand Up @@ -124,17 +125,19 @@ export class EditorHeader extends React.Component<IProps> {

states.showEdit = states.existingItem &&
!states.isLockedInContext &&
eventUtils.canEditEvent(initialValues, session, privileges, lockedItems);
eventUtils.canEditEvent(initialValues as IEventItem, session, privileges, lockedItems);

if (states.readOnly) {
return;
}

if (states.isLockedInContext && get(states.itemLock, 'action') === 'edit') {
states.canPost = eventUtils.canPostEvent(initialValues, session, privileges, lockedItems);
states.canUnpost = eventUtils.canUnpostEvent(initialValues, session, privileges, lockedItems);
states.canUpdate = eventUtils.canUpdateEvent(initialValues, session, privileges, lockedItems);
states.canEdit = eventUtils.canEditEvent(initialValues, session, privileges, lockedItems);
const initialVal = initialValues as IEventItem;

states.canPost = eventUtils.canPostEvent(initialVal, session, privileges, lockedItems);
states.canUnpost = eventUtils.canUnpostEvent(initialVal, session, privileges, lockedItems);
states.canUpdate = eventUtils.canUpdateEvent(initialVal, session, privileges, lockedItems);
states.canEdit = eventUtils.canEditEvent(initialVal, session, privileges, lockedItems);
}
}

Expand All @@ -153,9 +156,12 @@ export class EditorHeader extends React.Component<IProps> {
return;
}

const initialVals = initialValues as IPlanningItem;
const diffCasted = diff as IPlanningItem;

states.showEdit = states.existingItem &&
!states.isLockedInContext &&
planningUtils.canEditPlanning(initialValues, null, session, privileges, lockedItems) &&
planningUtils.canEditPlanning(initialVals, null, session, privileges, lockedItems) &&
!addNewsItemToPlanning;

if (states.readOnly) {
Expand All @@ -165,27 +171,51 @@ export class EditorHeader extends React.Component<IProps> {
if (states.isLockedInContext) {
switch (get(states, 'itemLock.action')) {
case 'edit':
states.canPost = planningUtils.canPostPlanning(diff,
associatedEvents, session, privileges, lockedItems);
states.canUnpost = planningUtils.canUnpostPlanning(initialValues,
session, privileges, lockedItems);
states.canUpdate = planningUtils.canUpdatePlanning(initialValues,
associatedEvents, session, privileges, lockedItems);
states.canEdit = planningUtils.canEditPlanning(initialValues,
associatedEvents, session, privileges, lockedItems);
states.canPost = planningUtils.canPostPlanning(
diffCasted,
associatedEvents,
session,
privileges,
lockedItems,
);
states.canUnpost = planningUtils.canUnpostPlanning(
initialVals,
session,
privileges,
lockedItems,
);
states.canUpdate = planningUtils.canUpdatePlanning(
initialVals,
associatedEvents,
session,
privileges,
lockedItems,
);
states.canEdit = planningUtils.canEditPlanning(
initialVals,
associatedEvents,
session,
privileges,
lockedItems,
);
break;
case 'add_to_planning':
states.canPost = planningUtils.canPostPlanning(
diff,
diffCasted,
associatedEvents,
session,
privileges,
lockedItems
);
states.canUpdate = planningUtils.canUpdatePlanning(initialValues,
associatedEvents, session, privileges, lockedItems);
states.canUpdate = planningUtils.canUpdatePlanning(
initialVals,
associatedEvents,
session,
privileges,
lockedItems,
);
states.canEdit = planningUtils.canEditPlanning(
initialValues,
initialVals,
associatedEvents,
session,
privileges,
Expand Down Expand Up @@ -234,7 +264,7 @@ export class EditorHeader extends React.Component<IProps> {
canEditExpired: false,
};

states.isExpired = isItemExpired(initialValues);
states.isExpired = isItemExpired(initialValues) ?? false;
states.canEditExpired = privileges[PRIVILEGES.EDIT_EXPIRED];
states.itemLock = lockUtils.getLock(initialValues, lockedItems);
states.isLockedInContext = addNewsItemToPlanning ?
Expand Down Expand Up @@ -280,15 +310,12 @@ export class EditorHeader extends React.Component<IProps> {
doubleSize={true}
color={states.isEvent ? ICON_COLORS.WHITE : ICON_COLORS.LIGHT_BLUE}
/>

{!showLockContainer ? null : (
{showLockContainer && (
<LockContainer
lockedUser={states.lockedUser}
users={users}
showUnlock={unlockPrivilege && showUnlock}
withLoggedInfo={true}
onUnlock={itemManager.unlockThenLock.bind(null, initialValues)}
small={false}
noMargin={true}
/>
)}
Expand Down Expand Up @@ -322,7 +349,13 @@ export class EditorHeader extends React.Component<IProps> {
}

const notDirtyOrSubmitting = !dirty || submitting;
const buttons = [{

type IButtonProps = Array<{
state: string,
props: IUIButtonProps
}>;

const buttons: IButtonProps = [{
state: 'showCancel',
props: {
color: states.isEvent ? 'ui-dark' : null,
Expand Down Expand Up @@ -451,7 +484,6 @@ export class EditorHeader extends React.Component<IProps> {
<NavButton
onClick={minimize}
icon="big-icon--minimize"
title={gettext('Minimise')}
aria-label={gettext('Minimise')}
/>
)}
Expand All @@ -461,7 +493,6 @@ export class EditorHeader extends React.Component<IProps> {
onClick={closeEditorAndOpenModal}
aria-label={gettext('Edit in popup')}
icon="icon-external"
title={gettext('Edit in popup')}
/>
)}

Expand Down
Loading
Loading