diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -8,7 +8,7 @@ return array( 'names' => array( 'core.pkg.css' => 'a2a90172', - 'core.pkg.js' => '328a9980', + 'core.pkg.js' => '218b6c3d', 'darkconsole.pkg.js' => '8ab24e01', 'differential.pkg.css' => '3500921f', 'differential.pkg.js' => 'c0506961', @@ -205,7 +205,7 @@ 'rsrc/externals/javelin/lib/JSON.js' => '69adf288', 'rsrc/externals/javelin/lib/Leader.js' => '331b1611', 'rsrc/externals/javelin/lib/Mask.js' => '8a41885b', - 'rsrc/externals/javelin/lib/Quicksand.js' => '97720512', + 'rsrc/externals/javelin/lib/Quicksand.js' => '517545ab', 'rsrc/externals/javelin/lib/Request.js' => '94b750d2', 'rsrc/externals/javelin/lib/Resource.js' => '44959b73', 'rsrc/externals/javelin/lib/Routable.js' => 'b3e7d692', @@ -350,7 +350,7 @@ 'rsrc/image/texture/table_header_hover.png' => '038ec3b9', 'rsrc/image/texture/table_header_tall.png' => 'd56b434f', 'rsrc/js/application/aphlict/Aphlict.js' => '30a6303c', - 'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => '00def500', + 'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => '01c816ca', 'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => 'b1a59974', 'rsrc/js/application/aphlict/behavior-aphlict-status.js' => 'ea681761', 'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18', @@ -550,7 +550,7 @@ 'inline-comment-summary-css' => 'eb5f8e8c', 'javelin-aphlict' => '30a6303c', 'javelin-behavior' => '61cbc29a', - 'javelin-behavior-aphlict-dropdown' => '00def500', + 'javelin-behavior-aphlict-dropdown' => '01c816ca', 'javelin-behavior-aphlict-listen' => 'b1a59974', 'javelin-behavior-aphlict-status' => 'ea681761', 'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884', @@ -677,7 +677,7 @@ 'javelin-leader' => '331b1611', 'javelin-magical-init' => '3010e992', 'javelin-mask' => '8a41885b', - 'javelin-quicksand' => '97720512', + 'javelin-quicksand' => '517545ab', 'javelin-reactor' => '2b8de964', 'javelin-reactor-dom' => 'c90a04fc', 'javelin-reactor-node-calmer' => '76f4ebed', @@ -842,7 +842,7 @@ 'unhandled-exception-css' => '37d4f9a2', ), 'requires' => array( - '00def500' => array( + '01c816ca' => array( 'javelin-behavior', 'javelin-request', 'javelin-stratcom', @@ -1183,6 +1183,9 @@ 'javelin-typeahead-source', 'javelin-util', ), + '517545ab' => array( + 'javelin-install', + ), '519705ea' => array( 'javelin-install', 'javelin-dom', @@ -1600,9 +1603,6 @@ 'javelin-resource', 'javelin-routable', ), - 97720512 => array( - 'javelin-install', - ), '988040b4' => array( 'javelin-install', 'javelin-dom', 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 @@ -93,6 +93,7 @@ 'AlmanacServiceTransactionQuery' => 'applications/almanac/query/AlmanacServiceTransactionQuery.php', 'AlmanacServiceType' => 'applications/almanac/servicetype/AlmanacServiceType.php', 'AlmanacServiceViewController' => 'applications/almanac/controller/AlmanacServiceViewController.php', + 'AphlictDropdownDataQuery' => 'applications/aphlict/query/AphlictDropdownDataQuery.php', 'Aphront304Response' => 'aphront/response/Aphront304Response.php', 'Aphront400Response' => 'aphront/response/Aphront400Response.php', 'Aphront403Response' => 'aphront/response/Aphront403Response.php', diff --git a/src/applications/aphlict/query/AphlictDropdownDataQuery.php b/src/applications/aphlict/query/AphlictDropdownDataQuery.php new file mode 100644 --- /dev/null +++ b/src/applications/aphlict/query/AphlictDropdownDataQuery.php @@ -0,0 +1,103 @@ +viewer = $viewer; + return $this; + } + + public function getViewer() { + return $this->viewer; + } + + private function setNotificationData(array $data) { + $this->notificationData = $data; + return $this; + } + + public function getNotificationData() { + if ($this->notificationData === null) { + throw new Exception('You must execute() first!'); + } + return $this->notificationData; + } + + private function setConpherenceData(array $data) { + $this->conpherenceData = $data; + return $this; + } + + public function getConpherenceData() { + if ($this->conpherenceData === null) { + throw new Exception('You must execute() first!'); + } + return $this->conpherenceData; + } + + public function execute() { + $viewer = $this->getViewer(); + + $conpherence_app = 'PhabricatorConpherenceApplication'; + $is_c_installed = PhabricatorApplication::isClassInstalledForViewer( + $conpherence_app, + $viewer); + $raw_message_count_number = null; + $message_count_number = null; + if ($is_c_installed) { + $unread_status = ConpherenceParticipationStatus::BEHIND; + $unread = id(new ConpherenceParticipantCountQuery()) + ->withParticipantPHIDs(array($viewer->getPHID())) + ->withParticipationStatus($unread_status) + ->execute(); + $raw_message_count_number = idx($unread, $viewer->getPHID(), 0); + $message_count_number = $this->formatNumber($raw_message_count_number); + } + $conpherence_data = array( + 'isInstalled' => $is_c_installed, + 'countType' => 'messages', + 'count' => $message_count_number, + 'rawCount' => $raw_message_count_number, + ); + $this->setConpherenceData($conpherence_data); + + $notification_app = 'PhabricatorNotificationsApplication'; + $is_n_installed = PhabricatorApplication::isClassInstalledForViewer( + $notification_app, + $viewer); + $notification_count_number = null; + $raw_notification_count_number = null; + if ($is_n_installed) { + $raw_notification_count_number = + id(new PhabricatorFeedStoryNotification()) + ->countUnread($viewer); + $notification_count_number = $this->formatNumber( + $raw_notification_count_number); + } + $notification_data = array( + 'isInstalled' => $is_n_installed, + 'countType' => 'notifications', + 'count' => $notification_count_number, + 'rawCount' => $raw_notification_count_number, + ); + $this->setNotificationData($notification_data); + + return array( + $notification_app => $this->getNotificationData(), + $conpherence_app => $this->getConpherenceData(), + ); + } + + private function formatNumber($number) { + $formatted = $number; + if ($number > 999) { + $formatted = "\xE2\x88\x9E"; + } + return $formatted; + } + +} 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 @@ -583,8 +583,18 @@ } private function buildQuicksandConfig() { + $user = $this->getRequest()->getUser(); + + $dropdown_query = id(new AphlictDropdownDataQuery()) + ->setViewer($user); + $dropdown_query->execute(); + return array( 'title' => $this->getTitle(), + 'aphlictDropdownData' => array( + $dropdown_query->getNotificationData(), + $dropdown_query->getConpherenceData(), + ), ) + $this->buildAphlictListenConfigData(); } diff --git a/src/view/page/menu/PhabricatorMainMenuView.php b/src/view/page/menu/PhabricatorMainMenuView.php --- a/src/view/page/menu/PhabricatorMainMenuView.php +++ b/src/view/page/menu/PhabricatorMainMenuView.php @@ -298,22 +298,20 @@ $container_classes = array('alert-notifications'); $aural = array(); + $dropdown_query = id(new AphlictDropdownDataQuery()) + ->setViewer($user); + $dropdown_data = $dropdown_query->execute(); + $message_tag = ''; $message_notification_dropdown = ''; - $conpherence = 'PhabricatorConpherenceApplication'; - if (PhabricatorApplication::isClassInstalledForViewer( - $conpherence, - $user)) { + $conpherence_app = 'PhabricatorConpherenceApplication'; + $conpherence_data = $dropdown_data[$conpherence_app]; + if ($conpherence_data['isInstalled']) { $message_id = celerity_generate_unique_node_id(); $message_count_id = celerity_generate_unique_node_id(); $message_dropdown_id = celerity_generate_unique_node_id(); - $unread_status = ConpherenceParticipationStatus::BEHIND; - $unread = id(new ConpherenceParticipantCountQuery()) - ->withParticipantPHIDs(array($user->getPHID())) - ->withParticipationStatus($unread_status) - ->execute(); - $message_count_number = idx($unread, $user->getPHID(), 0); + $message_count_number = $conpherence_data['rawCount']; if ($message_count_number) { $aural[] = phutil_tag( @@ -328,17 +326,13 @@ $aural[] = pht('No messages.'); } - if ($message_count_number > 999) { - $message_count_number = "\xE2\x88\x9E"; - } - $message_count_tag = phutil_tag( 'span', array( 'id' => $message_count_id, 'class' => 'phabricator-main-menu-message-count', ), - $message_count_number); + $conpherence_data['count']); $message_icon_tag = javelin_tag( 'span', @@ -373,7 +367,7 @@ 'dropdownID' => $message_dropdown_id, 'loadingText' => pht('Loading...'), 'uri' => '/conpherence/panel/', - 'countType' => 'messages', + 'countType' => $conpherence_data['countType'], 'countNumber' => $message_count_number, 'unreadClass' => 'message-unread', )); @@ -392,15 +386,13 @@ $bubble_tag = ''; $notification_dropdown = ''; $notification_app = 'PhabricatorNotificationsApplication'; - if (PhabricatorApplication::isClassInstalledForViewer( - $notification_app, - $user)) { + $notification_data = $dropdown_data[$notification_app]; + if ($notification_data['isInstalled']) { $count_id = celerity_generate_unique_node_id(); $dropdown_id = celerity_generate_unique_node_id(); $bubble_id = celerity_generate_unique_node_id(); - $count_number = id(new PhabricatorFeedStoryNotification()) - ->countUnread($user); + $count_number = $notification_data['rawCount']; if ($count_number) { $aural[] = phutil_tag( @@ -415,17 +407,13 @@ $aural[] = pht('No notifications.'); } - if ($count_number > 999) { - $count_number = "\xE2\x88\x9E"; - } - $count_tag = phutil_tag( 'span', array( 'id' => $count_id, 'class' => 'phabricator-main-menu-alert-count', ), - $count_number); + $notification_data['count']); $icon_tag = javelin_tag( 'span', @@ -457,7 +445,7 @@ 'dropdownID' => $dropdown_id, 'loadingText' => pht('Loading...'), 'uri' => '/notification/panel/', - 'countType' => 'notifications', + 'countType' => $notification_data['countType'], 'countNumber' => $count_number, 'unreadClass' => 'alert-unread', )); diff --git a/webroot/rsrc/externals/javelin/lib/Quicksand.js b/webroot/rsrc/externals/javelin/lib/Quicksand.js --- a/webroot/rsrc/externals/javelin/lib/Quicksand.js +++ b/webroot/rsrc/externals/javelin/lib/Quicksand.js @@ -192,7 +192,7 @@ // If it's the current page, draw it into the browser. It might not be // the current page if the user already clicked another link. if (self._current == id) { - self._draw(); + self._draw(true); } }, @@ -203,7 +203,7 @@ * After a navigation event or the arrival of page content, we paint it * onto the page. */ - _draw: function() { + _draw: function(from_server) { var self = JX.Quicksand; if (self._onpage == self._current) { @@ -234,7 +234,8 @@ null, { newResponse: self._responses[self._current], - oldResponse: self._responses[self._onpage] + oldResponse: self._responses[self._onpage], + fromServer: from_server }); self._onpage = self._current; @@ -278,7 +279,7 @@ } // Redraw the page. - self._draw(); + self._draw(false); } }, 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 @@ -28,6 +28,17 @@ JX.Title.setCount(config.countType, config.countNumber); + function _updateCount(number) { + JX.Title.setCount(config.countType, number); + + JX.DOM.setContent(count, number); + if (number === 0) { + JX.DOM.alterClass(bubble, config.unreadClass, false); + } else { + JX.DOM.alterClass(bubble, config.unreadClass, true); + } + } + function refresh() { if (dirty) { JX.DOM.setContent(dropdown, config.loadingText); @@ -43,16 +54,8 @@ } request = new JX.Request(config.uri, function(response) { - JX.Title.setCount(config.countType, response.number); - - var display = (response.number > 999) ? '\u221E' : response.number; - - JX.DOM.setContent(count, display); - if (response.number === 0) { - JX.DOM.alterClass(bubble, config.unreadClass, false); - } else { - JX.DOM.alterClass(bubble, config.unreadClass, true); - } + var number = response.number; + _updateCount(number); dirty = false; JX.DOM.alterClass( dropdown, @@ -64,6 +67,35 @@ request.send(); } + JX.Stratcom.listen( + 'quicksand-redraw', + null, + function (e) { + if (config.local) { + return; + } + + var data = e.getData(); + if (!data.fromServer) { + return; + } + var updated = false; + var new_data = data.newResponse.aphlictDropdownData; + for (var ii = 0; ii < new_data.length; ii++) { + if (new_data[ii].countType != config.countType) { + continue; + } + if (!new_data[ii].isInstalled) { + continue; + } + updated = true; + _updateCount(parseInt(new_data[ii].count)); + } + if (updated) { + dirty = true; + } + }); + function set_visible(menu, icon) { if (menu) { statics.visible = {menu: menu, icon: icon};