1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-03-19 15:50:17 +01:00

Quicksand - make notification and message counts update as you navigate around

Summary: Ref T7573. Unify code to fetch these counts and do some light formatting since we're going to need to do the same thing for some conpherence-specific ajax in the durable column (See T7708).

Test Plan: loaded up two tabs, one with a durable column on and one without. in the without browser, i read some messages, decrementing my unread count. when i navigated again in the durable column browser, the count updated correctly. with no notifications, commented on a task with another user to get a notification and it showed up properly. visited the task by clicking not the notification and the bubble count decremented correctly

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin, epriestley

Maniphest Tasks: T7573

Differential Revision: https://secure.phabricator.com/D12498
This commit is contained in:
Bob Trahan 2015-04-21 15:46:36 -07:00
parent c509e80a74
commit dd9ec255ec
7 changed files with 185 additions and 50 deletions

View file

@ -8,7 +8,7 @@
return array( return array(
'names' => array( 'names' => array(
'core.pkg.css' => 'a2a90172', 'core.pkg.css' => 'a2a90172',
'core.pkg.js' => '328a9980', 'core.pkg.js' => '218b6c3d',
'darkconsole.pkg.js' => '8ab24e01', 'darkconsole.pkg.js' => '8ab24e01',
'differential.pkg.css' => '3500921f', 'differential.pkg.css' => '3500921f',
'differential.pkg.js' => 'c0506961', 'differential.pkg.js' => 'c0506961',
@ -205,7 +205,7 @@ return array(
'rsrc/externals/javelin/lib/JSON.js' => '69adf288', 'rsrc/externals/javelin/lib/JSON.js' => '69adf288',
'rsrc/externals/javelin/lib/Leader.js' => '331b1611', 'rsrc/externals/javelin/lib/Leader.js' => '331b1611',
'rsrc/externals/javelin/lib/Mask.js' => '8a41885b', '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/Request.js' => '94b750d2',
'rsrc/externals/javelin/lib/Resource.js' => '44959b73', 'rsrc/externals/javelin/lib/Resource.js' => '44959b73',
'rsrc/externals/javelin/lib/Routable.js' => 'b3e7d692', 'rsrc/externals/javelin/lib/Routable.js' => 'b3e7d692',
@ -350,7 +350,7 @@ return array(
'rsrc/image/texture/table_header_hover.png' => '038ec3b9', 'rsrc/image/texture/table_header_hover.png' => '038ec3b9',
'rsrc/image/texture/table_header_tall.png' => 'd56b434f', 'rsrc/image/texture/table_header_tall.png' => 'd56b434f',
'rsrc/js/application/aphlict/Aphlict.js' => '30a6303c', '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-listen.js' => 'b1a59974',
'rsrc/js/application/aphlict/behavior-aphlict-status.js' => 'ea681761', 'rsrc/js/application/aphlict/behavior-aphlict-status.js' => 'ea681761',
'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18', 'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18',
@ -550,7 +550,7 @@ return array(
'inline-comment-summary-css' => 'eb5f8e8c', 'inline-comment-summary-css' => 'eb5f8e8c',
'javelin-aphlict' => '30a6303c', 'javelin-aphlict' => '30a6303c',
'javelin-behavior' => '61cbc29a', 'javelin-behavior' => '61cbc29a',
'javelin-behavior-aphlict-dropdown' => '00def500', 'javelin-behavior-aphlict-dropdown' => '01c816ca',
'javelin-behavior-aphlict-listen' => 'b1a59974', 'javelin-behavior-aphlict-listen' => 'b1a59974',
'javelin-behavior-aphlict-status' => 'ea681761', 'javelin-behavior-aphlict-status' => 'ea681761',
'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884', 'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884',
@ -677,7 +677,7 @@ return array(
'javelin-leader' => '331b1611', 'javelin-leader' => '331b1611',
'javelin-magical-init' => '3010e992', 'javelin-magical-init' => '3010e992',
'javelin-mask' => '8a41885b', 'javelin-mask' => '8a41885b',
'javelin-quicksand' => '97720512', 'javelin-quicksand' => '517545ab',
'javelin-reactor' => '2b8de964', 'javelin-reactor' => '2b8de964',
'javelin-reactor-dom' => 'c90a04fc', 'javelin-reactor-dom' => 'c90a04fc',
'javelin-reactor-node-calmer' => '76f4ebed', 'javelin-reactor-node-calmer' => '76f4ebed',
@ -842,7 +842,7 @@ return array(
'unhandled-exception-css' => '37d4f9a2', 'unhandled-exception-css' => '37d4f9a2',
), ),
'requires' => array( 'requires' => array(
'00def500' => array( '01c816ca' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-request', 'javelin-request',
'javelin-stratcom', 'javelin-stratcom',
@ -1183,6 +1183,9 @@ return array(
'javelin-typeahead-source', 'javelin-typeahead-source',
'javelin-util', 'javelin-util',
), ),
'517545ab' => array(
'javelin-install',
),
'519705ea' => array( '519705ea' => array(
'javelin-install', 'javelin-install',
'javelin-dom', 'javelin-dom',
@ -1600,9 +1603,6 @@ return array(
'javelin-resource', 'javelin-resource',
'javelin-routable', 'javelin-routable',
), ),
97720512 => array(
'javelin-install',
),
'988040b4' => array( '988040b4' => array(
'javelin-install', 'javelin-install',
'javelin-dom', 'javelin-dom',

View file

@ -93,6 +93,7 @@ phutil_register_library_map(array(
'AlmanacServiceTransactionQuery' => 'applications/almanac/query/AlmanacServiceTransactionQuery.php', 'AlmanacServiceTransactionQuery' => 'applications/almanac/query/AlmanacServiceTransactionQuery.php',
'AlmanacServiceType' => 'applications/almanac/servicetype/AlmanacServiceType.php', 'AlmanacServiceType' => 'applications/almanac/servicetype/AlmanacServiceType.php',
'AlmanacServiceViewController' => 'applications/almanac/controller/AlmanacServiceViewController.php', 'AlmanacServiceViewController' => 'applications/almanac/controller/AlmanacServiceViewController.php',
'AphlictDropdownDataQuery' => 'applications/aphlict/query/AphlictDropdownDataQuery.php',
'Aphront304Response' => 'aphront/response/Aphront304Response.php', 'Aphront304Response' => 'aphront/response/Aphront304Response.php',
'Aphront400Response' => 'aphront/response/Aphront400Response.php', 'Aphront400Response' => 'aphront/response/Aphront400Response.php',
'Aphront403Response' => 'aphront/response/Aphront403Response.php', 'Aphront403Response' => 'aphront/response/Aphront403Response.php',

View file

@ -0,0 +1,103 @@
<?php
final class AphlictDropdownDataQuery {
private $viewer;
private $notificationData;
private $conpherenceData;
public function setViewer(PhabricatorUser $viewer) {
$this->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;
}
}

View file

@ -583,8 +583,18 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView {
} }
private function buildQuicksandConfig() { private function buildQuicksandConfig() {
$user = $this->getRequest()->getUser();
$dropdown_query = id(new AphlictDropdownDataQuery())
->setViewer($user);
$dropdown_query->execute();
return array( return array(
'title' => $this->getTitle(), 'title' => $this->getTitle(),
'aphlictDropdownData' => array(
$dropdown_query->getNotificationData(),
$dropdown_query->getConpherenceData(),
),
) + $this->buildAphlictListenConfigData(); ) + $this->buildAphlictListenConfigData();
} }

View file

@ -298,22 +298,20 @@ final class PhabricatorMainMenuView extends AphrontView {
$container_classes = array('alert-notifications'); $container_classes = array('alert-notifications');
$aural = array(); $aural = array();
$dropdown_query = id(new AphlictDropdownDataQuery())
->setViewer($user);
$dropdown_data = $dropdown_query->execute();
$message_tag = ''; $message_tag = '';
$message_notification_dropdown = ''; $message_notification_dropdown = '';
$conpherence = 'PhabricatorConpherenceApplication'; $conpherence_app = 'PhabricatorConpherenceApplication';
if (PhabricatorApplication::isClassInstalledForViewer( $conpherence_data = $dropdown_data[$conpherence_app];
$conpherence, if ($conpherence_data['isInstalled']) {
$user)) {
$message_id = celerity_generate_unique_node_id(); $message_id = celerity_generate_unique_node_id();
$message_count_id = celerity_generate_unique_node_id(); $message_count_id = celerity_generate_unique_node_id();
$message_dropdown_id = celerity_generate_unique_node_id(); $message_dropdown_id = celerity_generate_unique_node_id();
$unread_status = ConpherenceParticipationStatus::BEHIND; $message_count_number = $conpherence_data['rawCount'];
$unread = id(new ConpherenceParticipantCountQuery())
->withParticipantPHIDs(array($user->getPHID()))
->withParticipationStatus($unread_status)
->execute();
$message_count_number = idx($unread, $user->getPHID(), 0);
if ($message_count_number) { if ($message_count_number) {
$aural[] = phutil_tag( $aural[] = phutil_tag(
@ -328,17 +326,13 @@ final class PhabricatorMainMenuView extends AphrontView {
$aural[] = pht('No messages.'); $aural[] = pht('No messages.');
} }
if ($message_count_number > 999) {
$message_count_number = "\xE2\x88\x9E";
}
$message_count_tag = phutil_tag( $message_count_tag = phutil_tag(
'span', 'span',
array( array(
'id' => $message_count_id, 'id' => $message_count_id,
'class' => 'phabricator-main-menu-message-count', 'class' => 'phabricator-main-menu-message-count',
), ),
$message_count_number); $conpherence_data['count']);
$message_icon_tag = javelin_tag( $message_icon_tag = javelin_tag(
'span', 'span',
@ -373,7 +367,7 @@ final class PhabricatorMainMenuView extends AphrontView {
'dropdownID' => $message_dropdown_id, 'dropdownID' => $message_dropdown_id,
'loadingText' => pht('Loading...'), 'loadingText' => pht('Loading...'),
'uri' => '/conpherence/panel/', 'uri' => '/conpherence/panel/',
'countType' => 'messages', 'countType' => $conpherence_data['countType'],
'countNumber' => $message_count_number, 'countNumber' => $message_count_number,
'unreadClass' => 'message-unread', 'unreadClass' => 'message-unread',
)); ));
@ -392,15 +386,13 @@ final class PhabricatorMainMenuView extends AphrontView {
$bubble_tag = ''; $bubble_tag = '';
$notification_dropdown = ''; $notification_dropdown = '';
$notification_app = 'PhabricatorNotificationsApplication'; $notification_app = 'PhabricatorNotificationsApplication';
if (PhabricatorApplication::isClassInstalledForViewer( $notification_data = $dropdown_data[$notification_app];
$notification_app, if ($notification_data['isInstalled']) {
$user)) {
$count_id = celerity_generate_unique_node_id(); $count_id = celerity_generate_unique_node_id();
$dropdown_id = celerity_generate_unique_node_id(); $dropdown_id = celerity_generate_unique_node_id();
$bubble_id = celerity_generate_unique_node_id(); $bubble_id = celerity_generate_unique_node_id();
$count_number = id(new PhabricatorFeedStoryNotification()) $count_number = $notification_data['rawCount'];
->countUnread($user);
if ($count_number) { if ($count_number) {
$aural[] = phutil_tag( $aural[] = phutil_tag(
@ -415,17 +407,13 @@ final class PhabricatorMainMenuView extends AphrontView {
$aural[] = pht('No notifications.'); $aural[] = pht('No notifications.');
} }
if ($count_number > 999) {
$count_number = "\xE2\x88\x9E";
}
$count_tag = phutil_tag( $count_tag = phutil_tag(
'span', 'span',
array( array(
'id' => $count_id, 'id' => $count_id,
'class' => 'phabricator-main-menu-alert-count', 'class' => 'phabricator-main-menu-alert-count',
), ),
$count_number); $notification_data['count']);
$icon_tag = javelin_tag( $icon_tag = javelin_tag(
'span', 'span',
@ -457,7 +445,7 @@ final class PhabricatorMainMenuView extends AphrontView {
'dropdownID' => $dropdown_id, 'dropdownID' => $dropdown_id,
'loadingText' => pht('Loading...'), 'loadingText' => pht('Loading...'),
'uri' => '/notification/panel/', 'uri' => '/notification/panel/',
'countType' => 'notifications', 'countType' => $notification_data['countType'],
'countNumber' => $count_number, 'countNumber' => $count_number,
'unreadClass' => 'alert-unread', 'unreadClass' => 'alert-unread',
)); ));

View file

@ -192,7 +192,7 @@ JX.install('Quicksand', {
// If it's the current page, draw it into the browser. It might not be // 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. // the current page if the user already clicked another link.
if (self._current == id) { if (self._current == id) {
self._draw(); self._draw(true);
} }
}, },
@ -203,7 +203,7 @@ JX.install('Quicksand', {
* After a navigation event or the arrival of page content, we paint it * After a navigation event or the arrival of page content, we paint it
* onto the page. * onto the page.
*/ */
_draw: function() { _draw: function(from_server) {
var self = JX.Quicksand; var self = JX.Quicksand;
if (self._onpage == self._current) { if (self._onpage == self._current) {
@ -234,7 +234,8 @@ JX.install('Quicksand', {
null, null,
{ {
newResponse: self._responses[self._current], newResponse: self._responses[self._current],
oldResponse: self._responses[self._onpage] oldResponse: self._responses[self._onpage],
fromServer: from_server
}); });
self._onpage = self._current; self._onpage = self._current;
@ -278,7 +279,7 @@ JX.install('Quicksand', {
} }
// Redraw the page. // Redraw the page.
self._draw(); self._draw(false);
} }
}, },

View file

@ -28,6 +28,17 @@ JX.behavior('aphlict-dropdown', function(config, statics) {
JX.Title.setCount(config.countType, config.countNumber); 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() { function refresh() {
if (dirty) { if (dirty) {
JX.DOM.setContent(dropdown, config.loadingText); JX.DOM.setContent(dropdown, config.loadingText);
@ -43,16 +54,8 @@ JX.behavior('aphlict-dropdown', function(config, statics) {
} }
request = new JX.Request(config.uri, function(response) { request = new JX.Request(config.uri, function(response) {
JX.Title.setCount(config.countType, response.number); var number = response.number;
_updateCount(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);
}
dirty = false; dirty = false;
JX.DOM.alterClass( JX.DOM.alterClass(
dropdown, dropdown,
@ -64,6 +67,35 @@ JX.behavior('aphlict-dropdown', function(config, statics) {
request.send(); 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) { function set_visible(menu, icon) {
if (menu) { if (menu) {
statics.visible = {menu: menu, icon: icon}; statics.visible = {menu: menu, icon: icon};