MDL-54701 message: add user profile links to popovers

Make the user name link to the user's profile page for the message and
notification popovers.
This commit is contained in:
Ryan Wyllie 2016-07-18 06:01:40 +00:00 committed by Mark Nelson
parent 540fec1e86
commit c826fa230a
7 changed files with 204 additions and 41 deletions

View File

@ -27,9 +27,9 @@
*/
define(['jquery', 'theme_bootstrapbase/bootstrap', 'core/ajax', 'core/templates', 'core/str',
'core/notification', 'core/custom_interaction_events', 'core/mdl_popover_controller',
'core_message/message_repository'],
'core_message/message_repository', 'core/url'],
function($, bootstrap, ajax, templates, str, debugNotification, customEvents,
PopoverController, messageRepo) {
PopoverController, messageRepo, URL) {
var SELECTORS = {
MARK_ALL_READ_BUTTON: '.mark-all-read-button',
@ -38,6 +38,7 @@ define(['jquery', 'theme_bootstrapbase/bootstrap', 'core/ajax', 'core/templates'
CONTENT: '.messages',
CONTENT_ITEM_CONTAINER: '.content-item-container',
EMPTY_MESSAGE: '.empty-message',
LINK_URL: '[data-link-url]',
};
/**
@ -112,6 +113,26 @@ define(['jquery', 'theme_bootstrapbase/bootstrap', 'core/ajax', 'core/templates'
}
};
/**
* Navigate the browser to the link URL for the item, if it has one.
*
* @method navigateToLinkURL
* @param {jQuery} item The link element
* @param {bool} item Should the URL be opened in a new tab or not.
*/
MessagePopoverController.prototype.navigateToLinkURL = function(item, newTab) {
var url = item.attr('data-link-url');
newTab = newTab || false;
if (url) {
if (newTab) {
window.open(url, '_blank');
} else {
window.location.assign(url);
}
}
};
/**
* Show the unread notification count badge on the menu toggle if there
* are unread notifications, otherwise hide it.
@ -167,6 +188,15 @@ define(['jquery', 'theme_bootstrapbase/bootstrap', 'core/ajax', 'core/templates'
if (messages.length) {
$.each(messages, function(index, message) {
message.contexturl = URL.relativeUrl('/message/index.php', {
user: this.userId,
id: message.userid,
}, true);
message.profileurl = URL.relativeUrl('/user/profile.php', {
id: message.userid,
});
var promise = templates.render('message/message_content_item', message);
promise.then(function(html, js) {
container.append(html);
@ -240,6 +270,10 @@ define(['jquery', 'theme_bootstrapbase/bootstrap', 'core/ajax', 'core/templates'
* @method registerEventListeners
*/
MessagePopoverController.prototype.registerEventListeners = function() {
customEvents.define(this.root, [
customEvents.events.keyboardActivate,
]);
// Update the notification information when the menu is opened.
this.root.on(this.events().menuOpened, function() {
this.hideUnreadCount();
@ -255,6 +289,31 @@ define(['jquery', 'theme_bootstrapbase/bootstrap', 'core/ajax', 'core/templates'
this.renderUnreadCount();
this.updateButtonAriaLabel();
}.bind(this));
// Load more messages when we scroll to the bottom of the open menu.
this.root.on(customEvents.events.scrollBottom, function() {
this.loadMoreMessages();
}.bind(this));
// Follow the link URL if the user activates it.
this.root.on('click', SELECTORS.LINK_URL, function(e) {
var linkItem = $(e.target).closest(SELECTORS.LINK_URL);
// Open link in a new tab if the user ctrl + click or command + click.
if (e.ctrlKey || e.metaKey) {
this.navigateToLinkURL(linkItem, true);
} else {
this.navigateToLinkURL(linkItem, false);
}
e.stopPropagation();
e.preventDefault();
}.bind(this));
// Follow the link URL if the user activates it.
this.root.on(customEvents.events.keyboardActivate, SELECTORS.LINK_URL, function(e) {
var linkItem = $(e.target).closest(SELECTORS.LINK_URL);
this.navigateToLinkURL(linkItem, false);
e.stopPropagation();
}.bind(this));
};
return MessagePopoverController;

View File

@ -19,21 +19,23 @@
* @module core_message/message_repository
* @class message_repository
* @package message
* @copyright 2015 Ryan Wyllie <ryan@moodle.com>
* @copyright 2016 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 3.2
*/
define(['jquery', 'core/ajax', 'core/notification'], function($, ajax, notification) {
var query = function(args) {
/*
// Normalise the arguments to use limit/offset rather than limitnum/limitfrom.
if (typeof args.limit === 'undefined') {
args.limit = 20;
args.limit = 0;
}
if (typeof args.offset === 'undefined') {
args.offset = 0;
}
*/
args.limitfrom = args.offset;
args.limitnum = args.limit;
delete args.limit;
delete args.offset;

View File

@ -44,6 +44,7 @@ define(['jquery', 'theme_bootstrapbase/bootstrap', 'core/ajax', 'core/templates'
EMPTY_MESSAGE: '.empty-message',
CONTENT_BODY_SHORT: '.content-body-short',
CONTENT_BODY_FULL: '.content-body-full',
LINK_URL: '[data-link-url]',
};
var PROCESSOR_NAME = 'popup';
@ -501,16 +502,22 @@ define(['jquery', 'theme_bootstrapbase/bootstrap', 'core/ajax', 'core/templates'
};
/**
* Navigate the browser to the content URL for the content item, if it has one.
* Navigate the browser to the link URL for the item, if it has one.
*
* @method navigateToContextURL
* @param item jQuery object representing the content item
* @method navigateToLinkURL
* @param {jQuery} item The link element
* @param {bool} item Should the URL be opened in a new tab or not.
*/
NotificationPopoverController.prototype.navigateToContextURL = function(item) {
var url = item.attr('data-context-url');
NotificationPopoverController.prototype.navigateToLinkURL = function(item, newTab) {
var url = item.attr('data-link-url');
newTab = newTab || false;
if (url) {
window.location.assign(url);
if (newTab) {
window.open(url, '_blank');
} else {
window.location.assign(url);
}
}
};
@ -602,6 +609,7 @@ define(['jquery', 'theme_bootstrapbase/bootstrap', 'core/ajax', 'core/templates'
NotificationPopoverController.prototype.registerEventListeners = function() {
customEvents.define(this.root, [
customEvents.events.activate,
customEvents.events.keyboardActivate,
customEvents.events.next,
customEvents.events.previous,
customEvents.events.asterix,
@ -674,10 +682,23 @@ define(['jquery', 'theme_bootstrapbase/bootstrap', 'core/ajax', 'core/templates'
e.stopPropagation();
}.bind(this));
// Follow the context URL if the user activates the content item.
this.root.on(customEvents.events.activate, SELECTORS.CONTENT_ITEM_CONTAINER, function(e) {
var contentItem = $(e.target).closest(SELECTORS.CONTENT_ITEM_CONTAINER);
this.navigateToContextURL(contentItem);
// Follow the link URL if the user activates it.
this.root.on('click', SELECTORS.LINK_URL, function(e) {
var linkItem = $(e.target).closest(SELECTORS.LINK_URL);
// Open link in a new tab if the user ctrl + click or command + click.
if (e.ctrlKey || e.metaKey) {
this.navigateToLinkURL(linkItem, true);
} else {
this.navigateToLinkURL(linkItem, false);
}
e.stopPropagation();
e.preventDefault();
}.bind(this));
// Follow the link URL if the user activates it.
this.root.on(customEvents.events.keyboardActivate, SELECTORS.LINK_URL, function(e) {
var linkItem = $(e.target).closest(SELECTORS.LINK_URL);
this.navigateToLinkURL(linkItem, false);
e.stopPropagation();
}.bind(this));

View File

@ -34,18 +34,25 @@
}
}}
<div class="content-item-container"
<div class="content-item-container {{^isread}}unread{{/isread}}"
role="listitem"
aria-expanded="false"
aria-label=""
tabindex="0"
{{#contexturl}}data-context-url="{{{.}}}"{{/contexturl}}>
tabindex="0">
{{#contexturl}}
<a class="content-item-link" href="{{{.}}}">
{{/contexturl}}
<div class="content-item">
{{{picture}}}
<div class="profile-image-container">
<img src="{{{profileimageurl}}}" />
</div>
<div class="content-item-body">
<h3>{{name}}</h3>
<h3 data-link-url="{{{profileurl}}}" role="link">{{fullname}}</h3>
<p>{{lastmessage}}</p>
</div>
</div>
{{#contexturl}}
</a>
{{/contexturl}}
</div>

View File

@ -38,16 +38,14 @@
role="listitem"
aria-expanded="false"
aria-label="{{subject}}"
tabindex="0"
{{#contexturl}}data-context-url="{{{.}}}"{{/contexturl}}>
tabindex="0">
{{#contexturl}}
<a class="content-item-link" href="{{{.}}}">
{{/contexturl}}
<div class="content-item">
<div class="content-item-header">
<h3>
{{#userfromprofileurl}}<a href="{{{.}}}">{{/userfromprofileurl}}
{{userfromfullname}}
{{#userfromprofileurl}}</a>{{/userfromprofileurl}}
</h3>
<h3 {{#userfromprofileurl}}data-link-url="{{{.}}}" role="link"{{/userfromprofileurl}}>{{userfromfullname}}</h3>
</div>
<div class="content-item-body">
<div class="content-body-short">
@ -92,4 +90,7 @@
</span>
</div>
</div>
{{#contexturl}}
</a>
{{/contexturl}}
</div>

View File

@ -83,6 +83,21 @@
}
}
[data-link-url] {
cursor: pointer;
color: @linkColor;
&:hover,
&:active {
outline: 0;
}
&:hover,
&:focus {
color: @linkColorHover;
text-decoration: underline;
}
}
&.collapsed {
.mdl-popover-container {
height: 0;
@ -307,8 +322,14 @@
opacity: 0.8;
}
&[data-context-url] {
cursor: pointer;
.content-item-link {
color: inherit;
text-decoration: none;
&:hover {
color: inherit;
text-decoration: none;
}
}
.content-item {
@ -568,29 +589,44 @@
box-sizing: border-box;
padding: 5px;
height: 85px;
cursor: pointer;
&:hover {
opacity: 0.8;
}
&[data-context-url] {
cursor: pointer;
.content-item-link {
color: inherit;
text-decoration: none;
&:hover {
color: inherit;
text-decoration: none;
}
}
.content-item {
height: 100%;
width: 100%;
img {
.profile-image-container {
height: 100%;
width: 20%;
display: inline-block;
vertical-align: middle;
img {
height: 100%;
display: inline-block;
vertical-align: middle;
border-radius: 50%;
}
}
.content-item-body {
display: inline-block;
box-sizing: border-box;
vertical-align: middle;
width: ~"calc(100% - 68px)";
width: 75%;
height: 100%;
h3 {
@ -608,6 +644,9 @@
&:last-child {
border-bottom: none;
}
&.unread {
background-color: #effaff;
}
}
}
&.loading {

View File

@ -7107,6 +7107,19 @@ body.path-question-type .mform fieldset.hidden {
.mdl-popover .mdl-popover-container .mdl-popover-content-container.loading .empty-message {
display: none;
}
.mdl-popover [data-link-url] {
cursor: pointer;
color: #0070a8;
}
.mdl-popover [data-link-url]:hover,
.mdl-popover [data-link-url]:active {
outline: 0;
}
.mdl-popover [data-link-url]:hover,
.mdl-popover [data-link-url]:focus {
color: #003d5c;
text-decoration: underline;
}
.mdl-popover.collapsed .mdl-popover-container {
height: 0;
overflow: hidden;
@ -7286,8 +7299,13 @@ body.path-question-type .mform fieldset.hidden {
.mdl-popover-notifications.mdl-popover .mdl-popover-container .mdl-popover-content-container .mdl-popover-content .content-item-container:hover {
opacity: 0.8;
}
.mdl-popover-notifications.mdl-popover .mdl-popover-container .mdl-popover-content-container .mdl-popover-content .content-item-container[data-context-url] {
cursor: pointer;
.mdl-popover-notifications.mdl-popover .mdl-popover-container .mdl-popover-content-container .mdl-popover-content .content-item-container .content-item-link {
color: inherit;
text-decoration: none;
}
.mdl-popover-notifications.mdl-popover .mdl-popover-container .mdl-popover-content-container .mdl-popover-content .content-item-container .content-item-link:hover {
color: inherit;
text-decoration: none;
}
.mdl-popover-notifications.mdl-popover .mdl-popover-container .mdl-popover-content-container .mdl-popover-content .content-item-container .content-item {
width: calc(100% - 45px);
@ -7474,26 +7492,39 @@ body.path-question-type .mform fieldset.hidden {
box-sizing: border-box;
padding: 5px;
height: 85px;
cursor: pointer;
}
.mdl-popover-messages.mdl-popover .mdl-popover-container .mdl-popover-content-container .mdl-popover-content .content-item-container:hover {
opacity: 0.8;
}
.mdl-popover-messages.mdl-popover .mdl-popover-container .mdl-popover-content-container .mdl-popover-content .content-item-container[data-context-url] {
cursor: pointer;
.mdl-popover-messages.mdl-popover .mdl-popover-container .mdl-popover-content-container .mdl-popover-content .content-item-container .content-item-link {
color: inherit;
text-decoration: none;
}
.mdl-popover-messages.mdl-popover .mdl-popover-container .mdl-popover-content-container .mdl-popover-content .content-item-container .content-item-link:hover {
color: inherit;
text-decoration: none;
}
.mdl-popover-messages.mdl-popover .mdl-popover-container .mdl-popover-content-container .mdl-popover-content .content-item-container .content-item {
height: 100%;
width: 100%;
}
.mdl-popover-messages.mdl-popover .mdl-popover-container .mdl-popover-content-container .mdl-popover-content .content-item-container .content-item img {
.mdl-popover-messages.mdl-popover .mdl-popover-container .mdl-popover-content-container .mdl-popover-content .content-item-container .content-item .profile-image-container {
height: 100%;
width: 20%;
display: inline-block;
}
.mdl-popover-messages.mdl-popover .mdl-popover-container .mdl-popover-content-container .mdl-popover-content .content-item-container .content-item .profile-image-container img {
height: 100%;
display: inline-block;
vertical-align: middle;
border-radius: 50%;
}
.mdl-popover-messages.mdl-popover .mdl-popover-container .mdl-popover-content-container .mdl-popover-content .content-item-container .content-item .content-item-body {
display: inline-block;
box-sizing: border-box;
vertical-align: middle;
width: calc(100% - 68px);
width: 75%;
height: 100%;
}
.mdl-popover-messages.mdl-popover .mdl-popover-container .mdl-popover-content-container .mdl-popover-content .content-item-container .content-item .content-item-body h3 {
@ -7508,6 +7539,9 @@ body.path-question-type .mform fieldset.hidden {
.mdl-popover-messages.mdl-popover .mdl-popover-container .mdl-popover-content-container .mdl-popover-content .content-item-container:last-child {
border-bottom: none;
}
.mdl-popover-messages.mdl-popover .mdl-popover-container .mdl-popover-content-container .mdl-popover-content .content-item-container.unread {
background-color: #effaff;
}
.mdl-popover-messages.mdl-popover .mdl-popover-container .mdl-popover-content-container.loading .mdl-popover-content .messages:empty + .empty-message {
display: none;
}