jenkins-bot has submitted this change and it was merged.
Change subject: Allow mark-as-unread in notifications
......................................................................
Allow mark-as-unread in notifications
Add a 'mark as unread' to all unread notifications and allow them
to be marked unread. These notifications will no longer be automatically
marked as read when the pages they refer to are visited.
Bug: T73564
Change-Id: I677d3c0399e46fd7c35531df1cc0e61db2d4eb1b
---
M Resources.php
M i18n/en.json
M i18n/qqq.json
M includes/NotifUser.php
M includes/api/ApiEchoMarkRead.php
M includes/gateway/UserNotificationGateway.php
M modules/api/mw.echo.api.APIHandler.js
M modules/api/mw.echo.api.EchoApi.js
M modules/api/mw.echo.api.LocalAPIHandler.js
M modules/ooui/mw.echo.ui.NotificationItemWidget.js
M modules/viewmodel/mw.echo.dm.NotificationGroupItem.js
M modules/viewmodel/mw.echo.dm.NotificationsModel.js
12 files changed, 135 insertions(+), 46 deletions(-)
Approvals:
Catrope: Looks good to me, approved
jenkins-bot: Verified
diff --git a/Resources.php b/Resources.php
index 10889ca..adfe2a5 100644
--- a/Resources.php
+++ b/Resources.php
@@ -85,6 +85,7 @@
'oojs-ui.styles.icons-user',
'oojs-ui.styles.icons-alerts',
'oojs-ui.styles.icons-content',
+ 'oojs-ui.styles.icons-interactions',
),
'messages' => array(
'echo-notification-count',
@@ -107,6 +108,7 @@
"notification-timestamp-ago-months",
"notification-timestamp-ago-years",
'echo-notification-markasread',
+ 'echo-notification-markasunread',
'echo-notification-alert-text-only',
'echo-notification-message-text-only',
'echo-email-batch-bullet',
diff --git a/i18n/en.json b/i18n/en.json
index 4e5269e..06bc6ca 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -59,6 +59,7 @@
"echo-notification-loginrequired": "You must login to see your
notifications.",
"echo-notification-popup-loginrequired": "Please log in to view your
notifications.",
"echo-notification-markasread": "Mark as read",
+ "echo-notification-markasunread": "Mark as unread",
"notification-link-text-expand-all": "View all",
"notification-link-text-expand-alert-count": "View {{PLURAL:$1|$1
alert|$1 alerts}}",
"notification-link-text-expand-message-count": "View {{PLURAL:$1|$1
message|$1 messages}}",
@@ -171,10 +172,12 @@
"echo-foreign-wiki-lang": "$1 - $2",
"apihelp-echomarkread-description": "Mark notifications as read for the
current user.",
"apihelp-echomarkread-param-list": "A list of notification IDs to mark
as read.",
+ "apihelp-echomarkread-param-unreadlist": "A list of notification IDs to
mark as unread.",
"apihelp-echomarkread-param-all": "If set, marks all of a user's
notifications as read.",
"apihelp-echomarkread-param-sections": "A list of sections to mark as
read.",
"apihelp-echomarkread-example-1": "Mark notification 8 as read",
"apihelp-echomarkread-example-2": "Mark all notifications as read",
+ "apihelp-echomarkread-example-3": "Mark notification 1 as unread",
"apihelp-echomarkseen-description": "Mark notifications as seen for the
current user.",
"apihelp-echomarkseen-example-1": "Mark notifications of all types as
seen",
"apihelp-echomarkseen-param-type": "Type of notifications to mark as
seen: 'alert', 'message' or 'all'.",
diff --git a/i18n/qqq.json b/i18n/qqq.json
index b4bd9b7..c19d268 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -80,6 +80,7 @@
"echo-notification-loginrequired": "Message that displays when an
anonymous user attempts to view notifications and gets redirect to the login
page.",
"echo-notification-popup-loginrequired": "Message that displays when an
anonymous user attempts to view notifications in their popup after a session
expired.",
"echo-notification-markasread": "Label for the button to mark the
notification as read.",
+ "echo-notification-markasunread": "Label for the button to mark the
notification as unread.",
"notification-link-text-expand-all": "Label for the button that expands
a bundled notification.\n{{Identical|View all}}",
"notification-link-text-expand-alert-count": "Label for the button that
expands a bundled alert notification.\n\nParameters:\n* $1 - The count of
messages that the bundle holds.\n{{Identical|View message}}",
"notification-link-text-expand-message-count": "Label for the button
that expands a bundled message notification.\n\nParameters:\n* $1 - The count
of messages that the bundle holds.\n{{Identical|View message}}",
@@ -192,10 +193,12 @@
"echo-foreign-wiki-lang": "Title of the wiki for which you're seeing
foreign notifications:\n\nParameters:\n* $1 - the name of the site (e.g.
'Wikipedia', 'Wikiversity', ...)\n* $2 - the language of the website (e.g.
'English', 'Deutsch', ...)",
"apihelp-echomarkread-description":
"{{doc-apihelp-description|echomarkread}}",
"apihelp-echomarkread-param-list":
"{{doc-apihelp-param|echomarkread|list}}",
+ "apihelp-echomarkread-param-unreadlist":
"{{doc-apihelp-param|echomarkread|unreadlist}}",
"apihelp-echomarkread-param-all":
"{{doc-apihelp-param|echomarkread|all}}",
"apihelp-echomarkread-param-sections":
"{{doc-apihelp-param|echomarkread|sections}}",
"apihelp-echomarkread-example-1":
"{{doc-apihelp-example|echomarkread}}",
"apihelp-echomarkread-example-2":
"{{doc-apihelp-example|echomarkread}}",
+ "apihelp-echomarkread-example-3":
"{{doc-apihelp-example|echomarkread}}",
"apihelp-echomarkseen-description":
"{{doc-apihelp-description|echomarkseen}}",
"apihelp-echomarkseen-example-1":
"{{doc-apihelp-example|echomarkseen}}",
"apihelp-echomarkseen-param-type":
"{{doc-apihelp-param|query+notifications|type}}",
diff --git a/includes/NotifUser.php b/includes/NotifUser.php
index 0d6211c..d972310 100644
--- a/includes/NotifUser.php
+++ b/includes/NotifUser.php
@@ -337,6 +337,35 @@
}
/**
+ * Mark one or more notifications unread for a user.
+ * @param $eventIds Array of event IDs to mark unread
+ * @return boolean
+ */
+ public function markUnRead( $eventIds ) {
+ $eventIds = array_filter( (array)$eventIds, 'is_numeric' );
+ if ( !$eventIds || wfReadOnly() ) {
+ return false;
+ }
+
+ $res = $this->userNotifGateway->markUnRead( $eventIds );
+ if ( $res ) {
+ // Update notification count in cache
+ $this->resetNotificationCount( DB_MASTER );
+
+ // After this 'mark unread', is there any unread
edit-user-talk?
+ // If so, we should add the edit-user-talk flag
+ if ( !$this->mUser->getNewtalk() ) {
+ $unreadEditUserTalk =
$this->notifMapper->fetchUnreadByUser( $this->mUser, 1, null, array(
'edit-user-talk' ), DB_MASTER );
+ if ( count( $unreadEditUserTalk ) > 0 ) {
+ $this->mUser->setNewtalk( true );
+ }
+ }
+ }
+
+ return $res;
+ }
+
+ /**
* Attempt to mark all or sections of notifications as read, this only
* updates up to $wgEchoMaxUpdateCount records per request, see more
* detail about this in Echo.php, the other reason is that mediawiki
diff --git a/includes/api/ApiEchoMarkRead.php b/includes/api/ApiEchoMarkRead.php
index 2920c5f..104bebe 100644
--- a/includes/api/ApiEchoMarkRead.php
+++ b/includes/api/ApiEchoMarkRead.php
@@ -29,6 +29,12 @@
}
}
+ // Mark as unread
+ if ( count( $params['unreadlist'] ) > 0 ) {
+ // Make sure there is a limit to the update
+ $notifUser->markUnRead( array_slice(
$params['unreadlist'], 0, ApiBase::LIMIT_SML2 ) );
+ }
+
$result = array(
'result' => 'success'
);
@@ -50,6 +56,9 @@
public function getAllowedParams() {
return array(
'list' => array(
+ ApiBase::PARAM_ISMULTI => true,
+ ),
+ 'unreadlist' => array(
ApiBase::PARAM_ISMULTI => true,
),
'all' => array(
@@ -91,6 +100,8 @@
=> 'apihelp-echomarkread-example-1',
'action=echomarkread&all=true'
=> 'apihelp-echomarkread-example-2',
+ 'action=echomarkread&unreadlist=1'
+ => 'apihelp-echomarkread-example-3',
);
}
diff --git a/includes/gateway/UserNotificationGateway.php
b/includes/gateway/UserNotificationGateway.php
index 264c6c4..f06b87d 100644
--- a/includes/gateway/UserNotificationGateway.php
+++ b/includes/gateway/UserNotificationGateway.php
@@ -57,6 +57,30 @@
}
/**
+ * Mark notifications as unread
+ * @param $eventIDs array
+ * @return boolean
+ */
+ public function markUnRead( array $eventIDs ) {
+ if ( !$eventIDs ) {
+ return;
+ }
+
+ $dbw = $this->dbFactory->getEchoDb( DB_MASTER );
+
+ return $dbw->update(
+ self::$notificationTable,
+ array( 'notification_read_timestamp' => null ),
+ array(
+ 'notification_user' => $this->user->getId(),
+ 'notification_event' => $eventIDs,
+ 'notification_read_timestamp IS NOT NULL'
+ ),
+ __METHOD__
+ );
+ }
+
+ /**
* Mark all notification as read, use MWEchoNotifUer::markAllRead()
instead
* @deprecated may need this when running in a job or revive this when
we
* have updateJoin()
diff --git a/modules/api/mw.echo.api.APIHandler.js
b/modules/api/mw.echo.api.APIHandler.js
index b16c1ff..5e7dd34 100644
--- a/modules/api/mw.echo.api.APIHandler.js
+++ b/modules/api/mw.echo.api.APIHandler.js
@@ -105,6 +105,8 @@
*
* @abstract
* @param {string[]} itemIdArray An array of notification item IDs
+ * @param {boolean} [isRead] Item's new read state; true for marking
the item
+ * as read, false for marking the item as unread
* @return {jQuery.Promise} A promise that resolves when all given
notifications
* are marked as read.
*/
@@ -114,11 +116,13 @@
* Update the read status of a notification item in the API
*
* @param {string} itemId Item id
+ * @param {boolean} [isRead] Item's new read state; true for marking
the item
+ * as read, false for marking the item as unread
* @return {jQuery.Promise} A promise that resolves when the
notifications
* are marked as read.
*/
- mw.echo.api.APIHandler.prototype.markItemRead = function ( itemId ) {
- this.markItemsRead( [ itemId ] );
+ mw.echo.api.APIHandler.prototype.markItemRead = function ( itemId,
isRead ) {
+ this.markItemsRead( [ itemId ], isRead );
};
/**
diff --git a/modules/api/mw.echo.api.EchoApi.js
b/modules/api/mw.echo.api.EchoApi.js
index 3ec0735..9366b16 100644
--- a/modules/api/mw.echo.api.EchoApi.js
+++ b/modules/api/mw.echo.api.EchoApi.js
@@ -78,13 +78,14 @@
*
* @param {string[]} itemIds An array of item IDs to mark as read
* @param {string} source The source that these items belong to
- * @param {string} type Notification type
+ * @param {boolean} [isRead] The read state of the item; true for
marking the
+ * item as read, false for marking the item as unread
* @return {jQuery.Promise} A promise that is resolved when the
operation
* is complete, with the number of unread notifications still remaining
* for that type in the given source
*/
- mw.echo.api.EchoApi.prototype.markItemsRead = function ( itemIds,
source, type ) {
- return this.network.getApiHandler( source ).markItemsRead(
itemIds, type );
+ mw.echo.api.EchoApi.prototype.markItemsRead = function ( itemIds,
source, isRead ) {
+ return this.network.getApiHandler( source ).markItemsRead(
itemIds, isRead );
};
/**
diff --git a/modules/api/mw.echo.api.LocalAPIHandler.js
b/modules/api/mw.echo.api.LocalAPIHandler.js
index 5fade41..7da8778 100644
--- a/modules/api/mw.echo.api.LocalAPIHandler.js
+++ b/modules/api/mw.echo.api.LocalAPIHandler.js
@@ -64,12 +64,17 @@
/**
* @inheritdoc
*/
- mw.echo.api.LocalAPIHandler.prototype.markItemsRead = function (
itemIdArray ) {
+ mw.echo.api.LocalAPIHandler.prototype.markItemsRead = function (
itemIdArray, isRead ) {
var data = {
- action: 'echomarkread',
- list: itemIdArray.join( '|' )
+ action: 'echomarkread'
};
+ if ( isRead ) {
+ data.list = itemIdArray.join( '|' );
+ } else {
+ data.unreadlist = itemIdArray.join( '|' );
+ }
+
return this.api.postWithToken( 'edit', data );
};
diff --git a/modules/ooui/mw.echo.ui.NotificationItemWidget.js
b/modules/ooui/mw.echo.ui.NotificationItemWidget.js
index ab000db..4351e2e 100644
--- a/modules/ooui/mw.echo.ui.NotificationItemWidget.js
+++ b/modules/ooui/mw.echo.ui.NotificationItemWidget.js
@@ -135,13 +135,13 @@
}
}
// Add a "mark as read" secondary action
- this.markAsReadSecondary = new mw.echo.ui.MenuItemWidget( {
- icon: 'check',
- data: 'markAsRead',
+ this.toggleReadSecondaryButton = new mw.echo.ui.MenuItemWidget(
{
+ data: 'toggleRead',
prioritized: false,
- label: mw.msg( 'echo-notification-markasread' ),
classes: [
'mw-echo-ui-notificationItemWidget-content-actions-button' ]
} );
+ this.menuPopupButtonWidget.getMenu().addItems( [
this.toggleReadSecondaryButton ] );
+
// Toggle 'mark as read' functionality
this.toggleMarkAsReadButtons( !this.markReadWhenSeen &&
!this.model.isRead() );
@@ -249,27 +249,35 @@
mw.echo.ui.NotificationItemWidget.prototype.onPopupButtonWidgetChoose =
function ( item ) {
var action = item && item.getData();
- if ( action === 'markAsRead' ) {
- this.model.toggleRead( true );
+ if ( action === 'toggleRead' ) {
+ this.model.toggleRead();
}
};
/**
- * Toggle the visibility of the 'mark as read' buttons and update the
visibility
- * of the secondary menu accordingly.
+ * Toggle the function of the 'mark as read' secondary button from
'mark as read' to
+ * 'mark as unread' and update the visibility of the primary 'mark as
read' X button.
+ *
*
* @param {boolean} [show] Show the 'mark as read' buttons
+ * - "false" means that the item is marked as read, whereby we show
the user 'mark unread'
+ * and hide the primary 'x' button
+ * - "true" means that the item is marked as unread and we show the
user 'mark as read'
+ * primary and secondary buttons.
*/
mw.echo.ui.NotificationItemWidget.prototype.toggleMarkAsReadButtons =
function ( show ) {
show = show !== undefined ? show : !this.model.isRead();
this.markAsReadButton.toggle( show );
- this.markAsReadSecondary.toggle( show );
if ( show ) {
- this.menuPopupButtonWidget.getMenu().addItems( [
this.markAsReadSecondary ] );
+ // Mark read
+ this.toggleReadSecondaryButton.setLabel( mw.msg(
'echo-notification-markasread' ) );
+ this.toggleReadSecondaryButton.setIcon( 'check' );
} else {
- this.menuPopupButtonWidget.getMenu().removeItems( [
this.markAsReadSecondary ] );
+ // Mark unread
+ this.toggleReadSecondaryButton.setLabel( mw.msg(
'echo-notification-markasunread' ) );
+ this.toggleReadSecondaryButton.setIcon( 'sun' );
}
this.menuPopupButtonWidget.toggle(
!this.menuPopupButtonWidget.getMenu().isEmpty() );
};
diff --git a/modules/viewmodel/mw.echo.dm.NotificationGroupItem.js
b/modules/viewmodel/mw.echo.dm.NotificationGroupItem.js
index a7cbbb9..089ff47 100644
--- a/modules/viewmodel/mw.echo.dm.NotificationGroupItem.js
+++ b/modules/viewmodel/mw.echo.dm.NotificationGroupItem.js
@@ -179,7 +179,7 @@
// Mark sub items as
read in the UI
model.markAllRead(
model.getSource(), model.getType() );
// Mark all existing
items as read in the API
-
model.markItemsReadInApi( idArray );
+
model.toggleItemsReadInApi( idArray, read );
};
} )( notifModels[ i ] ) );
}
diff --git a/modules/viewmodel/mw.echo.dm.NotificationsModel.js
b/modules/viewmodel/mw.echo.dm.NotificationsModel.js
index da6a22e..a176080 100644
--- a/modules/viewmodel/mw.echo.dm.NotificationsModel.js
+++ b/modules/viewmodel/mw.echo.dm.NotificationsModel.js
@@ -176,31 +176,30 @@
* @fires unreadChange
*/
mw.echo.dm.NotificationsModel.prototype.onItemRead = function ( item,
isRead ) {
- var id = item && item.getId(),
- unreadItem = id &&
this.unreadNotifications.getItemById( id );
+ var id = item && item.getId();
// Update unread status and emit events
- if ( unreadItem ) {
- if ( isRead ) {
- // We are skipping "mark as read" when the
operation is "mark all read"
- // because the API takes a single request to
mark all notifications
- // as read, and we don't need to send multiple
individual requests.
- if ( !this.markingAllAsRead ) {
- this.markItemsReadInApi( id );
- }
- if ( this.removeReadNotifications ) {
- // Remove this notification from the
model
- this.removeItems( [ unreadItem ] );
- }
- // Remove the item from the counter after all
other operations
- // finished, since some of the operations check
if there are any
- // unread notifications to begin with.
- this.unreadNotifications.removeItems( [
unreadItem ] );
- } else {
- this.unreadNotifications.addItems( [ unreadItem
] );
+ if ( isRead ) {
+ // We are skipping "mark as read" when the operation is
"mark all read"
+ // because the API takes a single request to mark all
notifications
+ // as read, and we don't need to send multiple
individual requests.
+ if ( !this.markingAllAsRead ) {
+ this.toggleItemsReadInApi( id, isRead );
}
- this.emit( 'unreadChange',
this.unreadNotifications.getItems() );
+ if ( this.removeReadNotifications ) {
+ // Remove this notification from the model
+ this.removeItems( [ item ] );
+ }
+ // Remove the item from the counter after all other
operations
+ // finished, since some of the operations check if
there are any
+ // unread notifications to begin with.
+ this.unreadNotifications.removeItems( [ item ] );
+ } else {
+ this.toggleItemsReadInApi( id, isRead );
+ this.unreadNotifications.addItems( [ item ] );
}
+
+ this.emit( 'unreadChange', this.unreadNotifications.getItems()
);
if ( this.unreadNotifications.isEmpty() ) {
this.emit( 'allRead' );
@@ -423,14 +422,14 @@
* @return {jQuery.Promise} A promise that resolves when the
notifications
* were marked as read.
*/
- mw.echo.dm.NotificationsModel.prototype.markItemsReadInApi = function (
itemIds ) {
- if ( !this.unreadNotifications.getItemCount() ) {
+ mw.echo.dm.NotificationsModel.prototype.toggleItemsReadInApi = function
( itemIds, isRead ) {
+ itemIds = $.isArray( itemIds ) ? itemIds : [ itemIds ];
+
+ if ( isRead && !this.unreadNotifications.getItemCount() ) {
return $.Deferred().resolve( 0 ).promise();
}
- itemIds = $.isArray( itemIds ) ? itemIds : [ itemIds ];
-
- return this.api.markItemsRead( itemIds, this.getSource(),
this.getType() );
+ return this.api.markItemsRead( itemIds, this.getSource(),
isRead );
};
/**
--
To view, visit https://gerrit.wikimedia.org/r/251309
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I677d3c0399e46fd7c35531df1cc0e61db2d4eb1b
Gerrit-PatchSet: 15
Gerrit-Project: mediawiki/extensions/Echo
Gerrit-Branch: master
Gerrit-Owner: Mooeypoo <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Mooeypoo <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits