Merge "UI: add to set when dropped is a dup but not yet a member of the set"

This commit is contained in:
Mathieu Lonjaret 2015-12-29 18:22:48 +00:00 committed by Gerrit Code Review
commit e67998ee12
2 changed files with 108 additions and 36 deletions

View File

@ -298,7 +298,7 @@ cam.IndexPage = React.createClass({
totalBytesComplete: completedBytes
});
console.log('Uploaded %d of %d bytes', completedBytes, this.state.totalBytesToUpload);
console.log('Completed %d of %d bytes', completedBytes, this.state.totalBytesToUpload);
},
onUploadComplete_: function() {
@ -319,16 +319,18 @@ cam.IndexPage = React.createClass({
var files = e.nativeEvent.dataTransfer.files;
var sc = this.props.serverConnection;
var parent = this.getTargetBlobref_();
this.onUploadStart_(files);
goog.labs.Promise.all(
Array.prototype.map.call(files, function(file) {
return uploadFile(file)
.then(fetchExistingPermanode)
.then(fetchPermanodeIfExists)
.then(createPermanodeIfNotExists)
.then(nameResults)
.then(createPermanodeAssociations.bind(this))
.then(updatePermanodeRef)
.then(checkExistingCamliMembership)
.then(createPermanodeAssociations)
.thenCatch(function(e) {
console.error('File upload fall down go boom. file: %s, error: %s', file.name, e);
})
@ -339,53 +341,92 @@ cam.IndexPage = React.createClass({
}).then(this.onUploadComplete_);
function uploadFile(file) {
// capture status of upload promise chain
var status = {
fileRef: '',
isCamliMemberOfParent: false,
parentRef: parent,
permanodeRef: '',
permanodeCreated: false
};
var uploadFile = new goog.labs.Promise(sc.uploadFile.bind(sc, file));
return goog.labs.Promise.all([uploadFile]);
return goog.labs.Promise.all([new goog.labs.Promise.resolve(status), uploadFile]);
}
function fetchExistingPermanode(blobIds) {
var fileRef = blobIds[0];
var fileUploaded = new goog.labs.Promise.resolve(fileRef);
var getPermanode = new goog.labs.Promise(sc.getPermanodeWithContent.bind(sc, fileRef));
return goog.labs.Promise.all([fileUploaded, getPermanode]);
function fetchPermanodeIfExists(results) {
var status = results[0];
status.fileRef = results[1];
var getPermanode = new goog.labs.Promise(sc.getPermanodeWithContent.bind(sc, status.fileRef));
return goog.labs.Promise.all([new goog.labs.Promise.resolve(status), getPermanode]);
}
function createPermanodeIfNotExists(results) {
var fileRef = results[0];
var permanode = results[1];
if (!permanode) {
var fileUploaded = new goog.labs.Promise.resolve(fileRef);
var status = results[0];
var permanodeRef = results[1];
if (!permanodeRef) {
status.permanodeCreated = true;
var createPermanode = new goog.labs.Promise(sc.createPermanode.bind(sc));
return goog.labs.Promise.all([fileUploaded, createPermanode]);
return goog.labs.Promise.all([new goog.labs.Promise.resolve(status), createPermanode]);
}
// Empty values so the next in chain knows that we're in the "permanode already exists" case.
return goog.labs.Promise.resolve(["", ""]);
return goog.labs.Promise.all([new goog.labs.Promise.resolve(status), new goog.labs.Promise.resolve(permanodeRef)]);
}
// 'readable-ify' the blob references returned from upload/create
function nameResults(blobIds) {
return {
'fileRef': blobIds[0],
'permanodeRef': blobIds[1]
};
function updatePermanodeRef(results) {
var status = results[0];
status.permanodeRef = results[1];
return goog.labs.Promise.all([new goog.labs.Promise.resolve(status)]);
}
function createPermanodeAssociations(refs) {
if (refs.permanodeRef == "") {
// Any value would do, but boolean helps make it clear that we end
// here, by resolving the file upload promise chain.
return goog.labs.Promise.resolve(true);
// TODO(mpl): this implementation means that when we're dropping on a set, we send
// one additional query for each permanode that already exists. So in the worst case,
// it amounts to one additional query per dropped item (with a small payload/response).
// Alternatively, we could ask (either by tweaking the search session, or
// "manually") the server for all the set members and cache the response, which means
// only one additional query, and we can then do all the tests locally. However, the
// response size scales with the number of members in the set, so I don't know if it's
// better. A working example is at
// https://camlistore-review.googlesource.com/#/c/5345/2 . We should benchmark and/or
// ask Brad.
// check, when appropriate, if the permanode is already part of the set we're dropping in.
function checkExistingCamliMembership(results) {
var status = results[0];
// Permanode did not exist before, so it couldn't be a member of any set.
if (!status.parentRef || status.permanodeCreated) {
return goog.labs.Promise.all([new goog.labs.Promise.resolve(status), new goog.labs.Promise.resolve(false)]);
}
console.log('checking membership');
var hasMembership = new goog.labs.Promise(sc.isCamliMember.bind(sc, status.permanodeRef, status.parentRef));
return goog.labs.Promise.all([new goog.labs.Promise.resolve(status), hasMembership]);
}
function createPermanodeAssociations(results) {
var status = results[0];
status.isCamliMemberOfParent = results[1];
var promises = [];
// associate uploaded file to new permanode
var camliContent = new goog.labs.Promise(sc.newSetAttributeClaim.bind(sc, refs.permanodeRef, 'camliContent', refs.fileRef));
var promises = [camliContent];
if (status.permanodeCreated) {
var setCamliContent = new goog.labs.Promise(sc.newSetAttributeClaim.bind(sc, status.permanodeRef, 'camliContent', status.fileRef));
promises.push(setCamliContent);
}
// if currently viewing a set, make new permanode a member of the set
var parentPermanodeRef = this.getTargetBlobref_();
if (parentPermanodeRef) {
var camliMember = new goog.labs.Promise(sc.newAddAttributeClaim.bind(sc, parentPermanodeRef, 'camliMember', refs.permanodeRef));
promises.push(camliMember);
// add CamliMember relationship if viewing a set
if (status.parentRef && !status.isCamliMemberOfParent) {
var setCamliMember = new goog.labs.Promise(sc.newAddAttributeClaim.bind(sc, status.parentRef, 'camliMember', status.permanodeRef));
promises.push(setCamliMember);
}
return goog.labs.Promise.all(promises);

View File

@ -60,7 +60,7 @@ cam.ServerConnection.DESCRIBE_REQUEST = {
]
};
cam.ServerConnection.prototype.getPermanodeWithContent = function(contentRef, success, opt_fail) {
cam.ServerConnection.prototype.getPermanodeWithContent = function(contentRef, success) {
var query = {
permanode: {
attr: "camliContent",
@ -77,6 +77,37 @@ cam.ServerConnection.prototype.getPermanodeWithContent = function(contentRef, su
this.search(query, null, null, null, callback);
};
// If child is a camliMember of parent success is called with 'true', otherrwise 'false'
// @param {string} blobref of the child
// @param {string} blobref of the parent
// @param {Function} success callback with data.
cam.ServerConnection.prototype.isCamliMember = function(child, parent, success) {
var query = {
logical: {
a: {
permanode: {
attr: "camliMember",
ValueInSet: {
blobRefPrefix: child,
}
}
},
op: "and",
b: {
blobRefPrefix: parent,
}
},
};
var callback = function(result) {
if (!result || !result.blobs || result.blobs.length == 0) {
success(false);
return;
}
success(true);
}
this.search(query, null, null, null, callback);
};
cam.ServerConnection.prototype.getWorker_ = function() {
if (!this.worker_) {
var r = new Date().getTime(); // For cachebusting the worker. Sigh. We need content stamping.