[UI] Progress indicator for pending operations - Refactoring for code clarity
This commit is contained in:
@@ -94,41 +94,42 @@ define(
|
|||||||
CopyAction.prototype.perform = function() {
|
CopyAction.prototype.perform = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
return AbstractComposeAction.prototype.perform.call(this)
|
function success(){
|
||||||
.then(
|
self.notification.dismiss();
|
||||||
function success(){
|
self.notificationService.info("Copying complete.");
|
||||||
self.notification.dismiss();
|
}
|
||||||
self.notificationService.info("Copying complete.");
|
|
||||||
},
|
function error(errorDetails){
|
||||||
function error(errorDetails){
|
var errorMessage = {
|
||||||
var errorMessage = {
|
title: "Error copying objects.",
|
||||||
title: "Error copying objects.",
|
severity: "error",
|
||||||
severity: "error",
|
hint: errorDetails.message,
|
||||||
hint: errorDetails.message,
|
minimized: true, // want the notification to be minimized initially (don't show banner)
|
||||||
minimized: true, // want the notification to be minimized initially (don't show banner)
|
options: [{
|
||||||
options: [{
|
label: "OK",
|
||||||
label: "OK",
|
callback: function() {
|
||||||
callback: function() {
|
self.dialogService.dismiss();
|
||||||
self.dialogService.dismiss();
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
self.dialogService.dismiss();
|
|
||||||
if (self.notification) {
|
|
||||||
self.notification.dismiss(); // Clear the progress notification
|
|
||||||
}
|
}
|
||||||
self.$log.error("Error copying objects. ", errorDetails);
|
}]
|
||||||
//Show a minimized notification of error for posterity
|
};
|
||||||
self.notificationService.notify(errorMessage);
|
|
||||||
//Display a blocking message
|
self.dialogService.dismiss();
|
||||||
self.dialogService.showBlockingMessage(errorMessage);
|
if (self.notification) {
|
||||||
|
self.notification.dismiss(); // Clear the progress notification
|
||||||
},
|
}
|
||||||
function notification(details){
|
self.$log.error("Error copying objects. ", errorDetails);
|
||||||
self.progress(details.phase, details.totalObjects, details.processed);
|
//Show a minimized notification of error for posterity
|
||||||
}
|
self.notificationService.notify(errorMessage);
|
||||||
);
|
//Display a blocking message
|
||||||
|
self.dialogService.showBlockingMessage(errorMessage);
|
||||||
|
|
||||||
|
}
|
||||||
|
function notification(details){
|
||||||
|
self.progress(details.phase, details.totalObjects, details.processed);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AbstractComposeAction.prototype.perform.call(this)
|
||||||
|
.then(success, error, notification);
|
||||||
};
|
};
|
||||||
return CopyAction;
|
return CopyAction;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,17 @@ define(
|
|||||||
function (uuid) {
|
function (uuid) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class encapsulates the process of copying a domain object
|
||||||
|
* and all of its children.
|
||||||
|
*
|
||||||
|
* @param domainObject The object to copy
|
||||||
|
* @param parent The new location of the cloned object tree
|
||||||
|
* @param persistenceService
|
||||||
|
* @param $q
|
||||||
|
* @param now
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
function CopyTask (domainObject, parent, persistenceService, $q, now){
|
function CopyTask (domainObject, parent, persistenceService, $q, now){
|
||||||
this.domainObject = domainObject;
|
this.domainObject = domainObject;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
@@ -35,6 +46,25 @@ define(
|
|||||||
this.persistenceService = persistenceService;
|
this.persistenceService = persistenceService;
|
||||||
this.persisted = 0;
|
this.persisted = 0;
|
||||||
this.now = now;
|
this.now = now;
|
||||||
|
this.clones = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function composeChild(child, parent) {
|
||||||
|
//Once copied, associate each cloned
|
||||||
|
// composee with its parent clone
|
||||||
|
child.model.location = parent.id;
|
||||||
|
parent.model.composition = parent.model.composition || [];
|
||||||
|
return parent.model.composition.push(child.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cloneObjectModel(objectModel) {
|
||||||
|
var clone = JSON.parse(JSON.stringify(objectModel));
|
||||||
|
|
||||||
|
delete clone.composition;
|
||||||
|
delete clone.persisted;
|
||||||
|
delete clone.modified;
|
||||||
|
|
||||||
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,26 +73,24 @@ define(
|
|||||||
* result in automatic request batching by the browser.
|
* result in automatic request batching by the browser.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
CopyTask.prototype.persistObjects = function(objectClones) {
|
CopyTask.prototype.persistObjects = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
return this.$q.all(objectClones.map(function(clone){
|
return this.$q.all(this.clones.map(function(clone){
|
||||||
clone.model.persisted = self.now();
|
clone.model.persisted = self.now();
|
||||||
return self.persistenceService.createObject(clone.persistenceSpace, clone.id, clone.model)
|
return self.persistenceService.createObject(clone.persistenceSpace, clone.id, clone.model)
|
||||||
.then(function(){
|
.then(function(){
|
||||||
return self.deferred.notify({phase: "copying", totalObjects: objectClones.length, processed: ++self.persisted});
|
return self.deferred.notify({phase: "copying", totalObjects: self.clones.length, processed: ++self.persisted});
|
||||||
});
|
});
|
||||||
})).then(function(){
|
}));
|
||||||
return objectClones;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will add a list of clones to the specified parent's composition
|
* Will add a list of clones to the specified parent's composition
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
CopyTask.prototype.addClonesToParent = function(clones) {
|
CopyTask.prototype.addClonesToParent = function() {
|
||||||
var parentClone = clones[clones.length-1],
|
var parentClone = this.clones[this.clones.length-1],
|
||||||
self = this;
|
self = this;
|
||||||
|
|
||||||
if (!this.parent.hasCapability('composition')){
|
if (!this.parent.hasCapability('composition')){
|
||||||
@@ -77,6 +105,61 @@ define(
|
|||||||
// Ensure the clone of the original domainObject is returned
|
// Ensure the clone of the original domainObject is returned
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an array of objects composed by a parent, clone them, then
|
||||||
|
* add them to the parent.
|
||||||
|
* @private
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
CopyTask.prototype.copyComposees = function(composees, clonedParent, originalParent){
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return (composees || []).reduce(function(promise, composee){
|
||||||
|
//If the composee is composed of other
|
||||||
|
// objects, chain a promise..
|
||||||
|
return promise.then(function(){
|
||||||
|
// ...to recursively copy it (and its children)
|
||||||
|
return self.copy(composee, originalParent).then(function(composee){
|
||||||
|
composeChild(composee, clonedParent)
|
||||||
|
});
|
||||||
|
});}, self.$q.when(undefined)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A recursive function that will perform a bottom-up copy of
|
||||||
|
* the object tree with originalObject at the root. Recurses to
|
||||||
|
* the farthest leaf, then works its way back up again,
|
||||||
|
* cloning objects, and composing them with their child clones
|
||||||
|
* as it goes
|
||||||
|
* @private
|
||||||
|
* @param originalObject
|
||||||
|
* @param originalParent
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
CopyTask.prototype.copy = function(originalObject, originalParent) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
//Make a clone of the model of the object to be copied
|
||||||
|
var modelClone = {
|
||||||
|
id: uuid(),
|
||||||
|
model: cloneObjectModel(originalObject.getModel()),
|
||||||
|
persistenceSpace: originalParent.hasCapability('persistence') && originalParent.getCapability('persistence').getSpace()
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.$q.when(originalObject.useCapability('composition')).then(function(composees){
|
||||||
|
self.deferred.notify({phase: "preparing"});
|
||||||
|
//Duplicate the object's children, and their children, and
|
||||||
|
// so on down to the leaf nodes of the tree.
|
||||||
|
return self.copyComposees(composees, modelClone, originalObject).then(function (){
|
||||||
|
//Add the clone to the list of clones that will
|
||||||
|
//be returned by this function
|
||||||
|
self.clones.push(modelClone);
|
||||||
|
return modelClone;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will build a graph of an object and all of its child objects in
|
* Will build a graph of an object and all of its child objects in
|
||||||
* memory
|
* memory
|
||||||
@@ -90,66 +173,18 @@ define(
|
|||||||
* references to their own children.
|
* references to their own children.
|
||||||
*/
|
*/
|
||||||
CopyTask.prototype.buildCopyPlan = function() {
|
CopyTask.prototype.buildCopyPlan = function() {
|
||||||
var clones = [],
|
var self = this;
|
||||||
$q = this.$q,
|
|
||||||
self = this;
|
|
||||||
|
|
||||||
function makeClone(object) {
|
return this.copy(self.domainObject, self.parent).then(function(domainObjectClone){
|
||||||
return JSON.parse(JSON.stringify(object));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A recursive function that will perform a bottom-up copy of
|
|
||||||
* the object tree with originalObject at the root. Recurses to
|
|
||||||
* the farthest leaf, then works its way back up again,
|
|
||||||
* cloning objects, and composing them with their child clones
|
|
||||||
* as it goes
|
|
||||||
* @param originalObject
|
|
||||||
* @param originalParent
|
|
||||||
* @returns {*}
|
|
||||||
*/
|
|
||||||
function copy(originalObject, originalParent) {
|
|
||||||
//Make a clone of the model of the object to be copied
|
|
||||||
var modelClone = {
|
|
||||||
id: uuid(),
|
|
||||||
model: makeClone(originalObject.getModel()),
|
|
||||||
persistenceSpace: originalParent.hasCapability('persistence') && originalParent.getCapability('persistence').getSpace()
|
|
||||||
};
|
|
||||||
|
|
||||||
delete modelClone.model.composition;
|
|
||||||
delete modelClone.model.persisted;
|
|
||||||
delete modelClone.model.modified;
|
|
||||||
|
|
||||||
return $q.when(originalObject.useCapability('composition')).then(function(composees){
|
|
||||||
self.deferred.notify({phase: "preparing"});
|
|
||||||
return (composees || []).reduce(function(promise, composee){
|
|
||||||
//If the object is composed of other
|
|
||||||
// objects, chain a promise..
|
|
||||||
return promise.then(function(){
|
|
||||||
// ...to recursively copy it (and its children)
|
|
||||||
return copy(composee, originalObject).then(function(composeeClone){
|
|
||||||
//Once copied, associate each cloned
|
|
||||||
// composee with its parent clone
|
|
||||||
composeeClone.model.location = modelClone.id;
|
|
||||||
modelClone.model.composition = modelClone.model.composition || [];
|
|
||||||
return modelClone.model.composition.push(composeeClone.id);
|
|
||||||
});
|
|
||||||
});}, $q.when(undefined)
|
|
||||||
).then(function (){
|
|
||||||
//Add the clone to the list of clones that will
|
|
||||||
//be returned by this function
|
|
||||||
clones.push(modelClone);
|
|
||||||
return modelClone;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return copy(self.domainObject, self.parent).then(function(domainObjectClone){
|
|
||||||
domainObjectClone.model.location = self.parent.getId();
|
domainObjectClone.model.location = self.parent.getId();
|
||||||
return clones;
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the copy task with the objects provided in the constructor.
|
||||||
|
* @returns {promise} Which will resolve with a clone of the object
|
||||||
|
* once complete.
|
||||||
|
*/
|
||||||
CopyTask.prototype.perform = function(){
|
CopyTask.prototype.perform = function(){
|
||||||
var persistObjects = this.persistObjects.bind(this),
|
var persistObjects = this.persistObjects.bind(this),
|
||||||
addClonesToParent = this.addClonesToParent.bind(this);
|
addClonesToParent = this.addClonesToParent.bind(this);
|
||||||
|
|||||||
@@ -342,7 +342,7 @@ define(
|
|||||||
expect(copyFinished.mostRecentCall.args[0].model.modified).toBeUndefined();
|
expect(copyFinished.mostRecentCall.args[0].model.modified).toBeUndefined();
|
||||||
expect(copyFinished.mostRecentCall.args[0].model.persisted).toBe(mockNow.now());
|
expect(copyFinished.mostRecentCall.args[0].model.persisted).toBe(mockNow.now());
|
||||||
});
|
});
|
||||||
|
|
||||||
it ("correctly locates cloned objects", function() {
|
it ("correctly locates cloned objects", function() {
|
||||||
expect(mockPersistenceService.createObject.calls[0].args[2].location).toEqual(mockPersistenceService.createObject.calls[1].args[1]);
|
expect(mockPersistenceService.createObject.calls[0].args[2].location).toEqual(mockPersistenceService.createObject.calls[1].args[1]);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user