diff --git a/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts b/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts index d1f9335cc0..1a0c0ad636 100644 --- a/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts +++ b/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts @@ -157,13 +157,8 @@ export class ItemBitstreamsComponent extends AbstractItemUpdateComponent impleme * Shows a notification to remind the user that they can undo this */ discard() { - super.discard(); const undoNotification = this.notificationsService.info(this.getNotificationTitle('discarded'), this.getNotificationContent('discarded'), {timeOut: this.discardTimeOut}); - this.bundles$.pipe(take(1)).subscribe((bundles: Bundle[]) => { - bundles.forEach((bundle: Bundle) => { - this.objectUpdatesService.discardFieldUpdates(bundle.self, undoNotification); - }); - }); + this.objectUpdatesService.discardAllFieldUpdates(this.url, undoNotification); } /** diff --git a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.ts b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.ts index e2b7864304..87774a3f17 100644 --- a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.ts +++ b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.ts @@ -26,6 +26,11 @@ export class ItemEditBitstreamBundleComponent implements OnInit { */ @Input() bundle: Bundle; + /** + * The current url of this page + */ + @Input() url: string; + /** * The updates to the current bundle */ diff --git a/src/app/core/data/object-updates/object-updates.actions.ts b/src/app/core/data/object-updates/object-updates.actions.ts index 6cd74b2626..6190387641 100644 --- a/src/app/core/data/object-updates/object-updates.actions.ts +++ b/src/app/core/data/object-updates/object-updates.actions.ts @@ -14,6 +14,7 @@ export const ObjectUpdatesActionTypes = { DISCARD: type('dspace/core/cache/object-updates/DISCARD'), REINSTATE: type('dspace/core/cache/object-updates/REINSTATE'), REMOVE: type('dspace/core/cache/object-updates/REMOVE'), + REMOVE_ALL: type('dspace/core/cache/object-updates/REMOVE_ALL'), REMOVE_FIELD: type('dspace/core/cache/object-updates/REMOVE_FIELD'), }; @@ -144,7 +145,8 @@ export class DiscardObjectUpdatesAction implements Action { type = ObjectUpdatesActionTypes.DISCARD; payload: { url: string, - notification: INotification + notification: INotification, + discardAll: boolean; }; /** @@ -153,12 +155,14 @@ export class DiscardObjectUpdatesAction implements Action { * @param url * the unique url of the page for which the changes should be discarded * @param notification The notification that is raised when changes are discarded + * @param discardAll discard all */ constructor( url: string, - notification: INotification + notification: INotification, + discardAll = false ) { - this.payload = { url, notification }; + this.payload = { url, notification, discardAll }; } } @@ -206,6 +210,13 @@ export class RemoveObjectUpdatesAction implements Action { } } +/** + * An ngrx action to remove all previously discarded updates in the ObjectUpdates state + */ +export class RemoveAllObjectUpdatesAction implements Action { + type = ObjectUpdatesActionTypes.REMOVE_ALL; +} + /** * An ngrx action to remove a single field update in the ObjectUpdates state for a certain page url and field uuid */ diff --git a/src/app/core/data/object-updates/object-updates.effects.ts b/src/app/core/data/object-updates/object-updates.effects.ts index 88cd3bc718..c115939576 100644 --- a/src/app/core/data/object-updates/object-updates.effects.ts +++ b/src/app/core/data/object-updates/object-updates.effects.ts @@ -3,12 +3,12 @@ import { Actions, Effect, ofType } from '@ngrx/effects'; import { DiscardObjectUpdatesAction, ObjectUpdatesAction, - ObjectUpdatesActionTypes, + ObjectUpdatesActionTypes, RemoveAllObjectUpdatesAction, RemoveObjectUpdatesAction } from './object-updates.actions'; import { delay, filter, map, switchMap, take, tap } from 'rxjs/operators'; import { of as observableOf, race as observableRace, Subject } from 'rxjs'; -import { hasNoValue } from '../../../shared/empty.util'; +import { hasNoValue, hasValue } from '../../../shared/empty.util'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { INotification } from '../../../shared/notifications/models/notification.model'; import { @@ -16,6 +16,7 @@ import { NotificationsActionTypes, RemoveNotificationAction } from '../../../shared/notifications/notifications.actions'; +import { Action } from '@ngrx/store'; /** * NGRX effects for ObjectUpdatesActions @@ -53,13 +54,14 @@ export class ObjectUpdatesEffects { .pipe( ofType(...Object.values(ObjectUpdatesActionTypes)), map((action: ObjectUpdatesAction) => { + if (hasValue(action.payload)) { const url: string = action.payload.url; if (hasNoValue(this.actionMap$[url])) { this.actionMap$[url] = new Subject(); } this.actionMap$[url].next(action); } - ) + }) ); /** @@ -91,9 +93,15 @@ export class ObjectUpdatesEffects { const url: string = action.payload.url; const notification: INotification = action.payload.notification; const timeOut = notification.options.timeOut; + + let removeAction: Action = new RemoveObjectUpdatesAction(action.payload.url); + if (action.payload.discardAll) { + removeAction = new RemoveAllObjectUpdatesAction(); + } + return observableRace( // Either wait for the delay and perform a remove action - observableOf(new RemoveObjectUpdatesAction(action.payload.url)).pipe(delay(timeOut)), + observableOf(removeAction).pipe(delay(timeOut)), // Or wait for a a user action this.actionMap$[url].pipe( take(1), @@ -106,19 +114,19 @@ export class ObjectUpdatesEffects { return { type: 'NO_ACTION' } } // If someone performed another action, assume the user does not want to reinstate and remove all changes - return new RemoveObjectUpdatesAction(action.payload.url); + return removeAction }) ), this.notificationActionMap$[notification.id].pipe( filter((notificationsAction: NotificationsActions) => notificationsAction.type === NotificationsActionTypes.REMOVE_NOTIFICATION), map(() => { - return new RemoveObjectUpdatesAction(action.payload.url); + return removeAction; }) ), this.notificationActionMap$[this.allIdentifier].pipe( filter((notificationsAction: NotificationsActions) => notificationsAction.type === NotificationsActionTypes.REMOVE_ALL_NOTIFICATIONS), map(() => { - return new RemoveObjectUpdatesAction(action.payload.url); + return removeAction; }) ) ) diff --git a/src/app/core/data/object-updates/object-updates.reducer.ts b/src/app/core/data/object-updates/object-updates.reducer.ts index c0f10ff92a..03fcf44b55 100644 --- a/src/app/core/data/object-updates/object-updates.reducer.ts +++ b/src/app/core/data/object-updates/object-updates.reducer.ts @@ -105,6 +105,9 @@ export function objectUpdatesReducer(state = initialState, action: ObjectUpdates case ObjectUpdatesActionTypes.REMOVE: { return removeObjectUpdates(state, action as RemoveObjectUpdatesAction); } + case ObjectUpdatesActionTypes.REMOVE_ALL: { + return removeAllObjectUpdates(state); + } case ObjectUpdatesActionTypes.REMOVE_FIELD: { return removeFieldUpdate(state, action as RemoveFieldUpdateAction); } @@ -175,7 +178,24 @@ function addFieldUpdate(state: any, action: AddFieldUpdateAction) { * @param action The action to perform on the current state */ function discardObjectUpdates(state: any, action: DiscardObjectUpdatesAction) { - const url: string = action.payload.url; + if (action.payload.discardAll) { + let newState = Object.assign({}, state); + Object.keys(state).filter((path: string) => !path.endsWith(OBJECT_UPDATES_TRASH_PATH)).forEach((path: string) => { + newState = discardObjectUpdatesFor(path, newState); + }); + return newState; + } else { + const url: string = action.payload.url; + return discardObjectUpdatesFor(url, state); + } +} + +/** + * Discard all updates for a specific action's url in the store + * @param url The action's url + * @param state The current state + */ +function discardObjectUpdatesFor(url: string, state: any) { const pageState: ObjectUpdatesEntry = state[url]; const newFieldStates = {}; Object.keys(pageState.fieldStates).forEach((uuid: string) => { @@ -228,6 +248,18 @@ function removeObjectUpdatesByURL(state: any, url: string) { return newState; } +/** + * Remove all updates in the store + * @param state The current state + */ +function removeAllObjectUpdates(state: any) { + const newState = Object.assign({}, state); + Object.keys(state).filter((path: string) => path.endsWith(OBJECT_UPDATES_TRASH_PATH)).forEach((path: string) => { + delete newState[path]; + }); + return newState; +} + /** * Discard the update for a specific action's url and field UUID in the store * @param state The current state diff --git a/src/app/core/data/object-updates/object-updates.service.ts b/src/app/core/data/object-updates/object-updates.service.ts index fd439c98de..15d50ecc5a 100644 --- a/src/app/core/data/object-updates/object-updates.service.ts +++ b/src/app/core/data/object-updates/object-updates.service.ts @@ -225,6 +225,15 @@ export class ObjectUpdatesService { this.store.dispatch(new DiscardObjectUpdatesAction(url, undoNotification)); } + /** + * Method to dispatch a DiscardObjectUpdatesAction to the store with discardAll set to true + * @param url The page's URL for which the changes should be discarded + * @param undoNotification The notification which is should possibly be canceled + */ + discardAllFieldUpdates(url: string, undoNotification: INotification) { + this.store.dispatch(new DiscardObjectUpdatesAction(url, undoNotification, true)); + } + /** * Method to dispatch an ReinstateObjectUpdatesAction to the store * @param url The page's URL for which the changes should be reinstated