Show upload status, success and failures for drag-and-drop files in notifications

Summary: Currently, we do a poor job of communicating drag-and-drop upload errors. Show progress and success/failure in notifications.

Test Plan:
{F20671}

{F20672}

Uploaded files to maniphest attachments, remarkup text areas, Files tool.

Reviewers: btrahan, vrana

Reviewed By: vrana

CC: aran

Differential Revision: https://secure.phabricator.com/D3655
This commit is contained in:
epriestley
2012-10-08 15:23:05 -07:00
parent e00d3b72fe
commit 2bc9ac5e2e
7 changed files with 267 additions and 71 deletions

View File

@@ -38,3 +38,13 @@
background: #ffa0ff;
border: 1px solid #aa60aa;
}
.jx-notification-done {
background: #d0ffd0;
border: 1px solid #60aa60;
}
.jx-notification-error {
background: #ffd0d0;
border: 1px solid #aa6060;
}

View File

@@ -4,6 +4,7 @@
* javelin-request
* javelin-dom
* javelin-uri
* phabricator-file-upload
* @provides phabricator-drag-and-drop-file-upload
* @javelin
*/
@@ -14,7 +15,7 @@ JX.install('PhabricatorDragAndDropFileUpload', {
this._node = node;
},
events : ['willUpload', 'didUpload'],
events : ['willUpload', 'progress', 'didUpload', 'didError'],
statics : {
isSupported : function() {
@@ -88,24 +89,80 @@ JX.install('PhabricatorDragAndDropFileUpload', {
var files = e.getRawEvent().dataTransfer.files;
for (var ii = 0; ii < files.length; ii++) {
var file = files[ii];
this.invoke('willUpload', file);
var up_uri = JX.$U(this.getURI())
.setQueryParam('name', file.name)
.toString();
new JX.Request(up_uri, JX.bind(this, function(r) {
this.invoke('didUpload', r);
}))
.setFile(file)
.send();
this._sendRequest(files[ii]);
}
// Force depth to 0.
this._updateDepth(-this._depth);
}));
},
_sendRequest : function(spec) {
var file = new JX.PhabricatorFileUpload()
.setName(spec.name)
.setTotalBytes(spec.size)
.setStatus('uploading')
.update();
this.invoke('willUpload', file);
var up_uri = JX.$U(this.getURI())
.setQueryParam('name', file.getName())
.toString();
var onupload = JX.bind(this, function(r) {
if (r.error) {
file
.setStatus('error')
.setError(r.error)
.update();
this.invoke('didError', file);
} else {
file
.setID(r.id)
.setPHID(r.phid)
.setURI(r.uri)
.setMarkup(r.html)
.setStatus('done')
.update();
this.invoke('didUpload', file);
}
});
var req = new JX.Request(up_uri, onupload);
var onerror = JX.bind(this, function(error) {
file.setStatus('error');
if (error) {
file.setError(error.code + ': ' + error.info);
} else {
var xhr = req.getTransport();
if (xhr.responseText) {
file.setError('Server responded: ' + xhr.responseText);
}
}
file.update();
this.invoke('didError', file);
});
var onprogress = JX.bind(this, function(progress) {
file
.setTotalBytes(progress.total)
.setUploadedBytes(progress.loaded)
.update();
this.invoke('progress', file);
});
req.listen('error', onerror);
req.listen('uploadprogress', onprogress);
req
.setFile(spec)
.send();
}
},
properties: {

View File

@@ -0,0 +1,111 @@
/**
* @requires javelin-install
* @provides phabricator-file-upload
* @javelin
*/
JX.install('PhabricatorFileUpload', {
construct : function() {
this._notification = new JX.Notification();
},
properties : {
name : null,
totalBytes : null,
uploadedBytes : null,
ID : null,
PHID : null,
URI : null,
status : null,
markup : null,
error : null
},
members : {
_notification : null,
update : function() {
if (!this._notification) {
return;
}
this._notification
.setDuration(0)
.show();
switch (this.getStatus()) {
case 'done':
var link = JX.$N('a', {href: this.getURI()}, 'F' + this.getID());
var content = [
JX.$N('strong', {}, ['Upload Complete (', link, ')']),
JX.$N('br'),
this.getName()
];
this._notification
.setContent(content)
.alterClassName('jx-notification-done', true)
.setDuration(12000);
this._notification = null;
break;
case 'error':
var content = [
JX.$N('strong', {}, 'Upload Failure'),
JX.$N('br'),
this.getName(),
JX.$N('br'),
JX.$N('br'),
this.getError()
];
this._notification
.setContent(content)
.alterClassName('jx-notification-error', true);
this._notification = null;
break;
default:
var info = '';
if (this.getTotalBytes()) {
var p = this._renderPercentComplete();
var f = this._renderFileSize();
info = ' (' + p + ' of ' + f + ')';
}
info = 'Uploading "' + this.getName() + '"' + info + '...';
this._notification
.setContent(info);
break;
}
return this;
},
_renderPercentComplete : function() {
if (!this.getTotalBytes()) {
return null;
}
var ratio = this.getUploadedBytes() / this.getTotalBytes();
return parseInt(100 * ratio) + '%';
},
_renderFileSize : function() {
if (!this.getTotalBytes()) {
return null;
}
var s = 3;
var n = this.getTotalBytes();
while (s && n >= 1000) {
n = Math.round(n / 100);
n = n / 10;
s--;
}
s = ['GB', 'MB', 'KB', 'bytes'][s];
return n + ' ' + s;
}
}
});

View File

@@ -12,7 +12,7 @@ JX.behavior('aphront-drag-and-drop-textarea', function(config) {
var target = JX.$(config.target);
function onupload(f) {
JX.TextAreaUtils.setSelectionText(target, '{F' + f.id + '}');
JX.TextAreaUtils.setSelectionText(target, '{F' + f.getID() + '}');
}
if (JX.PhabricatorDragAndDropFileUpload.isSupported()) {

View File

@@ -32,7 +32,7 @@ JX.behavior('aphront-drag-and-drop', function(config) {
});
drop.listen('didUpload', function(f) {
files[f.phid] = f;
files[f.getPHID()] = f;
// This redraws "Upload complete!"
pending--;
@@ -59,13 +59,13 @@ JX.behavior('aphront-drag-and-drop', function(config) {
var items = [];
for (var k in files) {
var file = files[k];
items.push(JX.$N('div', {}, JX.$H(file.html)));
items.push(JX.$N('div', {}, JX.$H(file.getMarkup())));
items.push(JX.$N(
'input',
{
type: "hidden",
name: config.name + "[" + file.phid + "]",
value: file.phid
name: config.name + "[" + file.getPHID() + "]",
value: file.getPHID()
}));
}

View File

@@ -16,6 +16,7 @@ JX.behavior('files-drag-and-drop', function(config) {
var pending = 0;
var files = [];
var errors = false;
var control = JX.$(config.control);
// Show the control, since we have browser support.
@@ -34,14 +35,14 @@ JX.behavior('files-drag-and-drop', function(config) {
files.push(f);
pending--;
if (pending == 0) {
if (pending == 0 && !errors) {
// If whatever the user dropped in has finished uploading, send them to
// their uploads.
var uri;
uri = JX.$U(config.browseURI);
var ids = [];
for (var ii = 0; ii < files.length; ii++) {
ids.push(files[ii].id);
ids.push(files[ii].getID());
}
uri.setQueryParam('h', ids.join('-'));
@@ -56,6 +57,12 @@ JX.behavior('files-drag-and-drop', function(config) {
}
});
drop.listen('didError', function(f) {
pending--;
errors = true;
redraw();
});
drop.start();
redraw();