Merge "Redo the notification badges"

This commit is contained in:
jenkins-bot 2016-07-29 22:53:34 +00:00 committed by Gerrit Code Review
commit 5f04fc334c
22 changed files with 186 additions and 211 deletions

View file

@ -888,12 +888,9 @@ class EchoHooks {
// HACK: inverted icons only work in the "MediaWiki" OOUI theme
// Avoid flashes in skins that don't use it (T111821)
$sk->getOutput()->setupOOUI( strtolower( $sk->getSkinName() ), $sk->getOutput()->getLanguage()->getDir() );
$oouiImageClass = get_class( OOUI\Theme::singleton() ) === 'OOUI\\MediaWikiTheme'
? 'oo-ui-image-invert'
: '';
$msgLinkClasses = array( "mw-echo-notifications-badge mw-echo-notification-badge-nojs $oouiImageClass oo-ui-iconElement oo-ui-iconElement-icon oo-ui-icon-speechBubbles" );
$alertLinkClasses = array( "mw-echo-notifications-badge mw-echo-notification-badge-nojs $oouiImageClass oo-ui-iconElement oo-ui-iconElement-icon" );
$msgLinkClasses = array( "mw-echo-notifications-badge", "mw-echo-notification-badge-nojs" );
$alertLinkClasses = array( "mw-echo-notifications-badge", "mw-echo-notification-badge-nojs" );
$hasUnseen = false;
if (
@ -903,25 +900,30 @@ class EchoHooks {
) {
$msgLinkClasses[] = 'mw-echo-unseen-notifications';
$hasUnseen = true;
} elseif ( $msgCount === 0 ) {
$msgLinkClasses[] = 'mw-echo-notifications-badge-all-read';
}
$alertIcon = "bell";
if (
$alertCount != 0 && // no unread notifications
$alertNotificationTimestamp !== false && // should already always be false if count === 0
( $seenAlertTime === null || $seenAlertTime < $alertNotificationTimestamp->getTimestamp( TS_ISO_8601 ) ) // all notifications have already been seen
) {
$alertLinkClasses[] = 'mw-echo-unseen-notifications';
$alertIcon = "bellOn";
$hasUnseen = true;
} elseif ( $alertCount === 0 ) {
$alertLinkClasses[] = 'mw-echo-notifications-badge-all-read';
}
$alertLinkClasses[] = 'oo-ui-icon-' . $alertIcon;
$alertLink = array(
'href' => $url,
'text' => $alertText,
'active' => ( $url == $title->getLocalUrl() ),
'class' => $alertLinkClasses,
'data' => array(
'counter-num' => $alertCount,
'counter-text' => $alertText,
),
);
$insertUrls = array(
@ -933,6 +935,10 @@ class EchoHooks {
'text' => $msgText,
'active' => ( $url == $title->getLocalUrl() ),
'class' => $msgLinkClasses,
'data' => array(
'counter-num' => $msgCount,
'counter-text' => $msgText,
),
);
$insertUrls['notifications-notice'] = $msgLink;

View file

@ -45,9 +45,6 @@ $wgResourceModules += array(
'modern' => array(
'styles/mw.echo.ui.NotificationBadgeWidget.modern.less'
),
'vector' => array(
'styles/mw.echo.ui.NotificationBadgeWidget.vector.less'
),
),
'dependencies' => array(
'ext.echo.ui',
@ -262,6 +259,9 @@ $wgResourceModules += array(
'vector' => array(
'nojs/mw.echo.badge.vector.less',
),
'modern' => array(
'nojs/mw.echo.badge.modern.less',
),
),
'targets' => array( 'desktop', 'mobile' ),
),

View file

@ -1,11 +1,13 @@
@badge-padding: 0.12em;
@badge-icon-size: 1.1em;
@badge-distance-adjustment: -0.4em;
@badge-distance-adjustment: 1em;
@notification-background-unseen: #dce8ff;
@notification-background-unread: white;
@notification-background-read: #f1f1f1;
@badge-counter-background-seen: #71777D;
@notification-text-color: #111;
@notification-body-color: #777;

View file

@ -1,4 +1,2 @@
/* Would use #333 here but opacity is applied to whole badge to lighten the icon */
@badge-text-color-idle: #000;
@badge-background-unseen-alert: #dea4a2;
@badge-background-unseen-message: #b7cffb;
@badge-counter-background-unseen-alert: #dea4a2;
@badge-counter-background-unseen-message: #b7cffb;

View file

@ -1,3 +1,2 @@
@badge-text-color-idle: white;
@badge-background-unseen-alert: #d11813;
@badge-background-unseen-message: #5d95ff;
@badge-counter-background-unseen-alert: #C33;
@badge-counter-background-unseen-message: #36C;

View file

@ -15,8 +15,10 @@
var myWidget, echoApi,
$existingAlertLink = $( '#pt-notifications-alert a' ),
$existingMessageLink = $( '#pt-notifications-notice a' ),
numAlerts = $existingAlertLink.text(),
numMessages = $existingMessageLink.text(),
numAlerts = $existingAlertLink.attr( 'data-counter-num' ),
numMessages = $existingMessageLink.attr( 'data-counter-num' ),
badgeLabelAlerts = $existingAlertLink.attr( 'data-counter-text' ),
badgeLabelMessages = $existingMessageLink.attr( 'data-counter-text' ),
hasUnseenAlerts = $existingAlertLink.hasClass( 'mw-echo-unseen-notifications' ),
hasUnseenMessages = $existingMessageLink.hasClass( 'mw-echo-unseen-notifications' ),
// Store links
@ -74,9 +76,10 @@
messageModelManager,
{
$overlay: mw.echo.ui.$overlay,
numItems: numMessages,
numItems: Number( numMessages ),
hasUnseen: hasUnseenMessages,
badgeIcon: 'speechBubbles',
badgeIcon: 'tray',
badgeLabel: badgeLabelMessages,
links: links,
href: $existingMessageLink.attr( 'href' )
}
@ -107,12 +110,10 @@
alertController,
alertModelManager,
{
numItems: numAlerts,
numItems: Number( numAlerts ),
badgeLabel: badgeLabelAlerts,
hasUnseen: hasUnseenAlerts,
badgeIcon: {
seen: 'bell',
unseen: 'bellOn'
},
badgeIcon: 'bell',
links: links,
$overlay: mw.echo.ui.$overlay,
href: $existingAlertLink.attr( 'href' )

View file

@ -4,17 +4,8 @@
"bell": {
"file": "../bell.svg"
},
"bellOn": {
"file": {
"ltr": "../bellOn-ltr.svg",
"rtl": "../bellOn-rtl.svg"
}
},
"speechBubbles": {
"file": {
"ltr": "../speechBubbles-ltr.svg",
"rtl": "../speechBubbles-rtl.svg"
}
"tray": {
"file": "../tray.svg"
}
}
}

View file

@ -1,26 +1,11 @@
{
"prefix": "oo-ui-icon",
"variants": {
"invert": {
"color": "#FFFFFF",
"global": true
}
},
"images": {
"bell": {
"file": "../bell.svg"
},
"bellOn": {
"file": {
"ltr": "../bellOn-ltr.svg",
"rtl": "../bellOn-rtl.svg"
}
},
"speechBubbles": {
"file": {
"ltr": "../speechBubbles-ltr.svg",
"rtl": "../speechBubbles-rtl.svg"
}
"tray": {
"file": "../tray.svg"
}
}
}

4
modules/icons/tray.svg Normal file
View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M3 13.35l1.8-7.2c.2-.996.81-1.8 1.8-1.8h10.8c.99 0 1.6.867 1.8 1.8l1.8 7.2v4.5c0 .99-.81 1.8-1.8 1.8H4.8c-.99 0-1.8-.81-1.8-1.8v-4.5zm6.96 1.8h4.08c-.49.557-1.212.9-2.04.9a2.68 2.68 0 0 1-2.04-.9h4.08c.414-.472.66-1.098.66-1.8h4.14l-1.44-7.2H6.6l-1.44 7.2H9.3c0 .702.246 1.328.66 1.8z" id="tray"/>
</svg>

After

Width:  |  Height:  |  Size: 441 B

View file

@ -1,33 +1,17 @@
@import '../echo.variables';
@import 'mediawiki.mixins';
/* We have to include the #pt-notifications selector due to monobook */
.mw-echo-notifications-badge {
#pt-notifications-alert &,
#pt-notifications-notice & {
font-weight: bold;
position: relative;
display: block;
width: 30px;
top: -5px;
font-size: 0;
cursor: pointer;
text-decoration: none;
margin-top: -0.2em;
display: inline-block;
padding: @badge-padding (@badge-padding + 0.25em) @badge-padding (@badge-padding + 0.25em + 1.1em);
// Override height, width, min-height and min-width from OOUI IconElement
height: 1.05em;
min-height: 1.05em;
width: auto;
min-width: 0;
body.ltr & {
background-position: @badge-padding 0.1em;
}
body.rtl & {
// CSS sucks, we can't specify background-position-x from the right
// You can in Firefox with background-position-x: right 0.12em;
// but that's not supported in any other browsers
background-position: ~"calc(100% - @{badge-padding}) 0.1em";
}
background-size: @badge-icon-size;
background-repeat: no-repeat;
&:hover,
&:active,
@ -39,20 +23,64 @@
&-dimmed {
opacity: 0.4;
}
// Background icon
&:before {
position: absolute;
display: inline-block;
cursor: pointer;
opacity: 0.8;
left: 0;
}
// Counter
&:after {
position: absolute;
display: inline-block;
cursor: pointer;
top: 10px;
left: 40%;
// The original font-size was 12px, we had
// to override it in the parent so the original
// number is hidden, so we rewrite it here. This
// is far from perfect, but it will make sure
// that screen-readers can still see the number that
// is outputted in the original <a> link while letting
// us style the boxed number the way we want to.
// We want 0.8em of the original 12px font size
font-size: 12px * 0.8;
font-weight: bold;
padding: 0 0.3em;
border: 1px solid white;
border-radius: 2px;
background-color: @badge-counter-background-seen;
content: attr(data-counter-text);
color: white;
}
+ .oo-ui-popupWidget {
margin-top: 7px;
}
&.mw-echo-notifications-badge-all-read {
opacity: 0.625;
&::after {
visibility: hidden;
}
}
}
#pt-notifications-alert & {
&:before {
/* @embed */
content: url('../icons/bell.svg');
}
}
#pt-notifications-notice & {
margin-left: @badge-distance-adjustment;
body.ltr & + .oo-ui-popupWidget {
// Only add a left adjustment for LTR pages.
// In LTR, the correction above moves the badge from the left
// which means we have to adjust the positioning of the anchor
// to be in the middle of the badge.
// In RTL, however, the above will be flipped to margin-right
// which doesn't affect the positioning of the anchor and hence
// doesn't require a correction.
left: (1em + @badge-distance-adjustment);
&:before {
/* @embed */
content: url('../icons/tray.svg');
}
}
}

View file

@ -0,0 +1,8 @@
.mw-echo-notifications-badge {
#pt-notifications-alert &,
#pt-notifications-notice & {
&:before {
z-index: 0;
}
}
}

View file

@ -3,28 +3,21 @@
/* We have to include the #pt-notifications selector due to monobook */
.mw-echo-notifications-badge {
#pt-notifications-alert &,
#pt-notifications-notice & {
color: @badge-text-color-idle;
opacity: 0.6;
&:hover,
&:active,
&:focus {
background-color: transparent;
}
&-dimmed {
opacity: 0.2;
}
}
&.oo-ui-flaggedElement-unseen,
&.mw-echo-unseen-notifications {
#pt-notifications-alert & {
background-color: @badge-background-unseen-alert;
#pt-notifications-alert &:after {
background-color: @badge-counter-background-unseen-alert;
color: #000;
}
#pt-notifications-notice & {
background-color: @badge-background-unseen-message;
#pt-notifications-notice &:after {
background-color: @badge-counter-background-unseen-message;
color: #000;
}
}
}
#p-personal #pt-notifications-alert,
#p-personal #pt-notifications-notice {
display: inline-block;
}

View file

@ -3,26 +3,14 @@
/* We have to include the #pt-notifications selector due to monobook */
.mw-echo-notifications-badge {
#pt-notifications-alert &,
#pt-notifications-notice & {
border-radius: 0.2em;
background-color: #d2d2d2;
font-size: 1.125em;
color: @badge-text-color-idle;
&:hover,
&:active,
&:focus {
background-color: #c2c2c2;
}
}
&.oo-ui-flaggedElement-unseen,
&.mw-echo-unseen-notifications {
#pt-notifications-alert & {
background-color: @badge-background-unseen-alert;
#pt-notifications-alert &:after {
background-color: @badge-counter-background-unseen-alert;
}
#pt-notifications-notice & {
background-color: @badge-background-unseen-message;
#pt-notifications-notice &:after {
background-color: @badge-counter-background-unseen-message;
}
}
}

View file

@ -9,7 +9,6 @@
}
// Badge
> .oo-ui-buttonElement-button {
padding-right: (@badge-padding + 0.25em);
&:hover {
.oo-ui-labelElement-label {
color: black !important;
@ -24,13 +23,3 @@
}
}
// These rules are outside of the LESS nesting because they don't work inside
// the nested rule. These should be very strong so as to override the base styles
#pt-notifications-alert .mw-echo-notifications-badge.oo-ui-flaggedElement-unseen {
background-color: @badge-background-unseen-alert;
}
#pt-notifications-notice .mw-echo-notifications-badge.oo-ui-flaggedElement-unseen {
background-color: @badge-background-unseen-message;
}

View file

@ -1,12 +0,0 @@
@import '../echo.variables';
@import '../echo.variables.vector';
// These rules are outside of the LESS nesting because they don't work inside
// the nested rule. These should be very strong so as to override the base styles
#pt-notifications-alert .mw-echo-notifications-badge.oo-ui-flaggedElement-unseen {
background-color: @badge-background-unseen-alert;
}
#pt-notifications-notice .mw-echo-notifications-badge.oo-ui-flaggedElement-unseen {
background-color: @badge-background-unseen-message;
}

View file

@ -7,14 +7,6 @@
*
* @constructor
* @param {Object} [config] Configuration object
* @cfg {string|Object} [badgeIcon] The icons to use for this button.
* If this is a string, it will be used as the icon regardless of the state.
* If it is an object, it must include
* the properties 'unseen' and 'seen' with icons attached to both. For example:
* { badgeIcon: {
* unseen: 'bellOn',
* seen: 'bell'
* } }
* @cfg {string} [href] URL the badge links to
*/
mw.echo.ui.BadgeLinkWidget = function MwEchoUiBadgeLinkWidget( config ) {
@ -26,11 +18,13 @@
// Mixin constructors
OO.ui.mixin.LabelElement.call( this, $.extend( { $label: this.$element }, config ) );
OO.ui.mixin.ButtonElement.call( this, $.extend( { $button: this.$element }, config ) );
OO.ui.mixin.IconElement.call( this, $.extend( { $icon: this.$element }, config ) );
OO.ui.mixin.TitledElement.call( this, $.extend( { $titled: this.$element }, config ) );
OO.ui.mixin.FlaggedElement.call( this, $.extend( {}, config, { $flagged: this.$element } ) );
this.$element.addClass( 'mw-echo-notifications-badge' );
this.$element
.addClass( 'mw-echo-notifications-badge' );
this.setCount( config.numItems, config.label );
if ( config.href !== undefined && OO.ui.isSafeUrl( config.href ) ) {
this.$element.attr( 'href', config.href );
@ -40,10 +34,23 @@
OO.inheritClass( mw.echo.ui.BadgeLinkWidget, OO.ui.Widget );
OO.mixinClass( mw.echo.ui.BadgeLinkWidget, OO.ui.mixin.LabelElement );
OO.mixinClass( mw.echo.ui.BadgeLinkWidget, OO.ui.mixin.ButtonElement );
OO.mixinClass( mw.echo.ui.BadgeLinkWidget, OO.ui.mixin.IconElement );
OO.mixinClass( mw.echo.ui.BadgeLinkWidget, OO.ui.mixin.TitledElement );
OO.mixinClass( mw.echo.ui.BadgeLinkWidget, OO.ui.mixin.FlaggedElement );
mw.echo.ui.BadgeLinkWidget.static.tagName = 'a';
/**
* Set the count labels for this button.
*
* @param {number} numItems Number of items
* @param {string} [label] Label of the button. Defaults to the item number.
*/
mw.echo.ui.BadgeLinkWidget.prototype.setCount = function ( numItems, label ) {
label = label || numItems;
this.$element
.toggleClass( 'mw-echo-notifications-badge-all-read', !numItems )
.attr( 'data-counter-num', numItems )
.attr( 'data-counter-text', label );
};
} )( mediaWiki, jQuery );

View file

@ -12,17 +12,12 @@
* @cfg {string|string[]} [type='message'] The type or array of types of
* notifications that are in this model. They can be 'alert', 'message' or
* an array of both. Defaults to 'message'
* @cfg {number} [numItems=0] How many items are in the button display
* @cfg {number} [numItems=0] The number of items that are in the button display
* @cfg {string} [badgeLabel=0] The initial label for the badge. This is the
* formatted version of the number of items in the badge.
* @cfg {boolean} [hasUnseen=false] Whether there are unseen items
* @cfg {number} [popupWidth=450] The width of the popup
* @cfg {string|Object} [badgeIcon] The icons to use for this button.
* If this is a string, it will be used as the icon regardless of the state.
* If it is an object, it must include
* the properties 'unseen' and 'seen' with icons attached to both. For example:
* { badgeIcon: {
* unseen: 'bellOn',
* seen: 'bell'
* } }
* @cfg {string} [badgeIcon] Icon to use for the popup header
* @cfg {string} [href] URL the badge links to
* @cfg {jQuery} [$overlay] A jQuery element functioning as an overlay
* for popups.
@ -57,18 +52,18 @@
this.maxNotificationCount = mw.config.get( 'wgEchoMaxNotificationCount' );
this.numItems = config.numItems || 0;
this.badgeIcon = config.badgeIcon || {};
this.badgeLabel = config.badgeLabel || this.numItems;
this.hasRunFirstTime = false;
buttonFlags = [ 'primary' ];
buttonFlags = [];
if ( !!config.hasUnseen ) {
buttonFlags.push( 'unseen' );
}
this.badgeButton = new mw.echo.ui.BadgeLinkWidget( {
label: this.numItems,
label: this.badgeLabel,
numItems: this.numItems,
flags: buttonFlags,
badgeIcon: config.badgeIcon,
// The following messages can be used here:
// tooltip-pt-notifications-alert
// tooltip-pt-notifications-notice
@ -130,6 +125,7 @@
$footer: $footer,
width: config.popupWidth || 500,
autoClose: true,
containerPadding: 20,
// Also ignore clicks from the nested action menu items, that
// actually exist in the overlay
$autoCloseIgnore: this.$element.add( this.$menuOverlay ),
@ -144,11 +140,10 @@
classes: [ 'mw-echo-ui-notificationBadgeButtonPopupWidget-popup' ]
} );
// HACK: Add an icon to the popup head label
this.popupHeadIcon = new OO.ui.IconWidget();
this.popupHeadIcon = new OO.ui.IconWidget( { icon: config.badgeIcon } );
this.popup.$head.prepend( this.popupHeadIcon.$element );
this.setPendingElement( this.popup.$head );
this.updateIcon( !!config.hasUnseen );
// Mark all as read button
this.markAllReadButton = new OO.ui.ButtonWidget( {
@ -230,20 +225,6 @@
this.popup.toggle();
};
/**
* Update the badge icon with the read/unread versions if they exist.
*
* @param {boolean} hasUnseen Widget has unseen notifications
*/
mw.echo.ui.NotificationBadgeWidget.prototype.updateIcon = function ( hasUnseen ) {
var icon = typeof this.badgeIcon === 'string' ?
this.badgeIcon :
this.badgeIcon[ hasUnseen ? 'unseen' : 'seen' ];
this.badgeButton.setIcon( icon );
this.popupHeadIcon.setIcon( icon );
};
// Client-side version of NotificationController::getCappedNotificationCount.
/**
* Gets the count to use for display
@ -276,7 +257,6 @@
hasUnseen = hasUnseen === undefined ? this.manager.hasUnseenInSource( 'local' ) : !!hasUnseen;
this.badgeButton.setFlags( { unseen: !!hasUnseen } );
this.updateIcon( !!hasUnseen );
};
/**
@ -289,8 +269,9 @@
cappedUnreadCount = this.getCappedNotificationCount( unreadCount );
cappedUnreadCount = mw.language.convertNumber( cappedUnreadCount );
badgeLabel = mw.message( 'echo-badge-count', cappedUnreadCount ).text();
this.badgeButton.setLabel( badgeLabel );
this.badgeButton.setLabel( badgeLabel );
this.badgeButton.setCount( unreadCount, badgeLabel );
// Update seen state only if the counter is 0
// so we don't run into inconsistencies and have an unseen state
// for the badge with 0 unread notifications

View file

@ -15,8 +15,8 @@ Feature: Testing notification types
Scenario: Someone writes on my talk page
Given another user writes on my talk page
When I refresh the page
Then the message badge is showing unseen notifications
And the message badge value is "1"
When I click the message badge
And I see the message popup
Then there are "1" unread notifications in the message popup
Then the alert badge is showing unseen notifications
And the alert badge value is "1"
When I click the alert badge
And I see the alert popup
Then there are "1" unread notifications in the alert popup

View file

@ -2,9 +2,9 @@
# Work in both nojs and js version
Given(/^I click the alert badge$/) do
on(ArticlePage).alerts.badge
on(ArticlePage).alerts.badge_element.when_present.click
end
Given(/^I click the message badge$/) do
on(ArticlePage).messages.badge
Given(/^I click the notice badge$/) do
on(ArticlePage).notices.badge_element.when_present.click
end

View file

@ -36,10 +36,10 @@ Given(/^the alert badge is showing unseen notifications$/) do
end
end
Given(/^the message badge is showing unseen notifications$/) do
Given(/^the notice badge is showing unseen notifications$/) do
on(ArticlePage) do |page|
page.refresh_until do
page.messages.badge_unseen_element.visible?
page.notices.badge_unseen_element.visible?
end
end
end
@ -52,17 +52,24 @@ Given(/^the alert badge value is "(.+)"$/) do |num|
end
end
Given(/^the message badge value is "(.+)"$/) do |num|
Given(/^the notice badge value is "(.+)"$/) do |num|
on(ArticlePage) do |page|
page.refresh_until do
page.messages.badge_element.text == num
page.notices.badge_element.text == num
end
end
end
Given(/^there are "(.+)" unread notifications in the message popup$/) do |num|
Given(/^there are "(.+)" unread notifications in the notice popup$/) do |num|
on(ArticlePage) do |page|
page.messages.when_loaded
expect(page.messages.num_unread_notifications).to eq(num.to_i)
page.notices.when_loaded
expect(page.notices.num_unread_notifications).to eq(num.to_i)
end
end
Given(/^there are "(.+)" unread notifications in the alert popup$/) do |num|
on(ArticlePage) do |page|
page.alerts.when_loaded
expect(page.alerts.num_unread_notifications).to eq(num.to_i)
end
end

View file

@ -1,12 +1,12 @@
class Notifications
include PageObject
link(:badge)
link(:badge, css: '.mw-echo-notifications-badge')
link(:badge_unseen, css: '.mw-echo-unseen-notifications')
link(:mark_all_as_read, css: '.mw-echo-ui-notificationsWidget-markAllReadButton > a')
link(:mark_all_as_read, css: '.mw-echo-ui-notificationsListWidget-markAllReadButton > a')
div(:popup, css: '.mw-echo-ui-notificationBadgeButtonPopupWidget-popup')
span(:title, css: '.oo-ui-popupWidget-head > .oo-ui-labelElement-label')
div(:notifications_container, css: '.mw-echo-ui-notificationsWidget')
div(:notifications_container, css: '.mw-echo-ui-notificationsListWidget')
def when_loaded
title_element.when_present

View file

@ -2,5 +2,5 @@ class ArticlePage
include PageObject
page_section(:alerts, Notifications, css: '#pt-notifications-alert')
page_section(:messages, Notifications, css: '#pt-notifications-message')
page_section(:notices, Notifications, css: '#pt-notifications-notice')
end