2019-04-03 22:57:13 +00:00
|
|
|
/* eslint-disable no-jquery/no-global-selector */
|
2019-10-01 20:15:25 +00:00
|
|
|
mw.echo = mw.echo || {};
|
|
|
|
mw.echo.config = mw.echo.config || {};
|
|
|
|
// Set default max prioritized action links per item
|
|
|
|
mw.echo.config.maxPrioritizedActions = 2;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialise desktop Echo experience
|
|
|
|
*/
|
|
|
|
function initDesktop() {
|
2015-08-13 00:54:16 +00:00
|
|
|
'use strict';
|
2020-10-19 19:24:11 +00:00
|
|
|
var uri;
|
2015-08-13 00:54:16 +00:00
|
|
|
|
2016-05-03 01:56:13 +00:00
|
|
|
// Remove ?markasread=XYZ from the URL
|
2020-10-19 19:24:11 +00:00
|
|
|
try {
|
|
|
|
uri = new mw.Uri();
|
|
|
|
if ( uri.query.markasread !== undefined ) {
|
|
|
|
delete uri.query.markasread;
|
|
|
|
delete uri.query.markasreadwiki;
|
|
|
|
window.history.replaceState( null, document.title, uri );
|
|
|
|
}
|
|
|
|
} catch ( e ) {
|
|
|
|
// Catch problems when the URI is malformed (T261799)
|
|
|
|
// e.g. #/media/Fitxer:Campbells_Soup_Cans_MOMA_reduced_80%.jpg
|
2016-05-03 01:56:13 +00:00
|
|
|
}
|
|
|
|
|
2015-08-13 00:54:16 +00:00
|
|
|
// Activate ooui
|
2017-01-06 13:06:39 +00:00
|
|
|
$( function () {
|
2019-06-10 15:29:05 +00:00
|
|
|
var selectedWidget,
|
|
|
|
echoApi,
|
|
|
|
messageController,
|
|
|
|
alertController,
|
|
|
|
messageModelManager,
|
|
|
|
alertModelManager,
|
|
|
|
unreadMessageCounter,
|
|
|
|
unreadAlertCounter,
|
2019-07-03 23:04:14 +00:00
|
|
|
maxNotificationCount = require( './config.json' ).EchoMaxNotificationCount,
|
|
|
|
pollingRate = require( './config.json' ).EchoPollForUpdates,
|
2019-06-25 02:22:58 +00:00
|
|
|
documentTitle = document.title,
|
2015-09-15 06:13:51 +00:00
|
|
|
$existingAlertLink = $( '#pt-notifications-alert a' ),
|
2016-07-21 18:19:17 +00:00
|
|
|
$existingMessageLink = $( '#pt-notifications-notice a' ),
|
2016-07-20 00:24:17 +00:00
|
|
|
numAlerts = $existingAlertLink.attr( 'data-counter-num' ),
|
|
|
|
numMessages = $existingMessageLink.attr( 'data-counter-num' ),
|
|
|
|
badgeLabelAlerts = $existingAlertLink.attr( 'data-counter-text' ),
|
|
|
|
badgeLabelMessages = $existingMessageLink.attr( 'data-counter-text' ),
|
2019-11-15 17:26:44 +00:00
|
|
|
// eslint-disable-next-line no-jquery/no-class-state
|
2015-09-03 21:24:03 +00:00
|
|
|
hasUnseenAlerts = $existingAlertLink.hasClass( 'mw-echo-unseen-notifications' ),
|
2019-11-15 17:26:44 +00:00
|
|
|
// eslint-disable-next-line no-jquery/no-class-state
|
2015-09-03 21:24:03 +00:00
|
|
|
hasUnseenMessages = $existingMessageLink.hasClass( 'mw-echo-unseen-notifications' ),
|
2019-07-19 14:08:06 +00:00
|
|
|
// latestMessageNotifTime is the time of most recent notification that came when we called showNotificationSnippet last
|
|
|
|
// the function showNotificationSnippet returns the time of the latest notification and latestMessageNotifTime is updated
|
|
|
|
latestMessageNotifTime = new Date(),
|
|
|
|
latestAlertNotifTime = new Date(),
|
2019-06-25 02:22:58 +00:00
|
|
|
alertCount = parseInt( numAlerts ),
|
|
|
|
messageCount = parseInt( numMessages ),
|
2019-06-10 15:29:05 +00:00
|
|
|
loadingPromise = null,
|
2015-08-13 00:54:16 +00:00
|
|
|
// Store links
|
|
|
|
links = {
|
2019-06-10 15:29:05 +00:00
|
|
|
notifications: $existingAlertLink.attr( 'href' ) || mw.util.getUrl( 'Special:Notifications' ),
|
2017-11-06 19:06:35 +00:00
|
|
|
preferences: ( $( '#pt-preferences a' ).attr( 'href' ) || mw.util.getUrl( 'Special:Preferences' ) ) +
|
|
|
|
'#mw-prefsection-echo'
|
2015-08-13 00:54:16 +00:00
|
|
|
};
|
|
|
|
|
2019-06-25 02:22:58 +00:00
|
|
|
function updateDocumentTitleWithNotificationCount( totalAlertCount, totalMessageCount ) {
|
|
|
|
var totalCount = totalAlertCount + totalMessageCount,
|
|
|
|
convertedTotalCount,
|
|
|
|
newTitle = documentTitle;
|
|
|
|
|
|
|
|
if ( totalCount > 0 ) {
|
|
|
|
convertedTotalCount = totalCount <= maxNotificationCount ? totalCount : maxNotificationCount + 1;
|
|
|
|
convertedTotalCount = mw.msg( 'echo-badge-count', mw.language.convertNumber( convertedTotalCount ) );
|
|
|
|
newTitle = mw.msg( 'parentheses', convertedTotalCount ) + ' ' + documentTitle;
|
|
|
|
}
|
|
|
|
document.title = newTitle;
|
|
|
|
}
|
|
|
|
|
2019-07-19 14:08:06 +00:00
|
|
|
/**
|
|
|
|
* Show notification snippet via mw.notify of notifications which came after highestNotifTime.
|
|
|
|
*
|
|
|
|
* @param {mw.echo.dm.ModelManager} modelManager
|
|
|
|
* @param {Date} highestNotifTime Timestamp of latest notification the last time function was called
|
|
|
|
* @return {Date} Timestamp of latest notification
|
|
|
|
*/
|
|
|
|
function showNotificationSnippet( modelManager, highestNotifTime ) {
|
|
|
|
var timestampAsDate,
|
|
|
|
highestTime = new Date();
|
|
|
|
highestTime = highestNotifTime;
|
|
|
|
modelManager.getLocalNotifications().forEach( function ( notificationItem ) {
|
|
|
|
timestampAsDate = new Date( notificationItem.timestamp );
|
|
|
|
if ( timestampAsDate > highestNotifTime ) {
|
|
|
|
if ( timestampAsDate > highestTime ) {
|
|
|
|
highestTime = timestampAsDate;
|
|
|
|
}
|
|
|
|
if ( !notificationItem.seen ) {
|
2019-08-20 09:26:36 +00:00
|
|
|
mw.notify( $.parseHTML( notificationItem.content.header ), { title: mw.msg( 'echo-displaysnippet-title' ) } );
|
2019-07-19 14:08:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
return highestTime;
|
|
|
|
}
|
|
|
|
|
2019-08-11 09:54:08 +00:00
|
|
|
/**
|
|
|
|
* Change the seen state of badges if there are any unseen notifications.
|
|
|
|
*
|
|
|
|
* @param {mw.echo.dm.ModelManager} modelManager
|
|
|
|
* @param {mw.echo.ui.NotificationBadgeWidget} badgeWidget
|
|
|
|
*/
|
|
|
|
function updateBadgeState( modelManager, badgeWidget ) {
|
|
|
|
modelManager.getLocalNotifications().forEach( function ( notificationItem ) {
|
|
|
|
if ( !notificationItem.isSeen() ) {
|
|
|
|
badgeWidget.updateBadgeSeenState( true );
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
}
|
2019-06-25 02:22:58 +00:00
|
|
|
|
2019-08-11 09:54:08 +00:00
|
|
|
function isLivePollingFeatureEnabledOnWiki() {
|
|
|
|
return pollingRate !== 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* User has opted in to preference to show notification snippets and update document title with unread count.
|
|
|
|
*
|
|
|
|
* Only useful when isLivePollingFeatureEnabledOnWiki() returns true.
|
|
|
|
*
|
|
|
|
* @return {boolean} User preference
|
|
|
|
*/
|
|
|
|
function userHasOptedInToLiveNotifications() {
|
|
|
|
return mw.user.options.get( 'echo-show-poll-updates' ) === '1';
|
|
|
|
}
|
|
|
|
|
2019-08-16 20:25:51 +00:00
|
|
|
// Change document title on initialization only when polling rate feature flag is non-zero.
|
|
|
|
if ( isLivePollingFeatureEnabledOnWiki() && userHasOptedInToLiveNotifications() ) {
|
|
|
|
updateDocumentTitleWithNotificationCount( alertCount, messageCount );
|
|
|
|
}
|
|
|
|
|
2019-06-10 15:29:05 +00:00
|
|
|
function loadEcho() {
|
|
|
|
if ( loadingPromise !== null ) {
|
|
|
|
return loadingPromise;
|
2015-09-24 01:13:37 +00:00
|
|
|
}
|
2019-06-10 15:29:05 +00:00
|
|
|
// This part executes only once, either when header icons are clicked or after completion of 60secs whichever occur first.
|
2016-08-04 14:42:11 +00:00
|
|
|
echoApi = new mw.echo.api.EchoApi();
|
2015-09-15 06:13:51 +00:00
|
|
|
|
2019-06-10 15:29:05 +00:00
|
|
|
loadingPromise = mw.loader.using( 'ext.echo.ui.desktop' ).then( function () {
|
2016-02-27 01:19:06 +00:00
|
|
|
|
2015-11-21 01:54:12 +00:00
|
|
|
// Overlay
|
2019-12-16 10:50:21 +00:00
|
|
|
mw.echo.ui.$overlay.appendTo( document.body );
|
2016-04-10 13:31:02 +00:00
|
|
|
|
2016-03-15 21:13:19 +00:00
|
|
|
unreadAlertCounter = new mw.echo.dm.UnreadNotificationCounter( echoApi, 'alert', maxNotificationCount );
|
2016-04-10 13:31:02 +00:00
|
|
|
alertModelManager = new mw.echo.dm.ModelManager( unreadAlertCounter, { type: 'alert' } );
|
2016-11-18 21:16:43 +00:00
|
|
|
alertController = new mw.echo.Controller( echoApi, alertModelManager );
|
2016-04-10 13:31:02 +00:00
|
|
|
|
|
|
|
mw.echo.ui.alertWidget = new mw.echo.ui.NotificationBadgeWidget(
|
|
|
|
alertController,
|
|
|
|
alertModelManager,
|
2018-05-22 14:42:08 +00:00
|
|
|
links,
|
2016-04-10 13:31:02 +00:00
|
|
|
{
|
2016-07-20 00:24:17 +00:00
|
|
|
numItems: Number( numAlerts ),
|
2017-09-02 00:08:21 +00:00
|
|
|
convertedNumber: badgeLabelAlerts,
|
2016-04-10 13:31:02 +00:00
|
|
|
hasUnseen: hasUnseenAlerts,
|
2016-07-20 00:24:17 +00:00
|
|
|
badgeIcon: 'bell',
|
2016-04-10 13:31:02 +00:00
|
|
|
$overlay: mw.echo.ui.$overlay,
|
|
|
|
href: $existingAlertLink.attr( 'href' )
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2019-06-10 15:29:05 +00:00
|
|
|
// Replace the link button with the ooui button
|
|
|
|
$existingAlertLink.parent().replaceWith( mw.echo.ui.alertWidget.$element );
|
|
|
|
|
2016-07-21 23:18:05 +00:00
|
|
|
alertModelManager.on( 'allTalkRead', function () {
|
|
|
|
// If there was a talk page notification, get rid of it
|
2021-05-27 00:58:05 +00:00
|
|
|
$( '#pt-talk-alert' ).remove();
|
2016-07-21 23:18:05 +00:00
|
|
|
} );
|
|
|
|
|
2019-06-25 02:22:58 +00:00
|
|
|
// listen to event countChange and change title only if polling rate is non-zero
|
2019-08-11 09:54:08 +00:00
|
|
|
if ( isLivePollingFeatureEnabledOnWiki() ) {
|
2019-06-25 02:22:58 +00:00
|
|
|
alertModelManager.getUnreadCounter().on( 'countChange', function ( count ) {
|
2019-08-11 09:54:08 +00:00
|
|
|
alertController.fetchLocalNotifications().then( function () {
|
|
|
|
updateBadgeState( alertModelManager, mw.echo.ui.alertWidget );
|
|
|
|
if ( userHasOptedInToLiveNotifications() ) {
|
2019-07-19 14:08:06 +00:00
|
|
|
latestAlertNotifTime = showNotificationSnippet( alertModelManager, latestAlertNotifTime );
|
2019-08-11 09:54:08 +00:00
|
|
|
alertCount = count;
|
|
|
|
updateDocumentTitleWithNotificationCount( count, messageCount );
|
|
|
|
}
|
|
|
|
} );
|
2019-06-25 02:22:58 +00:00
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
2019-06-10 15:29:05 +00:00
|
|
|
// Load message button and popup if messages exist
|
|
|
|
if ( $existingMessageLink.length ) {
|
|
|
|
unreadMessageCounter = new mw.echo.dm.UnreadNotificationCounter( echoApi, 'message', maxNotificationCount );
|
|
|
|
messageModelManager = new mw.echo.dm.ModelManager( unreadMessageCounter, { type: 'message' } );
|
|
|
|
messageController = new mw.echo.Controller( echoApi, messageModelManager );
|
2015-09-15 06:13:51 +00:00
|
|
|
|
2019-06-10 15:29:05 +00:00
|
|
|
mw.echo.ui.messageWidget = new mw.echo.ui.NotificationBadgeWidget(
|
|
|
|
messageController,
|
|
|
|
messageModelManager,
|
|
|
|
links,
|
|
|
|
{
|
|
|
|
$overlay: mw.echo.ui.$overlay,
|
|
|
|
numItems: Number( numMessages ),
|
|
|
|
hasUnseen: hasUnseenMessages,
|
|
|
|
badgeIcon: 'tray',
|
|
|
|
convertedNumber: badgeLabelMessages,
|
|
|
|
href: $existingMessageLink.attr( 'href' )
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Replace the link button with the ooui button
|
|
|
|
$existingMessageLink.parent().replaceWith( mw.echo.ui.messageWidget.$element );
|
2019-06-25 02:22:58 +00:00
|
|
|
|
|
|
|
// listen to event countChange and change title only if polling rate is non-zero
|
2019-08-11 09:54:08 +00:00
|
|
|
if ( isLivePollingFeatureEnabledOnWiki() ) {
|
2019-06-25 02:22:58 +00:00
|
|
|
messageModelManager.getUnreadCounter().on( 'countChange', function ( count ) {
|
2019-08-11 09:54:08 +00:00
|
|
|
messageController.fetchLocalNotifications().then( function () {
|
|
|
|
updateBadgeState( messageModelManager, mw.echo.ui.messageWidget );
|
|
|
|
if ( userHasOptedInToLiveNotifications() ) {
|
2019-07-19 14:08:06 +00:00
|
|
|
latestMessageNotifTime = showNotificationSnippet( messageModelManager, latestMessageNotifTime );
|
2019-08-11 09:54:08 +00:00
|
|
|
messageCount = count;
|
|
|
|
updateDocumentTitleWithNotificationCount( alertCount, count );
|
|
|
|
}
|
|
|
|
} );
|
2019-06-25 02:22:58 +00:00
|
|
|
} );
|
|
|
|
}
|
2019-06-10 15:29:05 +00:00
|
|
|
}
|
2015-08-13 00:54:16 +00:00
|
|
|
} );
|
2019-06-10 15:29:05 +00:00
|
|
|
return loadingPromise;
|
2015-09-15 22:58:13 +00:00
|
|
|
|
2019-06-10 15:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Respond to click on the notification button and load the UI on demand
|
|
|
|
$( '.mw-echo-notification-badge-nojs' ).on( 'click', function ( e ) {
|
|
|
|
var timeOfClick = mw.now(),
|
2019-06-20 20:54:36 +00:00
|
|
|
$badge = $( this ),
|
|
|
|
clickedSection = $badge.parent().prop( 'id' ) === 'pt-notifications-alert' ? 'alert' : 'message';
|
|
|
|
if ( e.which !== 1 || $badge.data( 'clicked' ) ) {
|
2022-02-17 17:46:30 +00:00
|
|
|
// Do not return false (as that calls stopPropagation)
|
|
|
|
e.preventDefault();
|
|
|
|
return;
|
2015-09-15 22:58:13 +00:00
|
|
|
}
|
|
|
|
|
2019-06-20 20:54:36 +00:00
|
|
|
$badge.data( 'clicked', true );
|
2019-06-10 15:29:05 +00:00
|
|
|
|
2019-06-20 20:54:36 +00:00
|
|
|
// Dim the badge while we load
|
|
|
|
$badge.addClass( 'mw-echo-notifications-badge-dimmed' );
|
2019-06-10 15:29:05 +00:00
|
|
|
|
|
|
|
// Fire the notification API requests
|
|
|
|
echoApi = new mw.echo.api.EchoApi();
|
|
|
|
echoApi.fetchNotifications( clickedSection )
|
|
|
|
.then( function ( data ) {
|
|
|
|
mw.track( 'timing.MediaWiki.echo.overlay.api', mw.now() - timeOfClick );
|
|
|
|
return data;
|
|
|
|
} );
|
|
|
|
|
|
|
|
loadEcho().then( function () {
|
|
|
|
// Now that the module loaded, show the popup
|
|
|
|
selectedWidget = clickedSection === 'alert' ? mw.echo.ui.alertWidget : mw.echo.ui.messageWidget;
|
|
|
|
selectedWidget.once( 'finishLoading', function () {
|
|
|
|
// Log timing after notifications are shown
|
|
|
|
mw.track( 'timing.MediaWiki.echo.overlay', mw.now() - timeOfClick );
|
|
|
|
} );
|
|
|
|
selectedWidget.popup.toggle( true );
|
|
|
|
mw.track( 'timing.MediaWiki.echo.overlay.ooui', mw.now() - timeOfClick );
|
|
|
|
|
|
|
|
if ( hasUnseenAlerts || hasUnseenMessages ) {
|
|
|
|
// Clicked on the flyout due to having unread notifications
|
2019-06-12 22:38:01 +00:00
|
|
|
// This is part of tracking how likely users are to click a badge with unseen notifications.
|
2021-05-14 20:44:10 +00:00
|
|
|
// The other part is the 'echo.unseen' counter, see EchoHooks::onSkinTemplateNavigationUniversal().
|
2019-06-10 15:29:05 +00:00
|
|
|
mw.track( 'counter.MediaWiki.echo.unseen.click' );
|
|
|
|
}
|
2019-06-20 20:54:36 +00:00
|
|
|
}, function () {
|
|
|
|
// Un-dim badge if loading failed
|
|
|
|
$badge.removeClass( 'mw-echo-notifications-badge-dimmed' );
|
2019-06-10 15:29:05 +00:00
|
|
|
} );
|
2022-02-17 17:46:30 +00:00
|
|
|
// Prevent default. Do not return false (as that calls stopPropagation)
|
|
|
|
e.preventDefault();
|
2015-08-13 00:54:16 +00:00
|
|
|
} );
|
2019-06-10 15:29:05 +00:00
|
|
|
|
|
|
|
function pollForNotificationCountUpdates() {
|
|
|
|
alertController.refreshUnreadCount();
|
|
|
|
messageController.refreshUnreadCount();
|
2019-07-13 11:41:00 +00:00
|
|
|
// Make notification update after n*pollingRate(time in secs) where n depends on document.hidden
|
|
|
|
setTimeout( pollForNotificationCountUpdates, ( document.hidden ? 5 : 1 ) * pollingRate * 1000 );
|
2019-06-10 15:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function pollStart() {
|
2019-08-16 20:25:51 +00:00
|
|
|
if ( mw.config.get( 'skin' ) !== 'minerva' && isLivePollingFeatureEnabledOnWiki() ) {
|
2019-06-10 15:29:05 +00:00
|
|
|
// load widgets if not loaded already then start polling
|
|
|
|
loadEcho().then( pollForNotificationCountUpdates );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setTimeout( pollStart, 60 * 1000 );
|
|
|
|
|
2015-08-13 00:54:16 +00:00
|
|
|
} );
|
|
|
|
|
2019-10-01 20:15:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialise a mobile experience instead
|
|
|
|
*/
|
|
|
|
function initMobile() {
|
|
|
|
if ( !mw.user.isAnon() ) {
|
2019-10-08 21:41:38 +00:00
|
|
|
mw.loader.using( [ 'ext.echo.mobile', 'mobile.startup' ] ).then( function ( require ) {
|
2022-03-01 23:04:53 +00:00
|
|
|
require( 'ext.echo.mobile' ).init();
|
2019-10-01 20:15:25 +00:00
|
|
|
} );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$( function () {
|
|
|
|
if ( mw.config.get( 'wgMFMode' ) ) {
|
|
|
|
initMobile();
|
|
|
|
} else {
|
|
|
|
initDesktop();
|
|
|
|
}
|
|
|
|
} );
|