From a11deec4d48c3c209aed3c3bc122fafe0ceb4f67 Mon Sep 17 00:00:00 2001 From: John-Ashton Allen Date: Mon, 11 Jun 2012 09:37:06 -0700 Subject: [PATCH] Adds the UI dropdown panel Summary: Add a dropdown to display notificaitons. Right now there is nothing real time about it, but we do update the panel when the user clicks. This panel is only displayed if the install has notifications enabled and you have them enabled in your preferences (not using them by default). Test Plan: Turn off notifications for user1, left them on for user2. Did things from user1 and from user2 on task both were cc'd on. user2 recieved all notifications, user1 recieved nothing. Made new user, made sure everything was switched off by default. Reviewers: epriestley, btrahan Reviewed By: epriestley CC: keebuhm, ddfisher, aran, Korvin Differential Revision: https://secure.phabricator.com/D2703 --- src/__celerity_resource_map__.php | 66 +++++++++++-------- src/__phutil_library_map__.php | 14 ++++ ...AphrontDefaultApplicationConfiguration.php | 1 + .../PhabricatorNotificationBuilder.php | 1 + ...PhabricatorNotificationPanelController.php | 44 +++++++++++++ .../PhabricatorFeedStoryNotification.php | 18 +++++ src/view/page/PhabricatorStandardPageView.php | 52 ++++++++++++++- .../application/base/standard-page-view.css | 51 ++++++++++++++ .../aphlict/behavior-aphlict-dropdown.js | 54 +++++++++++++++ 9 files changed, 274 insertions(+), 27 deletions(-) create mode 100644 src/applications/notification/controller/PhabricatorNotificationPanelController.php create mode 100644 webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 2f10ff2515..e190e040aa 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -756,6 +756,20 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/javelin/lib/behavior.js', ), + 'javelin-behavior-aphlict-dropdown' => + array( + 'uri' => '/res/d0025c08/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js', + 'type' => 'js', + 'requires' => + array( + 0 => 'javelin-behavior', + 1 => 'javelin-aphlict', + 2 => 'javelin-util', + 3 => 'javelin-request', + 4 => 'javelin-stratcom', + ), + 'disk' => '/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js', + ), 'javelin-behavior-aphlict-listen' => array( 'uri' => '/res/6388e057/rsrc/js/application/aphlict/behavior-aphlict-listen.js', @@ -2242,7 +2256,7 @@ celerity_register_resource_map(array( ), 'phabricator-standard-page-view' => array( - 'uri' => '/res/07d5f4cb/rsrc/css/application/base/standard-page-view.css', + 'uri' => '/res/479161fe/rsrc/css/application/base/standard-page-view.css', 'type' => 'css', 'requires' => array( @@ -2497,7 +2511,7 @@ celerity_register_resource_map(array( ), array( 'packages' => array( - '4984f83f' => + 'f6b9a4d4' => array( 'name' => 'core.pkg.css', 'symbols' => @@ -2526,7 +2540,7 @@ celerity_register_resource_map(array( 21 => 'phabricator-flag-css', 22 => 'aphront-error-view-css', ), - 'uri' => '/res/pkg/4984f83f/core.pkg.css', + 'uri' => '/res/pkg/f6b9a4d4/core.pkg.css', 'type' => 'css', ), '0c96375e' => @@ -2693,20 +2707,20 @@ celerity_register_resource_map(array( 'reverse' => array( 'aphront-attached-file-view-css' => '7839ae2d', - 'aphront-crumbs-view-css' => '4984f83f', - 'aphront-dialog-view-css' => '4984f83f', - 'aphront-error-view-css' => '4984f83f', - 'aphront-form-view-css' => '4984f83f', + 'aphront-crumbs-view-css' => 'f6b9a4d4', + 'aphront-dialog-view-css' => 'f6b9a4d4', + 'aphront-error-view-css' => 'f6b9a4d4', + 'aphront-form-view-css' => 'f6b9a4d4', 'aphront-headsup-action-list-view-css' => '32f461a4', - 'aphront-headsup-view-css' => '4984f83f', - 'aphront-list-filter-view-css' => '4984f83f', - 'aphront-pager-view-css' => '4984f83f', - 'aphront-panel-view-css' => '4984f83f', - 'aphront-side-nav-view-css' => '4984f83f', - 'aphront-table-view-css' => '4984f83f', - 'aphront-tokenizer-control-css' => '4984f83f', - 'aphront-tooltip-css' => '4984f83f', - 'aphront-typeahead-control-css' => '4984f83f', + 'aphront-headsup-view-css' => 'f6b9a4d4', + 'aphront-list-filter-view-css' => 'f6b9a4d4', + 'aphront-pager-view-css' => 'f6b9a4d4', + 'aphront-panel-view-css' => 'f6b9a4d4', + 'aphront-side-nav-view-css' => 'f6b9a4d4', + 'aphront-table-view-css' => 'f6b9a4d4', + 'aphront-tokenizer-control-css' => 'f6b9a4d4', + 'aphront-tooltip-css' => 'f6b9a4d4', + 'aphront-typeahead-control-css' => 'f6b9a4d4', 'differential-changeset-view-css' => '32f461a4', 'differential-core-view-css' => '32f461a4', 'differential-inline-comment-editor' => '86f654e2', @@ -2772,15 +2786,15 @@ celerity_register_resource_map(array( 'javelin-workflow' => '0c96375e', 'maniphest-task-summary-css' => '7839ae2d', 'maniphest-transaction-detail-css' => '7839ae2d', - 'phabricator-app-buttons-css' => '4984f83f', + 'phabricator-app-buttons-css' => 'f6b9a4d4', 'phabricator-content-source-view-css' => '32f461a4', - 'phabricator-core-buttons-css' => '4984f83f', - 'phabricator-core-css' => '4984f83f', - 'phabricator-directory-css' => '4984f83f', + 'phabricator-core-buttons-css' => 'f6b9a4d4', + 'phabricator-core-css' => 'f6b9a4d4', + 'phabricator-directory-css' => 'f6b9a4d4', 'phabricator-drag-and-drop-file-upload' => '86f654e2', 'phabricator-dropdown-menu' => '0c96375e', - 'phabricator-flag-css' => '4984f83f', - 'phabricator-jump-nav' => '4984f83f', + 'phabricator-flag-css' => 'f6b9a4d4', + 'phabricator-jump-nav' => 'f6b9a4d4', 'phabricator-keyboard-shortcut' => '0c96375e', 'phabricator-keyboard-shortcut-manager' => '0c96375e', 'phabricator-menu-item' => '0c96375e', @@ -2788,11 +2802,11 @@ celerity_register_resource_map(array( 'phabricator-paste-file-upload' => '0c96375e', 'phabricator-prefab' => '0c96375e', 'phabricator-project-tag-css' => '7839ae2d', - 'phabricator-remarkup-css' => '4984f83f', + 'phabricator-remarkup-css' => 'f6b9a4d4', 'phabricator-shaped-request' => '86f654e2', - 'phabricator-standard-page-view' => '4984f83f', + 'phabricator-standard-page-view' => 'f6b9a4d4', 'phabricator-tooltip' => '0c96375e', - 'phabricator-transaction-view-css' => '4984f83f', - 'syntax-highlighting-css' => '4984f83f', + 'phabricator-transaction-view-css' => 'f6b9a4d4', + 'syntax-highlighting-css' => 'f6b9a4d4', ), )); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index f2b2a160cb..db77d89d53 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -525,6 +525,9 @@ phutil_register_library_map(array( 'ManiphestView' => 'applications/maniphest/view/ManiphestView.php', 'MetaMTAConstants' => 'applications/metamta/constants/MetaMTAConstants.php', 'MetaMTANotificationType' => 'applications/metamta/constants/MetaMTANotificationType.php', + 'NotificationMessage' => 'applications/notification/constants/message/NotificationMessage.php', + 'NotificationPathname' => 'applications/notification/constants/pathname/NotificationPathname.php', + 'NotificationType' => 'applications/notification/constants/type/NotificationType.php', 'OwnersPackageReplyHandler' => 'applications/owners/OwnersPackageReplyHandler.php', 'PackageCreateMail' => 'applications/owners/mail/PackageCreateMail.php', 'PackageDeleteMail' => 'applications/owners/mail/PackageDeleteMail.php', @@ -735,8 +738,12 @@ phutil_register_library_map(array( 'PhabricatorMustVerifyEmailController' => 'applications/auth/controller/PhabricatorMustVerifyEmailController.php', 'PhabricatorMySQLFileStorageEngine' => 'applications/files/engine/PhabricatorMySQLFileStorageEngine.php', 'PhabricatorNotificationBuilder' => 'applications/notification/builder/PhabricatorNotificationBuilder.php', + 'PhabricatorNotificationConstants' => 'applications/notification/constants/base/PhabricatorNotificationConstants.php', 'PhabricatorNotificationController' => 'applications/notification/controller/PhabricatorNotificationController.php', + 'PhabricatorNotificationPanelController' => 'applications/notification/controller/PhabricatorNotificationPanelController.php', 'PhabricatorNotificationQuery' => 'applications/notification/PhabricatorNotificationQuery.php', + 'PhabricatorNotificationStory' => 'applications/notification/base/PhabricatorNotificationStory.php', + 'PhabricatorNotificationStoryTypeConstants' => 'applications/notification/constants/story/PhabricatorNotificationStoryTypeConstants.php', 'PhabricatorNotificationStoryView' => 'applications/notification/view/PhabricatorNotificationStoryView.php', 'PhabricatorNotificationTestController' => 'applications/notification/controller/PhabricatorNotificationTestController.php', 'PhabricatorNotificationView' => 'applications/notification/view/PhabricatorNotificationView.php', @@ -972,6 +979,7 @@ phutil_register_library_map(array( 'PhabricatorUserEmailPreferenceSettingsPanelController' => 'applications/people/controller/settings/panels/PhabricatorUserEmailPreferenceSettingsPanelController.php', 'PhabricatorUserEmailSettingsPanelController' => 'applications/people/controller/settings/panels/PhabricatorUserEmailSettingsPanelController.php', 'PhabricatorUserLog' => 'applications/people/storage/PhabricatorUserLog.php', + 'PhabricatorUserNotificationPreferenceSettingsPanelController' => 'applications/people/controller/settings/panels/PhabricatorUserNotificationPreferenceSettingsPanelController.php', 'PhabricatorUserOAuthInfo' => 'applications/people/storage/PhabricatorUserOAuthInfo.php', 'PhabricatorUserOAuthSettingsPanelController' => 'applications/people/controller/settings/panels/PhabricatorUserOAuthSettingsPanelController.php', 'PhabricatorUserPasswordSettingsPanelController' => 'applications/people/controller/settings/panels/PhabricatorUserPasswordSettingsPanelController.php', @@ -1518,6 +1526,9 @@ phutil_register_library_map(array( 'ManiphestTransactionType' => 'ManiphestConstants', 'ManiphestView' => 'AphrontView', 'MetaMTANotificationType' => 'MetaMTAConstants', + 'NotificationMessage' => 'PhabricatorNotificationConstants', + 'NotificationPathname' => 'PhabricatorNotificationConstants', + 'NotificationType' => 'PhabricatorNotificationConstants', 'OwnersPackageReplyHandler' => 'PhabricatorMailReplyHandler', 'PackageCreateMail' => 'PackageMail', 'PackageDeleteMail' => 'PackageMail', @@ -1697,6 +1708,8 @@ phutil_register_library_map(array( 'PhabricatorMustVerifyEmailController' => 'PhabricatorAuthController', 'PhabricatorMySQLFileStorageEngine' => 'PhabricatorFileStorageEngine', 'PhabricatorNotificationController' => 'PhabricatorController', + 'PhabricatorNotificationPanelController' => 'PhabricatorNotificationController', + 'PhabricatorNotificationStoryTypeConstants' => 'PhabricatorNotificationConstants', 'PhabricatorNotificationStoryView' => 'PhabricatorNotificationView', 'PhabricatorNotificationTestController' => 'PhabricatorNotificationController', 'PhabricatorNotificationView' => 'AphrontView', @@ -1898,6 +1911,7 @@ phutil_register_library_map(array( 'PhabricatorUserEmailPreferenceSettingsPanelController' => 'PhabricatorUserSettingsPanelController', 'PhabricatorUserEmailSettingsPanelController' => 'PhabricatorUserSettingsPanelController', 'PhabricatorUserLog' => 'PhabricatorUserDAO', + 'PhabricatorUserNotificationPreferenceSettingsPanelController' => 'PhabricatorUserSettingsPanelController', 'PhabricatorUserOAuthInfo' => 'PhabricatorUserDAO', 'PhabricatorUserOAuthSettingsPanelController' => 'PhabricatorUserSettingsPanelController', 'PhabricatorUserPasswordSettingsPanelController' => 'PhabricatorUserSettingsPanelController', diff --git a/src/aphront/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/configuration/AphrontDefaultApplicationConfiguration.php index 2608e0b2df..ebabff25b5 100644 --- a/src/aphront/configuration/AphrontDefaultApplicationConfiguration.php +++ b/src/aphront/configuration/AphrontDefaultApplicationConfiguration.php @@ -422,6 +422,7 @@ class AphrontDefaultApplicationConfiguration ), '/notification/test/' => 'PhabricatorNotificationTestController', + '/notification/panel/' => 'PhabricatorNotificationPanelController', '/flag/' => array( '' => 'PhabricatorFlagListController', 'view/(?P[^/]+)/' => 'PhabricatorFlagListController', diff --git a/src/applications/notification/builder/PhabricatorNotificationBuilder.php b/src/applications/notification/builder/PhabricatorNotificationBuilder.php index 1e123f88ad..8204dfa9de 100644 --- a/src/applications/notification/builder/PhabricatorNotificationBuilder.php +++ b/src/applications/notification/builder/PhabricatorNotificationBuilder.php @@ -39,6 +39,7 @@ final class PhabricatorNotificationBuilder { $null_view = new AphrontNullView(); + //TODO ADD NOTIFICATIONS HEADER foreach ($stories as $story) { $story->setHandles($handles); $view = $story->renderNotificationView(); diff --git a/src/applications/notification/controller/PhabricatorNotificationPanelController.php b/src/applications/notification/controller/PhabricatorNotificationPanelController.php new file mode 100644 index 0000000000..8cbcb606ea --- /dev/null +++ b/src/applications/notification/controller/PhabricatorNotificationPanelController.php @@ -0,0 +1,44 @@ +getRequest(); + $user = $request->getUser(); + + $query = new PhabricatorNotificationQuery(); + $query->setUserPHID($user->getPHID()); + $query->setLimit(15); + + $stories = $query->execute(); + + $builder = new PhabricatorNotificationBuilder($stories); + $notifications_view = $builder->buildView(); + + $json = array( + "content" => $stories ? + $notifications_view->render() : + "You currently have no notifications", + ); + + return id(new AphrontAjaxResponse())->setContent($json); + } +} diff --git a/src/applications/notification/storage/PhabricatorFeedStoryNotification.php b/src/applications/notification/storage/PhabricatorFeedStoryNotification.php index ca2416b3aa..c239c1e834 100644 --- a/src/applications/notification/storage/PhabricatorFeedStoryNotification.php +++ b/src/applications/notification/storage/PhabricatorFeedStoryNotification.php @@ -54,4 +54,22 @@ final class PhabricatorFeedStoryNotification extends PhabricatorFeedDAO { } } + /* should only be called when notifications are enabled */ + public function countUnread( + PhabricatorUser $user) { + + $conn = $this->establishConnection('r'); + + $data = queryfx_one( + $conn, + "SELECT COUNT(*) as count + FROM %T + WHERE userPHID = %s + AND hasViewed=0", + $this->getTableName(), + $user->getPHID()); + + return $data['count']; + } + } diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php index ea98182c4e..1ece236fe3 100644 --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -371,6 +371,54 @@ final class PhabricatorStandardPageView extends AphrontPageView { ' '); } + $notification_header = ''; + $notification_dropdown = ''; + + if (PhabricatorEnv::getEnvConfig('notification.enabled') && + $user->isLoggedIn()) { + $aphlict_object_id = 'aphlictswfobject'; + + $aphlict_content = phutil_render_tag( + 'object', + array( + 'classid' => 'clsid:d27cdb6e-ae6d-11cf-96b8-444553540000', + ), + ''. + ''. + ''. + ''); + + Javelin::initBehavior('aphlict-dropdown', array()); + + + $notification_indicator = + javelin_render_tag( + 'td', + array( + 'sigil' => 'aphlict-indicator', + 'id' => 'phabricator-notification-indicator', + ), + id(new PhabricatorFeedStoryNotification) + ->countUnread($user)); + + $notification_header = + $notification_indicator. + ''. + '
'. + $aphlict_content. + '
'. + ''; + $notification_dropdown = + javelin_render_tag( + 'div', + array( + 'sigil' => 'aphlict-dropdown', + 'id' => 'phabricator-notification-dropdown', + ), + ''); + } + $header_chrome = null; $footer_chrome = null; if ($this->getShowChrome()) { @@ -400,8 +448,10 @@ final class PhabricatorStandardPageView extends AphrontPageView { ''. $login_stuff. ''. + $notification_header. ''. - ''; + ''. + $notification_dropdown; $footer_chrome = '
'. $foot_links. diff --git a/webroot/rsrc/css/application/base/standard-page-view.css b/webroot/rsrc/css/application/base/standard-page-view.css index c5c3d64700..296d8c2e47 100644 --- a/webroot/rsrc/css/application/base/standard-page-view.css +++ b/webroot/rsrc/css/application/base/standard-page-view.css @@ -238,3 +238,54 @@ a.handle-disabled { cursor: pointer; } + +#phabricator-notification-indicator { + background-color: #0069a6; + text-align: center; + font-size: 20px; + vertical-align: middle; + height: 40px; + width: 40px; + + -moz-user-select: -moz-none; + -khtml-user-select: none; + -webkit-user-select: none; + -o-user-select: none; +} + +#phabricator-notification-indicator:hover { + cursor: pointer; + background-color: #0089d9; +} + +#phabricator-notification-dropdown { + + word-wrap: break-word; + + overflow-y: auto; + position: absolute; + width: 600px; + height: 300px; + right: 0px; + + + background-color: #f0f0f0; + border: 1px solid darkgrey; + box-shadow: 3px 3px #ccc; + -webkit-box-shadow: 3px 3px #ccc; + -moz-box-shadow: 3px 3px #ccc; + + z-index: 3; +} + +.phabricator-notification-story-head { + border-bottom: 1px solid darkgray; + padding-top: 2px; + padding-bottom: 2px; +} + + +.phabricator-notification-frame { + padding-left: 5px; + padding-right: 10px; +} diff --git a/webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js b/webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js new file mode 100644 index 0000000000..29e9c185cc --- /dev/null +++ b/webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js @@ -0,0 +1,54 @@ +/** + * @provides javelin-behavior-aphlict-dropdown + * @requires javelin-behavior + * javelin-aphlict + * javelin-util + * javelin-request + * javelin-stratcom + */ + +JX.behavior('aphlict-dropdown', function(config) { + var dropdown = JX.$('phabricator-notification-dropdown'); + var indicator = JX.$('phabricator-notification-indicator'); + var visible = false; + + + JX.DOM.hide(dropdown); + + //populate panel + (new JX.Request('/notification/panel/', + function(response) { + JX.DOM.setContent(dropdown, JX.$H(response.content)); + })).send(); + + + JX.Stratcom.listen( + 'click', + null, + function(e) { + if(e.getNode('aphlict-dropdown') || + e.getNode('aphlict-indicator')) { + // Click is inside the dropdown, or on indicator + return; + } + + JX.DOM.hide(dropdown); + visible = false; + }); + + + JX.DOM.listen( + indicator, + 'click', + null, + function(e) { + if(visible) { + JX.DOM.hide(dropdown); + } else { + JX.DOM.show(dropdown); + } + visible = !visible; + } + ) + +});