1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-22 23:02:42 +01:00

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

@ -916,7 +916,7 @@ celerity_register_resource_map(array(
),
'javelin-behavior-aphront-drag-and-drop' =>
array(
'uri' => '/res/724c922b/rsrc/js/application/core/behavior-drag-and-drop.js',
'uri' => '/res/0910fc0a/rsrc/js/application/core/behavior-drag-and-drop.js',
'type' => 'js',
'requires' =>
array(
@ -929,7 +929,7 @@ celerity_register_resource_map(array(
),
'javelin-behavior-aphront-drag-and-drop-textarea' =>
array(
'uri' => '/res/99f58821/rsrc/js/application/core/behavior-drag-and-drop-textarea.js',
'uri' => '/res/ad737ce4/rsrc/js/application/core/behavior-drag-and-drop-textarea.js',
'type' => 'js',
'requires' =>
array(
@ -1301,7 +1301,7 @@ celerity_register_resource_map(array(
),
'javelin-behavior-files-drag-and-drop' =>
array(
'uri' => '/res/3a7a2a8a/rsrc/js/application/core/behavior-files-drag-and-drop.js',
'uri' => '/res/4893f577/rsrc/js/application/core/behavior-files-drag-and-drop.js',
'type' => 'js',
'requires' =>
array(
@ -2361,7 +2361,7 @@ celerity_register_resource_map(array(
),
'phabricator-drag-and-drop-file-upload' =>
array(
'uri' => '/res/63a06ad9/rsrc/js/application/core/DragAndDropFileUpload.js',
'uri' => '/res/0db5b9d5/rsrc/js/application/core/DragAndDropFileUpload.js',
'type' => 'js',
'requires' =>
array(
@ -2370,6 +2370,7 @@ celerity_register_resource_map(array(
2 => 'javelin-request',
3 => 'javelin-dom',
4 => 'javelin-uri',
5 => 'phabricator-file-upload',
),
'disk' => '/rsrc/js/application/core/DragAndDropFileUpload.js',
),
@ -2397,6 +2398,16 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/css/application/feed/feed.css',
),
'phabricator-file-upload' =>
array(
'uri' => '/res/98503231/rsrc/js/application/core/FileUpload.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
),
'disk' => '/rsrc/js/application/core/FileUpload.js',
),
'phabricator-filetree-view-css' =>
array(
'uri' => '/res/be0ab498/rsrc/css/layout/phabricator-filetree-view.css',
@ -2512,7 +2523,7 @@ celerity_register_resource_map(array(
),
'phabricator-notification-css' =>
array(
'uri' => '/res/77e8c821/rsrc/css/aphront/notification.css',
'uri' => '/res/91197237/rsrc/css/aphront/notification.css',
'type' => 'css',
'requires' =>
array(
@ -2636,7 +2647,7 @@ celerity_register_resource_map(array(
),
'phabricator-remarkup-css' =>
array(
'uri' => '/res/66b4cd42/rsrc/css/core/remarkup.css',
'uri' => '/res/2e0d0042/rsrc/css/core/remarkup.css',
'type' => 'css',
'requires' =>
array(
@ -3003,7 +3014,7 @@ celerity_register_resource_map(array(
), array(
'packages' =>
array(
'c88632b0' =>
'315e8536' =>
array(
'name' => 'core.pkg.css',
'symbols' =>
@ -3032,7 +3043,7 @@ celerity_register_resource_map(array(
21 => 'phabricator-flag-css',
22 => 'aphront-error-view-css',
),
'uri' => '/res/pkg/c88632b0/core.pkg.css',
'uri' => '/res/pkg/315e8536/core.pkg.css',
'type' => 'css',
),
'3a455e4f' =>
@ -3084,7 +3095,7 @@ celerity_register_resource_map(array(
'uri' => '/res/pkg/2ba14b3d/differential.pkg.css',
'type' => 'css',
),
'a5cb9310' =>
'a569df32' =>
array(
'name' => 'differential.pkg.js',
'symbols' =>
@ -3107,7 +3118,7 @@ celerity_register_resource_map(array(
15 => 'differential-inline-comment-editor',
16 => 'javelin-behavior-differential-dropdown-menus',
),
'uri' => '/res/pkg/a5cb9310/differential.pkg.js',
'uri' => '/res/pkg/a569df32/differential.pkg.js',
'type' => 'js',
),
'c8ce2d88' =>
@ -3199,23 +3210,23 @@ celerity_register_resource_map(array(
'reverse' =>
array(
'aphront-attached-file-view-css' => '7839ae2d',
'aphront-crumbs-view-css' => 'c88632b0',
'aphront-dialog-view-css' => 'c88632b0',
'aphront-error-view-css' => 'c88632b0',
'aphront-form-view-css' => 'c88632b0',
'aphront-crumbs-view-css' => '315e8536',
'aphront-dialog-view-css' => '315e8536',
'aphront-error-view-css' => '315e8536',
'aphront-form-view-css' => '315e8536',
'aphront-headsup-action-list-view-css' => '2ba14b3d',
'aphront-headsup-view-css' => 'c88632b0',
'aphront-list-filter-view-css' => 'c88632b0',
'aphront-pager-view-css' => 'c88632b0',
'aphront-panel-view-css' => 'c88632b0',
'aphront-side-nav-view-css' => 'c88632b0',
'aphront-table-view-css' => 'c88632b0',
'aphront-tokenizer-control-css' => 'c88632b0',
'aphront-tooltip-css' => 'c88632b0',
'aphront-typeahead-control-css' => 'c88632b0',
'aphront-headsup-view-css' => '315e8536',
'aphront-list-filter-view-css' => '315e8536',
'aphront-pager-view-css' => '315e8536',
'aphront-panel-view-css' => '315e8536',
'aphront-side-nav-view-css' => '315e8536',
'aphront-table-view-css' => '315e8536',
'aphront-tokenizer-control-css' => '315e8536',
'aphront-tooltip-css' => '315e8536',
'aphront-typeahead-control-css' => '315e8536',
'differential-changeset-view-css' => '2ba14b3d',
'differential-core-view-css' => '2ba14b3d',
'differential-inline-comment-editor' => 'a5cb9310',
'differential-inline-comment-editor' => 'a569df32',
'differential-local-commits-view-css' => '2ba14b3d',
'differential-results-table-css' => '2ba14b3d',
'differential-revision-add-comment-css' => '2ba14b3d',
@ -3229,20 +3240,20 @@ celerity_register_resource_map(array(
'inline-comment-summary-css' => '2ba14b3d',
'javelin-behavior' => '6c45a1d8',
'javelin-behavior-aphront-basic-tokenizer' => '81c9cd69',
'javelin-behavior-aphront-drag-and-drop' => 'a5cb9310',
'javelin-behavior-aphront-drag-and-drop-textarea' => 'a5cb9310',
'javelin-behavior-aphront-drag-and-drop' => 'a569df32',
'javelin-behavior-aphront-drag-and-drop-textarea' => 'a569df32',
'javelin-behavior-aphront-form-disable-on-submit' => '3a455e4f',
'javelin-behavior-audit-preview' => '5e68be89',
'javelin-behavior-differential-accept-with-errors' => 'a5cb9310',
'javelin-behavior-differential-add-reviewers-and-ccs' => 'a5cb9310',
'javelin-behavior-differential-comment-jump' => 'a5cb9310',
'javelin-behavior-differential-diff-radios' => 'a5cb9310',
'javelin-behavior-differential-dropdown-menus' => 'a5cb9310',
'javelin-behavior-differential-edit-inline-comments' => 'a5cb9310',
'javelin-behavior-differential-feedback-preview' => 'a5cb9310',
'javelin-behavior-differential-keyboard-navigation' => 'a5cb9310',
'javelin-behavior-differential-populate' => 'a5cb9310',
'javelin-behavior-differential-show-more' => 'a5cb9310',
'javelin-behavior-differential-accept-with-errors' => 'a569df32',
'javelin-behavior-differential-add-reviewers-and-ccs' => 'a569df32',
'javelin-behavior-differential-comment-jump' => 'a569df32',
'javelin-behavior-differential-diff-radios' => 'a569df32',
'javelin-behavior-differential-dropdown-menus' => 'a569df32',
'javelin-behavior-differential-edit-inline-comments' => 'a569df32',
'javelin-behavior-differential-feedback-preview' => 'a569df32',
'javelin-behavior-differential-keyboard-navigation' => 'a569df32',
'javelin-behavior-differential-populate' => 'a569df32',
'javelin-behavior-differential-show-more' => 'a569df32',
'javelin-behavior-diffusion-commit-graph' => '5e68be89',
'javelin-behavior-diffusion-pull-lastmodified' => '5e68be89',
'javelin-behavior-maniphest-batch-selector' => '7707de41',
@ -3252,12 +3263,12 @@ celerity_register_resource_map(array(
'javelin-behavior-maniphest-transaction-preview' => '7707de41',
'javelin-behavior-phabricator-autofocus' => '3a455e4f',
'javelin-behavior-phabricator-keyboard-shortcuts' => '3a455e4f',
'javelin-behavior-phabricator-object-selector' => 'a5cb9310',
'javelin-behavior-phabricator-object-selector' => 'a569df32',
'javelin-behavior-phabricator-oncopy' => '3a455e4f',
'javelin-behavior-phabricator-tooltips' => '3a455e4f',
'javelin-behavior-phabricator-watch-anchor' => '3a455e4f',
'javelin-behavior-refresh-csrf' => '3a455e4f',
'javelin-behavior-repository-crossreference' => 'a5cb9310',
'javelin-behavior-repository-crossreference' => 'a569df32',
'javelin-behavior-workflow' => '3a455e4f',
'javelin-dom' => '6c45a1d8',
'javelin-event' => '6c45a1d8',
@ -3278,15 +3289,15 @@ celerity_register_resource_map(array(
'javelin-workflow' => '3a455e4f',
'maniphest-task-summary-css' => '7839ae2d',
'maniphest-transaction-detail-css' => '7839ae2d',
'phabricator-app-buttons-css' => 'c88632b0',
'phabricator-app-buttons-css' => '315e8536',
'phabricator-content-source-view-css' => '2ba14b3d',
'phabricator-core-buttons-css' => 'c88632b0',
'phabricator-core-css' => 'c88632b0',
'phabricator-directory-css' => 'c88632b0',
'phabricator-drag-and-drop-file-upload' => 'a5cb9310',
'phabricator-core-buttons-css' => '315e8536',
'phabricator-core-css' => '315e8536',
'phabricator-directory-css' => '315e8536',
'phabricator-drag-and-drop-file-upload' => 'a569df32',
'phabricator-dropdown-menu' => '3a455e4f',
'phabricator-flag-css' => 'c88632b0',
'phabricator-jump-nav' => 'c88632b0',
'phabricator-flag-css' => '315e8536',
'phabricator-jump-nav' => '315e8536',
'phabricator-keyboard-shortcut' => '3a455e4f',
'phabricator-keyboard-shortcut-manager' => '3a455e4f',
'phabricator-menu-item' => '3a455e4f',
@ -3294,11 +3305,11 @@ celerity_register_resource_map(array(
'phabricator-paste-file-upload' => '3a455e4f',
'phabricator-prefab' => '3a455e4f',
'phabricator-project-tag-css' => '7839ae2d',
'phabricator-remarkup-css' => 'c88632b0',
'phabricator-shaped-request' => 'a5cb9310',
'phabricator-standard-page-view' => 'c88632b0',
'phabricator-remarkup-css' => '315e8536',
'phabricator-shaped-request' => 'a569df32',
'phabricator-standard-page-view' => '315e8536',
'phabricator-tooltip' => '3a455e4f',
'phabricator-transaction-view-css' => 'c88632b0',
'syntax-highlighting-css' => 'c88632b0',
'phabricator-transaction-view-css' => '315e8536',
'syntax-highlighting-css' => '315e8536',
),
));

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();