From 399c3e4ee6d26073b61144498915d3d4d0ac891e Mon Sep 17 00:00:00 2001 From: Bob Trahan Date: Thu, 8 Aug 2013 13:43:33 -0700 Subject: [PATCH] Conpherence - add dropdown menu Summary: Fixes T3641. Probably needs some @chad love though on colors and what have you. Technique was to jam this into the existing notifications stuff as much as possible. I think its "okay" but if we were to add more stuff here (like a 3rd application) this could get a quality pass to consolidate even more code. Test Plan: played with it in Chrome and Safari - looks reasonable Reviewers: chad, epriestley Reviewed By: epriestley CC: aran, Korvin Maniphest Tasks: T3641 Differential Revision: https://secure.phabricator.com/D6708 --- src/__celerity_resource_map__.php | 89 ++++++++------- src/__phutil_library_map__.php | 2 + .../PhabricatorApplicationConpherence.php | 1 + ...ConpherenceNotificationPanelController.php | 103 ++++++++++++++++++ .../conpherence/storage/ConpherenceThread.php | 2 +- .../view/ConpherenceThreadListView.php | 2 +- .../page/menu/PhabricatorMainMenuView.php | 33 +++++- .../application/conpherence/notification.css | 81 ++++++++++++++ .../aphlict/behavior-aphlict-dropdown.js | 2 +- 9 files changed, 269 insertions(+), 46 deletions(-) create mode 100644 src/applications/conpherence/controller/ConpherenceNotificationPanelController.php create mode 100644 webroot/rsrc/css/application/conpherence/notification.css diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index e88bb2abc7..c9d7f5b2cc 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -997,6 +997,15 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/css/application/conpherence/message-pane.css', ), + 'conpherence-notification-css' => + array( + 'uri' => '/res/8804c853/rsrc/css/application/conpherence/notification.css', + 'type' => 'css', + 'requires' => + array( + ), + 'disk' => '/rsrc/css/application/conpherence/notification.css', + ), 'conpherence-update-css' => array( 'uri' => '/res/92094ed7/rsrc/css/application/conpherence/update.css', @@ -1226,7 +1235,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-aphlict-dropdown' => array( - 'uri' => '/res/b3be58e8/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js', + 'uri' => '/res/c8def75f/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js', 'type' => 'js', 'requires' => array( @@ -4212,7 +4221,7 @@ celerity_register_resource_map(array( 'uri' => '/res/pkg/e289f6ce/core.pkg.css', 'type' => 'css', ), - '606f7152' => + '7cc3f99d' => array( 'name' => 'core.pkg.js', 'symbols' => @@ -4255,7 +4264,7 @@ celerity_register_resource_map(array( 35 => 'phabricator-hovercard', 36 => 'javelin-behavior-phabricator-hovercards', ), - 'uri' => '/res/pkg/606f7152/core.pkg.js', + 'uri' => '/res/pkg/7cc3f99d/core.pkg.js', 'type' => 'js', ), '4ccfeb47' => @@ -4425,16 +4434,16 @@ celerity_register_resource_map(array( 'diffusion-icons-css' => 'c8ce2d88', 'global-drag-and-drop-css' => 'e289f6ce', 'inline-comment-summary-css' => 'dd27a69b', - 'javelin-aphlict' => '606f7152', + 'javelin-aphlict' => '7cc3f99d', 'javelin-behavior' => '2dbbb7d1', - 'javelin-behavior-aphlict-dropdown' => '606f7152', - 'javelin-behavior-aphlict-listen' => '606f7152', - 'javelin-behavior-aphront-basic-tokenizer' => '606f7152', + 'javelin-behavior-aphlict-dropdown' => '7cc3f99d', + 'javelin-behavior-aphlict-listen' => '7cc3f99d', + 'javelin-behavior-aphront-basic-tokenizer' => '7cc3f99d', 'javelin-behavior-aphront-drag-and-drop-textarea' => '48040be9', - 'javelin-behavior-aphront-form-disable-on-submit' => '606f7152', + 'javelin-behavior-aphront-form-disable-on-submit' => '7cc3f99d', 'javelin-behavior-audit-preview' => '96909266', 'javelin-behavior-dark-console' => '4ccfeb47', - 'javelin-behavior-device' => '606f7152', + 'javelin-behavior-device' => '7cc3f99d', 'javelin-behavior-differential-accept-with-errors' => '48040be9', 'javelin-behavior-differential-add-reviewers-and-ccs' => '48040be9', 'javelin-behavior-differential-comment-jump' => '48040be9', @@ -4450,33 +4459,33 @@ celerity_register_resource_map(array( 'javelin-behavior-diffusion-commit-graph' => '96909266', 'javelin-behavior-diffusion-pull-lastmodified' => '96909266', 'javelin-behavior-error-log' => '4ccfeb47', - 'javelin-behavior-global-drag-and-drop' => '606f7152', - 'javelin-behavior-history-install' => '606f7152', - 'javelin-behavior-konami' => '606f7152', - 'javelin-behavior-lightbox-attachments' => '606f7152', + 'javelin-behavior-global-drag-and-drop' => '7cc3f99d', + 'javelin-behavior-history-install' => '7cc3f99d', + 'javelin-behavior-konami' => '7cc3f99d', + 'javelin-behavior-lightbox-attachments' => '7cc3f99d', 'javelin-behavior-load-blame' => '48040be9', 'javelin-behavior-maniphest-batch-selector' => '98f64f07', 'javelin-behavior-maniphest-subpriority-editor' => '98f64f07', 'javelin-behavior-maniphest-transaction-controls' => '98f64f07', 'javelin-behavior-maniphest-transaction-expand' => '98f64f07', 'javelin-behavior-maniphest-transaction-preview' => '98f64f07', - 'javelin-behavior-phabricator-active-nav' => '606f7152', - 'javelin-behavior-phabricator-autofocus' => '606f7152', - 'javelin-behavior-phabricator-gesture' => '606f7152', - 'javelin-behavior-phabricator-hovercards' => '606f7152', - 'javelin-behavior-phabricator-keyboard-shortcuts' => '606f7152', - 'javelin-behavior-phabricator-nav' => '606f7152', + 'javelin-behavior-phabricator-active-nav' => '7cc3f99d', + 'javelin-behavior-phabricator-autofocus' => '7cc3f99d', + 'javelin-behavior-phabricator-gesture' => '7cc3f99d', + 'javelin-behavior-phabricator-hovercards' => '7cc3f99d', + 'javelin-behavior-phabricator-keyboard-shortcuts' => '7cc3f99d', + 'javelin-behavior-phabricator-nav' => '7cc3f99d', 'javelin-behavior-phabricator-object-selector' => '48040be9', - 'javelin-behavior-phabricator-oncopy' => '606f7152', - 'javelin-behavior-phabricator-remarkup-assist' => '606f7152', - 'javelin-behavior-phabricator-reveal-content' => '606f7152', - 'javelin-behavior-phabricator-search-typeahead' => '606f7152', - 'javelin-behavior-phabricator-tooltips' => '606f7152', - 'javelin-behavior-phabricator-watch-anchor' => '606f7152', - 'javelin-behavior-refresh-csrf' => '606f7152', + 'javelin-behavior-phabricator-oncopy' => '7cc3f99d', + 'javelin-behavior-phabricator-remarkup-assist' => '7cc3f99d', + 'javelin-behavior-phabricator-reveal-content' => '7cc3f99d', + 'javelin-behavior-phabricator-search-typeahead' => '7cc3f99d', + 'javelin-behavior-phabricator-tooltips' => '7cc3f99d', + 'javelin-behavior-phabricator-watch-anchor' => '7cc3f99d', + 'javelin-behavior-refresh-csrf' => '7cc3f99d', 'javelin-behavior-repository-crossreference' => '48040be9', - 'javelin-behavior-toggle-class' => '606f7152', - 'javelin-behavior-workflow' => '606f7152', + 'javelin-behavior-toggle-class' => '7cc3f99d', + 'javelin-behavior-workflow' => '7cc3f99d', 'javelin-dom' => '2dbbb7d1', 'javelin-event' => '2dbbb7d1', 'javelin-history' => '2dbbb7d1', @@ -4501,31 +4510,31 @@ celerity_register_resource_map(array( 'maniphest-transaction-detail-css' => '06bacb9a', 'phabricator-action-list-view-css' => 'e289f6ce', 'phabricator-application-launch-view-css' => 'e289f6ce', - 'phabricator-busy' => '606f7152', + 'phabricator-busy' => '7cc3f99d', 'phabricator-content-source-view-css' => 'dd27a69b', 'phabricator-core-css' => 'e289f6ce', 'phabricator-crumbs-view-css' => 'e289f6ce', 'phabricator-drag-and-drop-file-upload' => '48040be9', - 'phabricator-dropdown-menu' => '606f7152', - 'phabricator-file-upload' => '606f7152', + 'phabricator-dropdown-menu' => '7cc3f99d', + 'phabricator-file-upload' => '7cc3f99d', 'phabricator-filetree-view-css' => 'e289f6ce', 'phabricator-flag-css' => 'e289f6ce', 'phabricator-form-view-css' => 'e289f6ce', 'phabricator-header-view-css' => 'e289f6ce', - 'phabricator-hovercard' => '606f7152', + 'phabricator-hovercard' => '7cc3f99d', 'phabricator-jump-nav' => 'e289f6ce', - 'phabricator-keyboard-shortcut' => '606f7152', - 'phabricator-keyboard-shortcut-manager' => '606f7152', + 'phabricator-keyboard-shortcut' => '7cc3f99d', + 'phabricator-keyboard-shortcut-manager' => '7cc3f99d', 'phabricator-main-menu-view' => 'e289f6ce', - 'phabricator-menu-item' => '606f7152', + 'phabricator-menu-item' => '7cc3f99d', 'phabricator-nav-view-css' => 'e289f6ce', - 'phabricator-notification' => '606f7152', + 'phabricator-notification' => '7cc3f99d', 'phabricator-notification-css' => 'e289f6ce', 'phabricator-notification-menu-css' => 'e289f6ce', 'phabricator-object-item-list-view-css' => 'e289f6ce', 'phabricator-object-selector-css' => 'dd27a69b', - 'phabricator-phtize' => '606f7152', - 'phabricator-prefab' => '606f7152', + 'phabricator-phtize' => '7cc3f99d', + 'phabricator-prefab' => '7cc3f99d', 'phabricator-project-tag-css' => '06bacb9a', 'phabricator-property-list-view-css' => 'e289f6ce', 'phabricator-remarkup-css' => 'e289f6ce', @@ -4533,8 +4542,8 @@ celerity_register_resource_map(array( 'phabricator-side-menu-view-css' => 'e289f6ce', 'phabricator-standard-page-view' => 'e289f6ce', 'phabricator-tag-view-css' => 'e289f6ce', - 'phabricator-textareautils' => '606f7152', - 'phabricator-tooltip' => '606f7152', + 'phabricator-textareautils' => '7cc3f99d', + 'phabricator-tooltip' => '7cc3f99d', 'phabricator-transaction-view-css' => 'e289f6ce', 'phabricator-zindex-css' => 'e289f6ce', 'phui-button-css' => 'e289f6ce', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index b283398dae..009cf3f170 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -256,6 +256,7 @@ phutil_register_library_map(array( 'ConpherenceListController' => 'applications/conpherence/controller/ConpherenceListController.php', 'ConpherenceMenuItemView' => 'applications/conpherence/view/ConpherenceMenuItemView.php', 'ConpherenceNewController' => 'applications/conpherence/controller/ConpherenceNewController.php', + 'ConpherenceNotificationPanelController' => 'applications/conpherence/controller/ConpherenceNotificationPanelController.php', 'ConpherenceParticipant' => 'applications/conpherence/storage/ConpherenceParticipant.php', 'ConpherenceParticipantCountQuery' => 'applications/conpherence/query/ConpherenceParticipantCountQuery.php', 'ConpherenceParticipantQuery' => 'applications/conpherence/query/ConpherenceParticipantQuery.php', @@ -2274,6 +2275,7 @@ phutil_register_library_map(array( 'ConpherenceListController' => 'ConpherenceController', 'ConpherenceMenuItemView' => 'AphrontTagView', 'ConpherenceNewController' => 'ConpherenceController', + 'ConpherenceNotificationPanelController' => 'ConpherenceController', 'ConpherenceParticipant' => 'ConpherenceDAO', 'ConpherenceParticipantCountQuery' => 'PhabricatorOffsetPagedQuery', 'ConpherenceParticipantQuery' => 'PhabricatorOffsetPagedQuery', diff --git a/src/applications/conpherence/application/PhabricatorApplicationConpherence.php b/src/applications/conpherence/application/PhabricatorApplicationConpherence.php index 87e26d816b..719335fcf9 100644 --- a/src/applications/conpherence/application/PhabricatorApplicationConpherence.php +++ b/src/applications/conpherence/application/PhabricatorApplicationConpherence.php @@ -43,6 +43,7 @@ final class PhabricatorApplicationConpherence extends PhabricatorApplication { 'thread/(?P[1-9]\d*)/' => 'ConpherenceListController', '(?P[1-9]\d*)/' => 'ConpherenceViewController', 'new/' => 'ConpherenceNewController', + 'panel/' => 'ConpherenceNotificationPanelController', 'widget/(?P[1-9]\d*)/' => 'ConpherenceWidgetController', 'update/(?P[1-9]\d*)/' => 'ConpherenceUpdateController', ), diff --git a/src/applications/conpherence/controller/ConpherenceNotificationPanelController.php b/src/applications/conpherence/controller/ConpherenceNotificationPanelController.php new file mode 100644 index 0000000000..a9ff568726 --- /dev/null +++ b/src/applications/conpherence/controller/ConpherenceNotificationPanelController.php @@ -0,0 +1,103 @@ +getRequest(); + $user = $request->getUser(); + $conpherences = array(); + $unread_status = ConpherenceParticipationStatus::BEHIND; + + $participant_data = id(new ConpherenceParticipantQuery()) + ->withParticipantPHIDs(array($user->getPHID())) + ->setLimit(5) + ->execute(); + + if ($participant_data) { + $conpherences = id(new ConpherenceThreadQuery()) + ->setViewer($user) + ->withPHIDs(array_keys($participant_data)) + ->needParticipantCache(true) + ->execute(); + } + + if ($conpherences) { + require_celerity_resource('conpherence-notification-css'); + $view = new AphrontNullView(); + foreach ($conpherences as $conpherence) { + $p_data = $participant_data[$conpherence->getPHID()]; + $d_data = $conpherence->getDisplayData($user); + $classes = array( + 'phabricator-notification', + 'conpherence-notification', + ); + + if ($p_data->getParticipationStatus() == $unread_status) { + $classes[] = 'phabricator-notification-unread'; + } + $uri = $this->getApplicationURI($conpherence->getID().'/'); + $title = $d_data['title']; + $subtitle = $d_data['subtitle']; + $unread_count = $d_data['unread_count']; + $epoch = $d_data['epoch']; + $image = $d_data['image']; + + $msg_view = id(new ConpherenceMenuItemView()) + ->setUser($user) + ->setTitle($title) + ->setSubtitle($subtitle) + ->setHref($uri) + ->setEpoch($epoch) + ->setImageURI($image) + ->setUnreadCount($unread_count); + + $view->appendChild(javelin_tag( + 'div', + array( + 'class' => implode(' ', $classes), + 'sigil' => 'notification', + 'meta' => array( + 'href' => $uri, + ), + ), + $msg_view)); + } + $content = $view->render(); + } else { + $content = hsprintf( + '
%s
', + pht('You have no messages.')); + } + + $content = hsprintf( + '
%s
'. + '%s'. + '
%s
', + pht('Messages'), + $content, + phutil_tag( + 'a', + array( + 'href' => '/conpherence/', + ), + 'View All Conpherences')); + + $unread = id(new ConpherenceParticipantCountQuery()) + ->withParticipantPHIDs(array($user->getPHID())) + ->withParticipationStatus($unread_status) + ->execute(); + $unread_count = idx($unread, $user->getPHID(), 0); + + $json = array( + 'content' => $content, + 'number' => (int)$unread_count, + ); + + return id(new AphrontAjaxResponse())->setContent($json); + } +} diff --git a/src/applications/conpherence/storage/ConpherenceThread.php b/src/applications/conpherence/storage/ConpherenceThread.php index ee2d1498bc..b7e13e7db6 100644 --- a/src/applications/conpherence/storage/ConpherenceThread.php +++ b/src/applications/conpherence/storage/ConpherenceThread.php @@ -133,7 +133,7 @@ final class ConpherenceThread extends ConpherenceDAO return $this->widgetData; } - public function getDisplayData(PhabricatorUser $user, $size) { + public function getDisplayData(PhabricatorUser $user) { $recent_phids = $this->getRecentParticipantPHIDs(); $handles = $this->getHandles(); diff --git a/src/applications/conpherence/view/ConpherenceThreadListView.php b/src/applications/conpherence/view/ConpherenceThreadListView.php index 4f06224a9c..7b357f0cab 100644 --- a/src/applications/conpherence/view/ConpherenceThreadListView.php +++ b/src/applications/conpherence/view/ConpherenceThreadListView.php @@ -78,7 +78,7 @@ final class ConpherenceThreadListView extends AphrontView { $user = $this->getUser(); $uri = $this->baseURI.$thread->getID().'/'; - $data = $thread->getDisplayData($user, null); + $data = $thread->getDisplayData($user); $title = $data['title']; $subtitle = $data['subtitle']; $unread_count = $data['unread_count']; diff --git a/src/view/page/menu/PhabricatorMainMenuView.php b/src/view/page/menu/PhabricatorMainMenuView.php index 4734d5be69..102958d2e7 100644 --- a/src/view/page/menu/PhabricatorMainMenuView.php +++ b/src/view/page/menu/PhabricatorMainMenuView.php @@ -45,9 +45,9 @@ final class PhabricatorMainMenuView extends AphrontView { $app_button = ''; if ($user->isLoggedIn()) { - list($menu, $dropdown) = $this->renderNotificationMenu(); + list($menu, $dropdowns) = $this->renderNotificationMenu(); $alerts[] = $menu; - $menus[] = $dropdown; + $menus = array_merge($menus, $dropdowns); $app_button = $this->renderApplicationMenuButton($header_id); $search_button = $this->renderSearchMenuButton($header_id); } @@ -248,10 +248,12 @@ final class PhabricatorMainMenuView extends AphrontView { ); $message_tag = ''; + $message_notification_dropdown = ''; $conpherence = 'PhabricatorApplicationConpherence'; if (PhabricatorApplication::isClassInstalled($conpherence)) { $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()) @@ -293,6 +295,26 @@ final class PhabricatorMainMenuView extends AphrontView { $message_icon_tag, $message_count_tag, )); + + Javelin::initBehavior( + 'aphlict-dropdown', + array( + 'bubbleID' => $message_id, + 'countID' => $message_count_id, + 'dropdownID' => $message_dropdown_id, + 'loadingText' => pht('Loading...'), + 'uri' => '/conpherence/panel/', + )); + + $message_notification_dropdown = javelin_tag( + 'div', + array( + 'id' => $message_dropdown_id, + 'class' => 'phabricator-notification-menu', + 'sigil' => 'phabricator-notification-menu', + 'style' => 'display: none;', + ), + ''); } $count_id = celerity_generate_unique_node_id(); @@ -341,6 +363,7 @@ final class PhabricatorMainMenuView extends AphrontView { 'countID' => $count_id, 'dropdownID' => $dropdown_id, 'loadingText' => pht('Loading...'), + 'uri' => '/notification/panel/', )); $notification_dropdown = javelin_tag( @@ -353,9 +376,13 @@ final class PhabricatorMainMenuView extends AphrontView { ), ''); + $dropdowns = array( + $notification_dropdown, + $message_notification_dropdown); + return array( hsprintf('%s%s', $bubble_tag, $message_tag), - $notification_dropdown, + $dropdowns ); } diff --git a/webroot/rsrc/css/application/conpherence/notification.css b/webroot/rsrc/css/application/conpherence/notification.css new file mode 100644 index 0000000000..92ab4e7b0b --- /dev/null +++ b/webroot/rsrc/css/application/conpherence/notification.css @@ -0,0 +1,81 @@ +/** + * @provides conpherence-notification-css + */ + +/* kill styles on phabricator-notification */ +.conpherence-notification { + padding: 0; +} + +.phabricator-notification .conpherence-menu-item-view { + display: block; + height: 55px; + width: 100%; + overflow: hidden; + position: relative; + text-decoration: none; + border-bottom: solid 1px #E9E9E9; + border-right: 0; + border-left: 2px solid transparent; +} + +.phabricator-notification .conpherence-menu-item-view +.conpherence-menu-item-image { + top: 6px; + left: 6px; + display: block; + position: absolute; + width: 35px; + height: 35px; + background-size: 35px; + border: 4px solid #DEDEDE; + border-radius: 3px; +} + +.phabricator-notification .conpherence-menu-item-view +.conpherence-menu-item-title { + display: block; + margin-top: 12px; + margin-left: 58px; + text-align: left; + font-weight: bold; + font-size: 13px; + color: #333; + overflow: hidden; + width: 220px; + text-overflow: ellipsis; +} + +.phabricator-notification .conpherence-menu-item-view +.conpherence-menu-item-subtitle { + display: block; + color: #a1a5a9; + font-size: 11px; + margin-top: 2px; + margin-left: 58px; + font-style: italic; +} + +.phabricator-notification .conpherence-menu-item-view +.conpherence-menu-item-unread-count { + position: absolute; + left: 35px; + top: 3px; + background: #f00; + border-radius: 10px; + color: white; + font-weight: bold; + padding: 1px 6px 2px; + border: 1px solid #a00; + font-size: 12px; +} + +.phabricator-notification .conpherence-menu-item-view +.conpherence-menu-item-date { + position: absolute; + top: 15px; + right: 16px; + color: #a1a5a9; + font-size: 11px; +} + diff --git a/webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js b/webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js index 0017cb5489..1a36fee06b 100644 --- a/webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js +++ b/webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js @@ -28,7 +28,7 @@ JX.behavior('aphlict-dropdown', function(config) { if (request) { //already fetching return; } - request = new JX.Request('/notification/panel/', function(response) { + request = new JX.Request(config.uri, function(response) { var display = (response.number > 999) ? "\u221E" : response.number; JX.DOM.setContent(count, display);