65717: Ability to discard all field-updates at once (fixes discard and reinstate issues)

This commit is contained in:
Kristof De Langhe
2019-10-21 10:43:46 +02:00
parent 6dca421256
commit b9754764b3
6 changed files with 77 additions and 17 deletions

View File

@@ -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);
}
/**

View File

@@ -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
*/

View File

@@ -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
*/

View File

@@ -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<ObjectUpdatesAction>();
}
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;
})
)
)

View File

@@ -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) {
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

View File

@@ -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