diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -8,10 +8,10 @@ return array( 'names' => array( 'core.pkg.css' => 'f7d01efc', - 'core.pkg.js' => 'd3f3a35c', + 'core.pkg.js' => '5d19843a', 'darkconsole.pkg.js' => '8ab24e01', 'differential.pkg.css' => '3500921f', - 'differential.pkg.js' => 'c0506961', + 'differential.pkg.js' => '7e35f6a9', 'diffusion.pkg.css' => '591664fa', 'diffusion.pkg.js' => '0115b37c', 'maniphest.pkg.css' => '68d4dd3d', @@ -442,7 +442,7 @@ 'rsrc/js/application/uiexample/gesture-example.js' => '558829c2', 'rsrc/js/application/uiexample/notification-example.js' => '8ce821c5', 'rsrc/js/core/Busy.js' => '59a7976a', - 'rsrc/js/core/DragAndDropFileUpload.js' => '7fa4b248', + 'rsrc/js/core/DragAndDropFileUpload.js' => '83454d71', 'rsrc/js/core/DraggableList.js' => 'a16ec1c6', 'rsrc/js/core/FileUpload.js' => '477359c8', 'rsrc/js/core/Hovercard.js' => '7e8468ae', @@ -468,7 +468,7 @@ 'rsrc/js/core/behavior-file-tree.js' => '88236f00', 'rsrc/js/core/behavior-form.js' => '5c54cbf3', 'rsrc/js/core/behavior-gesture.js' => '3ab51e2c', - 'rsrc/js/core/behavior-global-drag-and-drop.js' => 'bbdf75ca', + 'rsrc/js/core/behavior-global-drag-and-drop.js' => '0ad0f872', 'rsrc/js/core/behavior-high-security-warning.js' => '8fc1c918', 'rsrc/js/core/behavior-history-install.js' => '7ee2b591', 'rsrc/js/core/behavior-hovercard.js' => 'f36e01af', @@ -593,7 +593,7 @@ 'javelin-behavior-durable-column' => '657c2b50', 'javelin-behavior-error-log' => '6882e80a', 'javelin-behavior-fancy-datepicker' => 'c51ae228', - 'javelin-behavior-global-drag-and-drop' => 'bbdf75ca', + 'javelin-behavior-global-drag-and-drop' => '0ad0f872', 'javelin-behavior-herald-rule-editor' => '7ebaeed3', 'javelin-behavior-high-security-warning' => '8fc1c918', 'javelin-behavior-history-install' => '7ee2b591', @@ -726,7 +726,7 @@ 'phabricator-core-css' => '76e8ee93', 'phabricator-countdown-css' => '86b7b0a0', 'phabricator-dashboard-css' => '17937d22', - 'phabricator-drag-and-drop-file-upload' => '7fa4b248', + 'phabricator-drag-and-drop-file-upload' => '83454d71', 'phabricator-draggable-list' => 'a16ec1c6', 'phabricator-fatal-config-template-css' => '8e6c6fcd', 'phabricator-feed-css' => 'b513b5f4', @@ -894,6 +894,13 @@ 'javelin-behavior-device', 'javelin-vector', ), + '0ad0f872' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-uri', + 'javelin-mask', + 'phabricator-drag-and-drop-file-upload', + ), '0c6946e7' => array( 'javelin-install', 'javelin-dom', @@ -1424,14 +1431,6 @@ 'javelin-behavior', 'javelin-history', ), - '7fa4b248' => array( - 'javelin-install', - 'javelin-util', - 'javelin-request', - 'javelin-dom', - 'javelin-uri', - 'phabricator-file-upload', - ), 82439934 => array( 'javelin-behavior', 'javelin-dom', @@ -1440,6 +1439,14 @@ 'javelin-workflow', 'phabricator-draggable-list', ), + '83454d71' => array( + 'javelin-install', + 'javelin-util', + 'javelin-request', + 'javelin-dom', + 'javelin-uri', + 'phabricator-file-upload', + ), '834a1173' => array( 'javelin-behavior', 'javelin-scrollbar', @@ -1722,13 +1729,6 @@ 'javelin-stratcom', 'javelin-dom', ), - 'bbdf75ca' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-uri', - 'javelin-mask', - 'phabricator-drag-and-drop-file-upload', - ), 'bc965352' => array( 'javelin-behavior', 'javelin-typeahead-ondemand-source', diff --git a/src/applications/base/controller/PhabricatorController.php b/src/applications/base/controller/PhabricatorController.php --- a/src/applications/base/controller/PhabricatorController.php +++ b/src/applications/base/controller/PhabricatorController.php @@ -57,6 +57,10 @@ return false; } + public function isGlobalDragAndDropUploadEnabled() { + return false; + } + public function willBeginExecution() { $request = $this->getRequest(); diff --git a/src/applications/files/controller/PhabricatorFileListController.php b/src/applications/files/controller/PhabricatorFileListController.php --- a/src/applications/files/controller/PhabricatorFileListController.php +++ b/src/applications/files/controller/PhabricatorFileListController.php @@ -2,19 +2,17 @@ final class PhabricatorFileListController extends PhabricatorFileController { - private $key; - public function shouldAllowPublic() { return true; } - public function willProcessRequest(array $data) { - $this->key = idx($data, 'key'); + public function isGlobalDragAndDropUploadEnabled() { + return true; } - public function processRequest() { + public function handleRequest(AphrontRequest $request) { $controller = id(new PhabricatorApplicationSearchController()) - ->setQueryKey($this->key) + ->setQueryKey($request->getURIData('key')) ->setSearchEngine(new PhabricatorFileSearchEngine()) ->setNavigation($this->buildSideNavView()); diff --git a/src/applications/files/controller/PhabricatorFileUploadController.php b/src/applications/files/controller/PhabricatorFileUploadController.php --- a/src/applications/files/controller/PhabricatorFileUploadController.php +++ b/src/applications/files/controller/PhabricatorFileUploadController.php @@ -2,8 +2,11 @@ final class PhabricatorFileUploadController extends PhabricatorFileController { - public function processRequest() { - $request = $this->getRequest(); + public function isGlobalDragAndDropUploadEnabled() { + return true; + } + + public function handleRequest(AphrontRequest $request) { $viewer = $request->getUser(); $file = PhabricatorFile::initializeNewFile(); diff --git a/src/applications/files/view/PhabricatorGlobalUploadTargetView.php b/src/applications/files/view/PhabricatorGlobalUploadTargetView.php --- a/src/applications/files/view/PhabricatorGlobalUploadTargetView.php +++ b/src/applications/files/view/PhabricatorGlobalUploadTargetView.php @@ -1,5 +1,16 @@ only = idx($data, 'only'); + public function isGlobalDragAndDropUploadEnabled() { + return true; } - public function processRequest() { - $user = $this->getRequest()->getUser(); + public function handleRequest(AphrontRequest $request) { + $user = $request->getUser(); $dashboard = PhabricatorDashboardInstall::getDashboard( $user, @@ -42,7 +41,7 @@ $content = $this->buildMainResponse($projects); } - if (!$this->only) { + if (!$request->getURIData('only')) { $nav = $this->buildNav(); $nav->appendChild( array( diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -605,6 +605,7 @@ private function buildQuicksandConfig() { $viewer = $this->getRequest()->getUser(); + $controller = $this->getController(); $dropdown_query = id(new AphlictDropdownDataQuery()) ->setViewer($viewer); @@ -624,7 +625,7 @@ $rendered_dropdowns[$application_class] = $application->buildMainMenuExtraNodes( $viewer, - $this->getController()); + $controller); } $console_config = null; @@ -638,6 +639,7 @@ $dropdown_query->getNotificationData(), $dropdown_query->getConpherenceData(), ), + 'globalDragAndDrop' => $controller->isGlobalDragAndDropUploadEnabled(), 'aphlictDropdowns' => $rendered_dropdowns, 'consoleConfig' => $console_config, ) + $this->buildAphlictListenConfigData(); diff --git a/webroot/rsrc/js/core/DragAndDropFileUpload.js b/webroot/rsrc/js/core/DragAndDropFileUpload.js --- a/webroot/rsrc/js/core/DragAndDropFileUpload.js +++ b/webroot/rsrc/js/core/DragAndDropFileUpload.js @@ -40,6 +40,7 @@ members : { _node : null, _depth : 0, + _listeners: null, _updateDepth : function(delta) { if (this._depth === 0 && delta > 0) { this.invoke('didBeginDrag'); @@ -66,9 +67,12 @@ return false; } + // cache all the listeners we install so we can remove them later + this._listeners = []; + // Firefox has some issues sometimes; implement this click handler so // the user can recover. See T5188. - JX.DOM.listen( + this._listeners.push(JX.DOM.listen( this._node, 'click', null, @@ -78,11 +82,11 @@ // Force depth to 0. this._updateDepth(-this._depth); } - })); + }))); // We track depth so that the _node may have children inside of it and // not become unselected when they are dragged over. - JX.DOM.listen( + this._listeners.push(JX.DOM.listen( this._node, 'dragenter', null, @@ -90,9 +94,9 @@ if (contains(this._node, e.getTarget())) { this._updateDepth(1); } - })); + }))); - JX.DOM.listen( + this._listeners.push(JX.DOM.listen( this._node, 'dragleave', null, @@ -100,9 +104,9 @@ if (contains(this._node, e.getTarget())) { this._updateDepth(-1); } - })); + }))); - JX.DOM.listen( + this._listeners.push(JX.DOM.listen( this._node, 'dragover', null, @@ -111,9 +115,9 @@ // download shelf. e.getRawEvent().dataTransfer.dropEffect = 'copy'; e.kill(); - }); + })); - JX.DOM.listen( + this._listeners.push(JX.DOM.listen( this._node, 'drop', null, @@ -127,10 +131,10 @@ // Force depth to 0. this._updateDepth(-this._depth); - })); + }))); if (JX.PhabricatorDragAndDropFileUpload.isPasteSupported()) { - JX.DOM.listen( + this._listeners.push(JX.DOM.listen( this._node, 'paste', null, @@ -166,7 +170,15 @@ } this._sendRequest(spec); } - })); + }))); + } + }, + + stop: function() { + var listener; + while (this._listeners.length) { + listener = this._listeners.pop(); + listener.remove(); } }, diff --git a/webroot/rsrc/js/core/behavior-global-drag-and-drop.js b/webroot/rsrc/js/core/behavior-global-drag-and-drop.js --- a/webroot/rsrc/js/core/behavior-global-drag-and-drop.js +++ b/webroot/rsrc/js/core/behavior-global-drag-and-drop.js @@ -7,64 +7,104 @@ * phabricator-drag-and-drop-file-upload */ -JX.behavior('global-drag-and-drop', function(config) { +JX.behavior('global-drag-and-drop', function(config, statics) { if (!JX.PhabricatorDragAndDropFileUpload.isSupported()) { return; } - var pending = 0; - var files = []; - var errors = false; + function init() { + statics.pending = 0; + statics.files = []; + statics.errors = false; - if (config.ifSupported) { - JX.$(config.ifSupported).style.display = ''; + if (config.ifSupported) { + JX.$(config.ifSupported).style.display = ''; + } + + var page = JX.$('phabricator-standard-page'); + statics.drop = new JX.PhabricatorDragAndDropFileUpload(page) + .setURI(config.uploadURI) + .setViewPolicy(config.viewPolicy) + .setChunkThreshold(config.chunkThreshold); + + statics.listeners = install_listeners(); + + statics.drop.start(); + + return true; } - var page = JX.$('phabricator-standard-page'); - var drop = new JX.PhabricatorDragAndDropFileUpload(page) - .setURI(config.uploadURI) - .setViewPolicy(config.viewPolicy) - .setChunkThreshold(config.chunkThreshold); - - drop.listen('didBeginDrag', function() { - JX.Mask.show('global-upload-mask'); - JX.DOM.show(JX.$(config.instructions)); - }); - - drop.listen('didEndDrag', function() { - JX.Mask.hide('global-upload-mask'); - JX.DOM.hide(JX.$(config.instructions)); - }); - - drop.listen('willUpload', function() { - pending++; - }); - - drop.listen('didUpload', function(f) { - files.push(f); - - pending--; - 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].getID()); + function install_listeners() { + var list = []; + var listener = statics.drop.listen('didBeginDrag', function() { + JX.Mask.show('global-upload-mask'); + JX.DOM.show(JX.$(config.instructions)); + }); + list.push(listener); + + listener = statics.drop.listen('didEndDrag', function() { + JX.Mask.hide('global-upload-mask'); + JX.DOM.hide(JX.$(config.instructions)); + }); + list.push(listener); + + listener = statics.drop.listen('willUpload', function() { + statics.pending++; + }); + list.push(listener); + + listener = statics.drop.listen('didUpload', function(f) { + statics.files.push(f); + + statics.pending--; + if (statics.pending === 0 && !statics.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 < statics.files.length; ii++) { + ids.push(statics.files[ii].getID()); + } + uri.setQueryParam('h', ids.join(',')); + + statics.files = []; + + uri.go(); } - uri.setQueryParam('h', ids.join(',')); + }); + list.push(listener); - files = []; + listener = statics.drop.listen('didError', function() { + statics.pending--; + statics.errors = true; + }); + list.push(listener); - uri.go(); - } - }); + return list; + } + + statics.init = statics.init || init(); - drop.listen('didError', function() { - pending--; - errors = true; - }); + JX.Stratcom.listen( + 'quicksand-redraw', + null, + function (e) { + e.kill(); + // kill the listeners we added + var remove_listener; + while (statics.listeners.length) { + remove_listener = statics.listeners.pop(); + remove_listener.remove(); + } + // stop the drop from operating + statics.drop.stop(); - drop.start(); + var data = e.getData(); + // ...and if we have global drag and drop on this page + // turn it back on + if (data.newResponse.globalDragAndDrop) { + init(); + } + }); });