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

[SDESK-6851] fix: Items not unlocked after item actions #1810

Merged
merged 7 commits into from
Jun 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/actions/assignments/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ const save = (original, assignmentUpdates) => (
return promise.then((originalItem) => {
let updates;

if (original.lock_action === 'reassign') {
if (original.lock_action === ASSIGNMENTS.ITEM_ACTIONS.REASSIGN.lock_action) {
updates = pick(assignmentUpdates, 'assigned_to');
updates.assigned_to = pick(
assignmentUpdates.assigned_to,
Expand Down
6 changes: 3 additions & 3 deletions client/actions/assignments/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ const reassign = (assignment) => (
(dispatch) => dispatch(self._openActionModal(
assignment,
ASSIGNMENTS.ITEM_ACTIONS.REASSIGN.actionName,
'reassign'
ASSIGNMENTS.ITEM_ACTIONS.REASSIGN.lock_action
))
);

Expand All @@ -405,7 +405,7 @@ const editPriority = (assignment) => (
(dispatch) => dispatch(_openActionModal(
assignment,
ASSIGNMENTS.ITEM_ACTIONS.EDIT_PRIORITY.actionName,
'edit_priority'
ASSIGNMENTS.ITEM_ACTIONS.EDIT_PRIORITY.lock_action,
))
);

Expand All @@ -418,7 +418,7 @@ const save = (original, updates) => (
(dispatch, getState, {notify}) => (
dispatch(assignments.api.save(original, updates))
.then((updatedItem) => {
notify.success(get(original, 'lock_action') === 'reassign' ?
notify.success(get(original, 'lock_action') === ASSIGNMENTS.ITEM_ACTIONS.REASSIGN.lock_action ?
gettext('The assignment was reassigned.') :
gettext('Assignment priority has been updated.')
);
Expand Down
6 changes: 2 additions & 4 deletions client/actions/events/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,18 @@ const onEventCreated = (_e, data) => (
function onEventUnlocked(_e: {}, data: IWebsocketMessageData['ITEM_UNLOCKED']) {
return (dispatch, getState) => {
if (data?.item != null) {
planningApi.locks.setItemAsUnlocked(data);

const state = getState();
const events = selectors.events.storedEvents(state);
let eventInStore = get(events, data.item, {});
const isCurrentlyLocked = lockUtils.isItemLocked(eventInStore, selectors.locks.getLockedItems(state));

dispatch(main.onItemUnlocked(data, eventInStore, ITEM_TYPE.EVENT));

if (!isCurrentlyLocked && eventInStore?.lock_session == null) {
// No need to announce an unlock, as we have already done so
return Promise.resolve(eventInStore);
}

dispatch(main.onItemUnlocked(data, eventInStore, ITEM_TYPE.EVENT));

eventInStore = {
recurrence_id: get(data, 'recurrence_id') || null,
...eventInStore,
Expand Down
2 changes: 1 addition & 1 deletion client/actions/events/tests/ui_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ describe('actions.events.ui', () => {
data.events[1],
{},
'onSpikeEvent',
null,
'spike',
true,
false,
false,
Expand Down
36 changes: 26 additions & 10 deletions client/actions/events/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ const openSpikeModal = (event, post = false, modalProps = {}) => (
eventWithData,
{},
EVENTS.ITEM_ACTIONS.SPIKE.actionName,
null,
EVENTS.ITEM_ACTIONS.SPIKE.lock_action,
true,
post,
false,
Expand All @@ -187,7 +187,7 @@ const openUnspikeModal = (event, post = false) => (
event,
{},
EVENTS.ITEM_ACTIONS.UNSPIKE.actionName,
null,
EVENTS.ITEM_ACTIONS.UNSPIKE.lock_action,
true,
post
))
Expand All @@ -209,7 +209,7 @@ const openUpdateTimeModal = (event, post = false, fromEditor = true) => {
event,
{},
EVENTS.ITEM_ACTIONS.UPDATE_TIME.actionName,
null,
EVENTS.ITEM_ACTIONS.UPDATE_TIME.lock_action,
true,
post
);
Expand All @@ -233,7 +233,7 @@ const openCancelModal = (event, post = false, fromEditor = true) => {
event,
{},
EVENTS.ITEM_ACTIONS.CANCEL_EVENT.actionName,
null,
EVENTS.ITEM_ACTIONS.CANCEL_EVENT.lock_action,
true,
post
);
Expand All @@ -256,7 +256,7 @@ const openPostponeModal = (event, post = false, fromEditor = true) => {
event,
{},
EVENTS.ITEM_ACTIONS.POSTPONE_EVENT.actionName,
null,
EVENTS.ITEM_ACTIONS.POSTPONE_EVENT.lock_action,
true,
post
);
Expand All @@ -279,7 +279,7 @@ const openRescheduleModal = (event, post = false, fromEditor = true) => {
event,
{},
EVENTS.ITEM_ACTIONS.RESCHEDULE_EVENT.actionName,
null,
EVENTS.ITEM_ACTIONS.RESCHEDULE_EVENT.lock_action,
true,
post
);
Expand Down Expand Up @@ -403,10 +403,20 @@ const _openActionModalFromEditor = ({
Promise.resolve(modifiedEvent);

if (get(previousLock, 'action')) {
promise.then((refetchedEvent) => (
(openInEditor || openInModal) ?
dispatch(main.openForEdit(refetchedEvent, !openInModal, openInModal)) :
planningApi.locks.lockItem(refetchedEvent, previousLock.action)
promise.then((refetchedItem) => (
planningApi.locks.lockItem(refetchedItem, previousLock.action)
.then((lockedItem) => {
if (openInEditor || openInModal) {
dispatch(main.openEditorAction(
lockedItem,
'edit',
true,
openInModal,
));
}

return lockedItem;
})
), () => Promise.reject());
}

Expand Down Expand Up @@ -989,6 +999,9 @@ const onMarkEventCompleted = (event, editor = false) => (
}
}),
autoClose: true,
// Add the event to modalProps, so if this item was unlocked by someone else
// this modal will close
original: event,
},
}))), (error) => {
notify.error(getErrorMessage(error, gettext('Could not obtain lock on the event.')));
Expand All @@ -1008,6 +1021,9 @@ const onMarkEventCompleted = (event, editor = false) => (
}),
onCancel: () => planningApi.locks.unlockItem(original),
autoClose: true,
// Add the event to modalProps, so if this item was unlocked by someone else
// this modal will close
original: event,
},
}))), (error) => {
notify.error(getErrorMessage(error, gettext('Could not obtain lock on the event.')));
Expand Down
40 changes: 26 additions & 14 deletions client/actions/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1377,14 +1377,17 @@ function onItemUnlocked(
itemType: ITEM_TYPE,
) {
return (dispatch, getState) => {
const lockedItems = selectors.locks.getLockedItems(getState());
const state = getState();
const lockedItems = selectors.locks.getLockedItems(state);
const itemLock = lockUtils.getLock(item, lockedItems);
const sessionId = selectors.general.session(getState()).sessionId;
const sessionId = selectors.general.session(state).sessionId;

const editorItemId = selectors.forms.currentItemId(getState());
const editorModalItemId = selectors.forms.currentItemIdModal(getState());
const editorItemId = selectors.forms.currentItemId(state);
const editorModalItemId = selectors.forms.currentItemIdModal(state);
const itemId = getItemId(item);

planningApi.locks.setItemAsUnlocked(data);

if (editorItemId === itemId || editorModalItemId === itemId) {
dispatch(self.changeEditorAction(
'read',
Expand All @@ -1397,14 +1400,19 @@ function onItemUnlocked(
data.lock_session !== sessionId &&
itemLock.session === sessionId
) {
const user = selectors.general.users(getState()).find((u) => u._id === data.user);
const autoSaves = selectors.forms.autosaves(getState());
const user = selectors.general.users(state).find((u) => u._id === data.user);
const autoSaves = selectors.forms.autosaves(state);
const autoSaveInStore = get(autoSaves, `${itemType}['${data.item}']`);

if (autoSaveInStore) {
// Delete the changes from the local redux
dispatch(autosave.removeLocalAutosave(autoSaveInStore));
}
if (selectors.general.getActionModalItemId(state) === item._id) {
// This item has an action modal open, such as 'Spike <item>'
// Close it now, so the 'Item Unlocked' Modal will be the only one in the Modal stack
dispatch(hideModal());
}

dispatch(showModal({
modalType: MODALS.NOTIFICATION_MODAL,
Expand All @@ -1414,7 +1422,7 @@ function onItemUnlocked(
},
}));

if (getItemType(item) === ITEM_TYPE.PLANNING && selectors.general.currentWorkspace(getState())
if (getItemType(item) === ITEM_TYPE.PLANNING && selectors.general.currentWorkspace(state)
=== WORKSPACE.AUTHORING) {
dispatch(self.closePreviewAndEditorForItems([item]));
}
Expand Down Expand Up @@ -1534,13 +1542,17 @@ const spikeAfterUnlock = (unlockedItem, previousLock, openInEditor, openInModal)
(dispatch) => {
const onCloseModal = (updatedItem) => {
if (!isItemSpiked(updatedItem) && get(previousLock, 'action')) {
if (openInEditor || openInModal) {
return dispatch(
self.openForEdit(updatedItem, !openInModal, openInModal)
);
}

return planningApi.locks.lockItem(updatedItem, previousLock.action);
planningApi.locks.lockItem(updatedItem, previousLock.action)
.then((lockedItem) => {
if (openInEditor || openInModal) {
dispatch(self.openEditorAction(
lockedItem,
'edit',
true,
openInModal
));
}
});
}
};
const dispatchCall = getItemType(unlockedItem) === ITEM_TYPE.PLANNING ?
Expand Down
6 changes: 2 additions & 4 deletions client/actions/planning/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,19 +146,17 @@ const onPlanningLocked = (e, data) => (
function onPlanningUnlocked(_e: {}, data: IWebsocketMessageData['ITEM_UNLOCKED']) {
return (dispatch, getState) => {
if (data?.item != null) {
planningApi.locks.setItemAsUnlocked(data);

const state = getState();
let planningItem = selectors.planning.storedPlannings(state)[data.item];
const isCurrentlyLocked = lockUtils.isItemLocked(planningItem, selectors.locks.getLockedItems(state));

dispatch(main.onItemUnlocked(data, planningItem, ITEM_TYPE.PLANNING));

if (!isCurrentlyLocked && planningItem?.lock_session == null) {
// No need to announce an unlock, as we have already done so
return Promise.resolve();
}

dispatch(main.onItemUnlocked(data, planningItem, ITEM_TYPE.PLANNING));

planningItem = {
event_item: get(data, 'event_item') || null,
recurrence_id: get(data, 'recurrence_id') || null,
Expand Down
4 changes: 2 additions & 2 deletions client/actions/planning/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ const openSpikeModal = (plan, post = false, modalProps = {}) => (
dispatch(self._openActionModal(
plan,
PLANNING.ITEM_ACTIONS.SPIKE.actionName,
null,
PLANNING.ITEM_ACTIONS.SPIKE.lock_action,
post,
false,
modalProps
Expand All @@ -351,7 +351,7 @@ const openUnspikeModal = (plan, post = false) => (
(dispatch) => dispatch(self._openActionModal(
plan,
PLANNING.ITEM_ACTIONS.UNSPIKE.actionName,
null,
PLANNING.ITEM_ACTIONS.UNSPIKE.lock_action,
post
))
);
Expand Down
2 changes: 1 addition & 1 deletion client/api/locks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
IPlanningAPI,
IWebsocketMessageData,
IAssignmentOrPlanningItem,
IFeaturedPlanningLock, IAssignmentItem,
IFeaturedPlanningLock,
} from '../interfaces';
import {planningApi, superdeskApi} from '../superdeskApi';

Expand Down
19 changes: 7 additions & 12 deletions client/components/ItemActionConfirmation/forms/cancelEventForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {get, cloneDeep, isEmpty} from 'lodash';

import {planningApi} from '../../../superdeskApi';
import {IEventItem} from '../../../interfaces';
import * as actions from '../../../actions';
import * as selectors from '../../../selectors';
import {eventUtils, gettext} from '../../../utils';
import {EVENTS} from '../../../constants';
import {onItemActionModalHide} from './utils';

import {EventScheduleSummary} from '../../Events';
import {UpdateMethodSelection} from '../UpdateMethodSelection';
Expand Down Expand Up @@ -198,17 +199,11 @@ const mapDispatchToProps = (dispatch) => ({
onSubmit: (original, updates) => dispatch(
actions.events.ui.cancelEvent(original, updates)
),
onHide: (original, modalProps) => {
const promise = original.lock_action === EVENTS.ITEM_ACTIONS.CANCEL_EVENT.lock_action ?
planningApi.locks.unlockItem(original) :
Promise.resolve(original);

if (get(modalProps, 'onCloseModal')) {
promise.then((updatedEvent) => modalProps.onCloseModal(updatedEvent));
}

return promise;
},
onHide: (original: IEventItem, modalProps) => onItemActionModalHide(
original,
original.lock_action === EVENTS.ITEM_ACTIONS.CANCEL_EVENT.lock_action,
modalProps
),
});

export const CancelEventForm = connect(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as selectors from '../../../selectors';
import {formProfile} from '../../../validators';
import {isItemCancelled, gettext} from '../../../utils';
import {PLANNING} from '../../../constants';
import {onItemActionModalHide} from './utils';

import {Row} from '../../UI/Preview';
import {TextAreaInput} from '../../UI/Form';
Expand Down Expand Up @@ -154,12 +155,11 @@ const mapDispatchToProps = (dispatch) => ({
return Promise.resolve(updatedPlan);
});
},

onHide: (planning: IPlanningItem) => {
if (cancelBasedLocks.includes(planning.lock_action)) {
planningApi.locks.unlockItem(planning);
}
},
onHide: (original: IPlanningItem, modalProps) => onItemActionModalHide(
original,
cancelBasedLocks.includes(original.lock_action),
modalProps,
),
});

export const CancelPlanningCoveragesForm = connect(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {connect} from 'react-redux';
import {get, isEqual, cloneDeep} from 'lodash';

import {appConfig} from 'appConfig';
import {planningApi} from '../../../superdeskApi';
import {IEventItem} from '../../../interfaces';

import * as actions from '../../../actions';
import {EventScheduleSummary, EventScheduleInput} from '../../Events';
Expand All @@ -14,6 +14,7 @@ import {Row} from '../../UI/Preview';
import {Field} from '../../UI/Form';
import {validateItem} from '../../../validators';
import {updateFormValues, eventUtils, timeUtils, gettext} from '../../../utils';
import {onItemActionModalHide} from './utils';

import '../style.scss';

Expand Down Expand Up @@ -208,11 +209,11 @@ const mapDispatchToProps = (dispatch) => ({
return dispatch(actions.main.save(original, newUpdates, false));
},

onHide: (event) => {
if (event.lock_action === EVENTS.ITEM_ACTIONS.CONVERT_TO_RECURRING.lock_action) {
planningApi.locks.unlockItem(event);
}
},
onHide: (original: IEventItem, modalProps) => onItemActionModalHide(
original,
original.lock_action === EVENTS.ITEM_ACTIONS.CONVERT_TO_RECURRING.lock_action,
modalProps,
),

onValidate: (item, profile, errors, errorsMessages, fieldsToValidate) => dispatch(validateItem({
profileName: ITEM_TYPE.EVENT,
Expand Down
Loading