diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,8 +7,8 @@ return array( 'names' => array( - 'core.pkg.css' => '22e4fc33', - 'core.pkg.js' => 'f5ba2408', + 'core.pkg.css' => 'f9c94804', + 'core.pkg.js' => '8c184823', 'darkconsole.pkg.js' => 'df001cab', 'differential.pkg.css' => '4a93db37', 'differential.pkg.js' => 'd1443567', @@ -37,7 +37,7 @@ 'rsrc/css/aphront/typeahead.css' => 'a989b5b3', 'rsrc/css/application/auth/auth.css' => '1e655982', 'rsrc/css/application/base/main-menu-view.css' => 'aceca0e9', - 'rsrc/css/application/base/notification-menu.css' => 'cbff1b94', + 'rsrc/css/application/base/notification-menu.css' => '8ae4a008', 'rsrc/css/application/base/phabricator-application-launch-view.css' => '8b7e271d', 'rsrc/css/application/base/standard-page-view.css' => '517cdfb1', 'rsrc/css/application/chatlog/chatlog.css' => '852140ff', @@ -346,9 +346,10 @@ 'rsrc/image/texture/table_header.png' => '5c433037', 'rsrc/image/texture/table_header_hover.png' => '038ec3b9', 'rsrc/image/texture/table_header_tall.png' => 'd56b434f', - 'rsrc/js/application/aphlict/Aphlict.js' => 'da12704d', - 'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => '862ea0fe', + 'rsrc/js/application/aphlict/Aphlict.js' => '4a07e8e3', + 'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => 'f51afce0', 'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => 'a826c925', + 'rsrc/js/application/aphlict/behavior-aphlict-status.js' => '58f7803f', 'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18', 'rsrc/js/application/config/behavior-reorder-fields.js' => '14a827de', 'rsrc/js/application/conpherence/behavior-menu.js' => 'f0a41b9f', @@ -490,7 +491,7 @@ 'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8', 'rsrc/js/phuix/PHUIXActionView.js' => '6e8cefa4', 'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca', - 'rsrc/swf/aphlict.swf' => 'ef64606d', + 'rsrc/swf/aphlict.swf' => 'e5a24c72', ), 'symbols' => array( @@ -539,10 +540,11 @@ 'herald-rule-editor' => '6c9e6fb8', 'herald-test-css' => '778b008e', 'inline-comment-summary-css' => '8cfd34e8', - 'javelin-aphlict' => 'da12704d', + 'javelin-aphlict' => '4a07e8e3', 'javelin-behavior' => '8a3ed18b', - 'javelin-behavior-aphlict-dropdown' => '862ea0fe', + 'javelin-behavior-aphlict-dropdown' => 'f51afce0', 'javelin-behavior-aphlict-listen' => 'a826c925', + 'javelin-behavior-aphlict-status' => '58f7803f', 'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884', 'javelin-behavior-aphront-crop' => 'fa0f4fc2', 'javelin-behavior-aphront-drag-and-drop-textarea' => '92eb531d', @@ -725,7 +727,7 @@ 'phabricator-nav-view-css' => '9283c2df', 'phabricator-notification' => '0c6946e7', 'phabricator-notification-css' => 'ef2c9b34', - 'phabricator-notification-menu-css' => 'cbff1b94', + 'phabricator-notification-menu-css' => '8ae4a008', 'phabricator-object-selector-css' => '029a133d', 'phabricator-phtize' => 'd254d646', 'phabricator-prefab' => '41ed7994', @@ -1190,6 +1192,11 @@ 1 => 'javelin-dom', 2 => 'javelin-reactor-dom', ), + '4a07e8e3' => + array( + 0 => 'javelin-install', + 1 => 'javelin-util', + ), '4d94d9c3' => array( 0 => 'javelin-behavior', @@ -1235,6 +1242,13 @@ 2 => 'javelin-vector', 3 => 'javelin-dom', ), + '58f7803f' => + array( + 0 => 'javelin-behavior', + 1 => 'javelin-aphlict', + 2 => 'phabricator-phtize', + 3 => 'javelin-dom', + ), '59b251eb' => array( 0 => 'javelin-behavior', @@ -1477,16 +1491,6 @@ 3 => 'javelin-workflow', 4 => 'javelin-stratcom', ), - '862ea0fe' => - array( - 0 => 'javelin-behavior', - 1 => 'javelin-request', - 2 => 'javelin-stratcom', - 3 => 'javelin-vector', - 4 => 'javelin-dom', - 5 => 'javelin-uri', - 6 => 'javelin-behavior-device', - ), '880fa5ac' => array( 0 => 'javelin-behavior', @@ -1921,11 +1925,6 @@ 1 => 'javelin-util', 2 => 'javelin-stratcom', ), - 'da12704d' => - array( - 0 => 'javelin-install', - 1 => 'javelin-util', - ), 'dd7e8ef5' => array( 0 => 'javelin-behavior', @@ -2038,6 +2037,16 @@ 5 => 'phuix-action-view', 6 => 'javelin-workflow', ), + 'f51afce0' => + array( + 0 => 'javelin-behavior', + 1 => 'javelin-request', + 2 => 'javelin-stratcom', + 3 => 'javelin-vector', + 4 => 'javelin-dom', + 5 => 'javelin-uri', + 6 => 'javelin-behavior-device', + ), 'f588412e' => array( 0 => 'javelin-behavior', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1803,6 +1803,7 @@ 'PhabricatorNotificationPanelController' => 'applications/notification/controller/PhabricatorNotificationPanelController.php', 'PhabricatorNotificationQuery' => 'applications/notification/PhabricatorNotificationQuery.php', 'PhabricatorNotificationStatusController' => 'applications/notification/controller/PhabricatorNotificationStatusController.php', + 'PhabricatorNotificationStatusView' => 'applications/notification/view/PhabricatorNotificationStatusView.php', 'PhabricatorNotificationTestController' => 'applications/notification/controller/PhabricatorNotificationTestController.php', 'PhabricatorOAuthClientAuthorization' => 'applications/oauthserver/storage/PhabricatorOAuthClientAuthorization.php', 'PhabricatorOAuthClientAuthorizationQuery' => 'applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php', @@ -4631,6 +4632,7 @@ 'PhabricatorNotificationPanelController' => 'PhabricatorNotificationController', 'PhabricatorNotificationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorNotificationStatusController' => 'PhabricatorNotificationController', + 'PhabricatorNotificationStatusView' => 'AphrontTagView', 'PhabricatorNotificationTestController' => 'PhabricatorNotificationController', 'PhabricatorOAuthClientAuthorization' => array( diff --git a/src/applications/notification/controller/PhabricatorNotificationPanelController.php b/src/applications/notification/controller/PhabricatorNotificationPanelController.php --- a/src/applications/notification/controller/PhabricatorNotificationPanelController.php +++ b/src/applications/notification/controller/PhabricatorNotificationPanelController.php @@ -25,16 +25,31 @@ pht('You have no notifications.')); } + $notifications_link = phutil_tag( + 'a', + array( + 'href' => '/notification/', + ), + pht('Notifications')); + + $connection_status = new PhabricatorNotificationStatusView(); + + $header = phutil_tag( + 'div', + array( + 'class' => 'phabricator-notification-header', + ), + array( + $connection_status, + $notifications_link, + )); + $content = hsprintf( - '
%s %s
'. '%s'. - '
%s
', - phutil_tag( - 'a', - array( - 'href' => '/notification/', - ), - pht('Notifications')), + '%s'. + '
%s %s %s
', + $header, + $content, javelin_tag( 'a', array( @@ -43,7 +58,7 @@ 'class' => 'phabricator-notification-clear-all' ), pht('Mark All Read')), - $content, + " \xC2\xB7 ", phutil_tag( 'a', array( diff --git a/src/applications/notification/view/PhabricatorNotificationStatusView.php b/src/applications/notification/view/PhabricatorNotificationStatusView.php new file mode 100644 --- /dev/null +++ b/src/applications/notification/view/PhabricatorNotificationStatusView.php @@ -0,0 +1,34 @@ +getID()) { + $this->setID(celerity_generate_unique_node_id()); + } + + Javelin::initBehavior( + 'aphlict-status', + array( + 'nodeID' => $this->getID(), + 'pht' => array( + 'setup' => pht('Setting Up Client'), + 'start' => pht('Starting Client'), + 'ready' => pht('Ready to Connect'), + 'connecting' => pht('Connecting...'), + 'connected' => pht('Connected'), + 'error' => pht('Connection Error'), + 'client' => pht('Connected Locally'), + + 'error.flash.xdomain' => pht( + 'Unable to connect to Flash Policy Server. Check that the '. + 'notification server is running and port 843 is not firewalled.'), + ), + )); + + return array( + 'class' => 'aphlict-connection-status', + ); + } + +} diff --git a/support/aphlict/client/src/Aphlict.as b/support/aphlict/client/src/Aphlict.as --- a/support/aphlict/client/src/Aphlict.as +++ b/support/aphlict/client/src/Aphlict.as @@ -42,10 +42,6 @@ this.externalInvoke('log', message); } - final protected function setStatus(status:String):void { - this.externalInvoke('status', {type: status}); - } - } } diff --git a/support/aphlict/client/src/AphlictClient.as b/support/aphlict/client/src/AphlictClient.as --- a/support/aphlict/client/src/AphlictClient.as +++ b/support/aphlict/client/src/AphlictClient.as @@ -41,6 +41,7 @@ UncaughtErrorEvent.UNCAUGHT_ERROR, this.uncaughtErrorHandler); + ExternalInterface.marshallExceptions = true; ExternalInterface.addCallback('connect', this.externalConnect); this.setStatus('ready'); @@ -150,6 +151,10 @@ this.externalInvoke('receive', msg); } + public function setStatus(status:String, code:String = null):void { + this.externalInvoke('status', {type: status, code: code}); + } + } } diff --git a/support/aphlict/client/src/AphlictMaster.as b/support/aphlict/client/src/AphlictMaster.as --- a/support/aphlict/client/src/AphlictMaster.as +++ b/support/aphlict/client/src/AphlictMaster.as @@ -48,6 +48,9 @@ private var socket:Socket; private var readBuffer:ByteArray; + private var status:String; + private var statusCode:String; + public function AphlictMaster(server:String, port:Number) { super(); @@ -75,6 +78,8 @@ if (!this.clients[client]) { this.log('Registering client: ' + client); this.clients[client] = new Date().getTime(); + + this.send.send(client, 'setStatus', this.status, this.statusCode); } } @@ -106,6 +111,8 @@ } private function connectToServer():void { + this.setStatusOnClients('connecting'); + var socket:Socket = new Socket(); socket.addEventListener(Event.CONNECT, didConnectSocket); @@ -124,7 +131,7 @@ } private function didConnectSocket(event:Event):void { - this.externalInvoke('connected'); + this.setStatusOnClients('connected'); // Send subscriptions var phids = new Array(); @@ -146,7 +153,15 @@ } private function didSecurityErrorSocket(event:SecurityErrorEvent):void { - this.externalInvoke('error', event.text); + var text = event.text; + + // This is really gross but there doesn't seem to be anything else + // on the object which gives us an error code. + if (text.match(/^Error #2048/)) { + this.setStatusOnClients('error', 'error.flash.xdomain'); + } + + this.error(text); } public function subscribe(client:String, phids:Array):void { @@ -280,6 +295,18 @@ } } + private function setStatusOnClients( + status:String, + code:String = null):void { + + this.status = status; + this.statusCode = code; + + for (var client:String in this.clients) { + this.send.send(client, 'setStatus', status, code); + } + } + } } diff --git a/webroot/rsrc/css/application/base/notification-menu.css b/webroot/rsrc/css/application/base/notification-menu.css --- a/webroot/rsrc/css/application/base/notification-menu.css +++ b/webroot/rsrc/css/application/base/notification-menu.css @@ -97,3 +97,17 @@ padding: 8px; font-size: 12px; } + +.phabricator-notification-menu .aphlict-connection-status { + float: right; + font-weight: normal; + color: {$lightgreytext}; +} + +.aphlict-connection-status .aphlict-connection-status-connected { + color: {$green}; +} + +.aphlict-connection-status .aphlict-connection-status-error { + color: {$red}; +} diff --git a/webroot/rsrc/js/application/aphlict/Aphlict.js b/webroot/rsrc/js/application/aphlict/Aphlict.js --- a/webroot/rsrc/js/application/aphlict/Aphlict.js +++ b/webroot/rsrc/js/application/aphlict/Aphlict.js @@ -28,7 +28,7 @@ construct: function(id, server, port, subscriptions) { if (__DEV__) { if (JX.Aphlict._instance) { - JX.$E('Aphlict object is a singleton!'); + JX.$E('Aphlict object is a singleton.'); } } @@ -36,17 +36,24 @@ this._server = server; this._port = port; this._subscriptions = subscriptions; + this._setStatus('setup'); JX.Aphlict._instance = this; }, + events: ['didChangeStatus'], + members: { _id: null, _server: null, _port: null, _subscriptions: null, + _status: null, + _statusCode: null, start: function(node, uri) { + this._setStatus('start'); + // NOTE: This is grotesque, but seems to work everywhere. node.innerHTML = '' + @@ -72,6 +79,20 @@ this._server, this._port, this._subscriptions); + }, + + getStatus: function() { + return this._status; + }, + + getStatusCode: function() { + return this._statusCode; + }, + + _setStatus: function(status, code) { + this._status = status; + this._statusCode = code || null; + this.invoke('didChangeStatus'); } }, @@ -98,6 +119,7 @@ } if (type == 'status') { + client._setStatus(message.type, message.code); switch (message.type) { case 'ready': client._didStartFlash(); diff --git a/webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js b/webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js --- a/webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js +++ b/webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js @@ -73,6 +73,12 @@ return; } + if (!e.getNode('notification')) { + // User clicked somewhere in the dead area of the menu, like the header + // or footer. + return; + } + // If the user clicked a notification (but missed a link) and it has a // primary URI, go there. var href = e.getNodeData('notification').href; diff --git a/webroot/rsrc/js/application/aphlict/behavior-aphlict-status.js b/webroot/rsrc/js/application/aphlict/behavior-aphlict-status.js new file mode 100644 --- /dev/null +++ b/webroot/rsrc/js/application/aphlict/behavior-aphlict-status.js @@ -0,0 +1,47 @@ +/** + * @provides javelin-behavior-aphlict-status + * @requires javelin-behavior + * javelin-aphlict + * phabricator-phtize + * javelin-dom + * @javelin + */ + +JX.behavior('aphlict-status', function(config) { + var pht = JX.phtize(config.pht); + + function update() { + var client = JX.Aphlict.getInstance(); + if (!client) { + return; + } + + var node; + try { + node = JX.$(config.nodeID); + } catch (ignored) { + return; + } + + var tip = null; + var status = client.getStatus(); + + if (status == 'error') { + tip = pht(client.getStatusCode()); + } + + var status_node = JX.$N( + 'span', + { + className: 'aphlict-connection-status-' + status, + sigil: tip ? 'has-tooltip' : null, + meta: tip ? {tip: tip, align: 'S', size: 300} : {} + }, + pht(status)); + + JX.DOM.setContent(node, status_node); + } + + JX.Aphlict.listen('didChangeStatus', update); + update(); +}); diff --git a/webroot/rsrc/swf/aphlict.swf b/webroot/rsrc/swf/aphlict.swf index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@