From 80dd6e2d7cd0c417bda42ecb2044464639ea3834 Mon Sep 17 00:00:00 2001 From: Ed Sanders Date: Fri, 17 Feb 2023 15:14:44 -0800 Subject: [PATCH] Add new topic subscription button to page actions Bug: T263821 Change-Id: Iab7f7d5fd1f7c229c2e0cda9369676cfd401a24e --- extension.json | 5 ++++ i18n/en.json | 4 ++++ i18n/qqq.json | 4 ++++ includes/Hooks/PageHooks.php | 39 ++++++++++++++++++++++++++++++ modules/topicsubscriptions.js | 45 ++++++++++++++++++++++++++++++----- modules/utils.js | 11 +++++++++ 6 files changed, 102 insertions(+), 6 deletions(-) diff --git a/extension.json b/extension.json index a0d130444..2cecb326e 100644 --- a/extension.json +++ b/extension.json @@ -123,6 +123,10 @@ "discussiontools-newtopic-legacy-hint", "discussiontools-newtopic-placeholder-title", "discussiontools-newtopic-missing-title", + "discussiontools-newtopicssubscription-button-subscribe-label", + "discussiontools-newtopicssubscription-button-subscribe-tooltip", + "discussiontools-newtopicssubscription-button-unsubscribe-label", + "discussiontools-newtopicssubscription-button-unsubscribe-tooltip", "discussiontools-newtopicssubscription-notify-subscribed-body", "discussiontools-newtopicssubscription-notify-subscribed-title", "discussiontools-newtopicssubscription-notify-unsubscribed-body", @@ -408,6 +412,7 @@ "GetActionName": "page", "OutputPageBeforeHTML": "page", "OutputPageParserOutput": "page", + "SkinTemplateNavigation::Universal": "page", "TitleGetEditNotices": "page", "ResourceLoaderGetConfigVars": "resourceloader", "GetBetaFeaturePreferences": "\\MediaWiki\\Extension\\DiscussionTools\\Hooks\\PreferenceHooks::onGetBetaFeaturePreferences", diff --git a/i18n/en.json b/i18n/en.json index 9aec1133b..d1f6c9ef5 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -53,6 +53,10 @@ "discussiontools-newtopic-legacy-hint-return": "You are now viewing the legacy experience. You can [$1 return to the new experience] or visit preferences to [$2 set the legacy experience as your default].", "discussiontools-newtopic-missing-title": "Please provide a title for your discussion topic. If you click \"{{int:discussiontools-replywidget-newtopic}}\", your topic will be added without a title.", "discussiontools-newtopic-placeholder-title": "Subject", + "discussiontools-newtopicssubscription-button-subscribe-label": "Subscribe", + "discussiontools-newtopicssubscription-button-subscribe-tooltip": "Subscribe to receive notifications when new topics are started on this page.", + "discussiontools-newtopicssubscription-button-unsubscribe-label": "Unsubscribe", + "discussiontools-newtopicssubscription-button-unsubscribe-tooltip": "Unsubscribe to stop receiving notifications when new topics are started on this page.", "discussiontools-newtopicssubscription-notify-subscribed-body": "You will receive notifications when new topics are started on this page.", "discussiontools-newtopicssubscription-notify-subscribed-title": "You have subscribed!", "discussiontools-newtopicssubscription-notify-unsubscribed-body": "You will no longer receive notifications when new topics are started on this page.", diff --git a/i18n/qqq.json b/i18n/qqq.json index 2cfe16d6a..9791d1503 100644 --- a/i18n/qqq.json +++ b/i18n/qqq.json @@ -67,6 +67,10 @@ "discussiontools-newtopic-legacy-hint-return": "Message shown on the legacy section adding tool", "discussiontools-newtopic-missing-title": "Warning message shown when leaving the title field empty while adding a new topic to the page.\n\nCopy {{int:discussiontools-replywidget-newtopic}} into your translation. It will be shown as {{msg-mw|discussiontools-replywidget-newtopic}}", "discussiontools-newtopic-placeholder-title": "Placeholder describing the heading field of a new topic\n{{identical|Subject}}", + "discussiontools-newtopicssubscription-button-subscribe-label": "Label for the button to subscribe to notifications about new topics on this page.", + "discussiontools-newtopicssubscription-button-subscribe-tooltip": "Tooltip for the button to subscribe to notifications about new topics on this page.", + "discussiontools-newtopicssubscription-button-unsubscribe-label": "Label for the button to unsubscribe from notifications about new topics on this page.", + "discussiontools-newtopicssubscription-button-unsubscribe-tooltip": "Tooltip for the button to unsubscribe from notifications about new topics on this page.", "discussiontools-newtopicssubscription-notify-subscribed-body": "Body of notification shown when a user subscribes to notifications about new topics on this page.", "discussiontools-newtopicssubscription-notify-subscribed-title": "Title of notification shown when a user subscribes to notifications about new topics on this page.", "discussiontools-newtopicssubscription-notify-unsubscribed-body": "Body of notification shown when a user unsubscribes from notifications about new topics on this page.", diff --git a/includes/Hooks/PageHooks.php b/includes/Hooks/PageHooks.php index f58e20593..2ffa6e9f4 100644 --- a/includes/Hooks/PageHooks.php +++ b/includes/Hooks/PageHooks.php @@ -17,11 +17,13 @@ use Html; use IContextSource; use MediaWiki\Actions\Hook\GetActionNameHook; use MediaWiki\Extension\DiscussionTools\CommentFormatter; +use MediaWiki\Extension\DiscussionTools\CommentUtils; use MediaWiki\Extension\DiscussionTools\SubscriptionStore; use MediaWiki\Extension\VisualEditor\Hooks as VisualEditorHooks; use MediaWiki\Hook\BeforePageDisplayHook; use MediaWiki\Hook\OutputPageBeforeHTMLHook; use MediaWiki\Hook\OutputPageParserOutputHook; +use MediaWiki\Hook\SkinTemplateNavigation__UniversalHook; use MediaWiki\Hook\TitleGetEditNoticesHook; use MediaWiki\MediaWikiServices; use MediaWiki\Page\Hook\BeforeDisplayNoArticleTextHook; @@ -34,6 +36,7 @@ use OutputPage; use ParserOutput; use RequestContext; use Skin; +use SkinTemplate; use SpecialPage; use Title; @@ -43,6 +46,7 @@ class PageHooks implements GetActionNameHook, OutputPageBeforeHTMLHook, OutputPageParserOutputHook, + SkinTemplateNavigation__UniversalHook, TitleGetEditNoticesHook { @@ -527,6 +531,41 @@ class PageHooks implements return $wrapped; } + /** + * @param SkinTemplate $sktemplate + * @param array &$links + * @return void + * @phpcs:disable MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName + */ + public function onSkinTemplateNavigation__Universal( $sktemplate, &$links ): void { + $output = $sktemplate->getOutput(); + if ( HookUtils::isFeatureEnabledForOutput( $output, HookUtils::TOPICSUBSCRIPTION ) ) { + $user = $sktemplate->getUser(); + $title = $sktemplate->getTitle(); + $items = $this->subscriptionStore->getSubscriptionItemsForUser( + $user, + [ CommentUtils::getNewTopicsSubscriptionId( $title ) ] + ); + $subscriptionItem = count( $items ) ? $items[ 0 ] : null; + $isSubscribed = $subscriptionItem && !$subscriptionItem->isMuted(); + + $context = $sktemplate->getContext(); + $links['actions']['dt-page-subscribe'] = [ + 'text' => $context->msg( $isSubscribed ? + 'discussiontools-newtopicssubscription-button-unsubscribe-label' : + 'discussiontools-newtopicssubscription-button-subscribe-label' + ), + 'title' => $context->msg( $isSubscribed ? + 'discussiontools-newtopicssubscription-button-unsubscribe-tooltip' : + 'discussiontools-newtopicssubscription-button-subscribe-tooltip' + ), + 'data-mw-subscribed' => $isSubscribed ? '1' : '0', + // TODO: Provide a no-JS action? + 'href' => '#', + ]; + } + } + /** * @param Title $title Title object for the page the edit notices are for * @param int $oldid Revision ID that the edit notices are for (or 0 for latest) diff --git a/modules/topicsubscriptions.js b/modules/topicsubscriptions.js index 143528f1e..9bdc1db6a 100644 --- a/modules/topicsubscriptions.js +++ b/modules/topicsubscriptions.js @@ -16,24 +16,37 @@ var * * @param {HTMLElement} element Subscribe link * @param {number|null} state State constant (STATE_UNSUBSCRIBED, STATE_SUBSCRIBED or STATE_AUTOSUBSCRIBED) + * @param {HTMLElement|null} labelElement Subscribe link, if different to element + * @param {boolean} isNewTopics Is a subscribe link for new topics subscriptions */ -function updateSubscribeLink( element, state ) { +function updateSubscribeLink( element, state, labelElement, isNewTopics ) { + labelElement = labelElement || element; if ( state !== null ) { element.setAttribute( 'data-mw-subscribed', String( state ) ); } if ( state ) { - element.textContent = mw.msg( 'discussiontools-topicsubscription-button-unsubscribe' ); - element.setAttribute( 'title', mw.msg( 'discussiontools-topicsubscription-button-unsubscribe-tooltip' ) ); + labelElement.textContent = mw.msg( isNewTopics ? + 'discussiontools-newtopicssubscription-button-unsubscribe-label' : + 'discussiontools-topicsubscription-button-unsubscribe' ); + element.setAttribute( 'title', mw.msg( isNewTopics ? + 'discussiontools-newtopicssubscription-button-unsubscribe-tooltip' : + 'discussiontools-topicsubscription-button-unsubscribe-tooltip' ) + ); } else { - element.textContent = mw.msg( 'discussiontools-topicsubscription-button-subscribe' ); - element.setAttribute( 'title', mw.msg( 'discussiontools-topicsubscription-button-subscribe-tooltip' ) ); + labelElement.textContent = mw.msg( isNewTopics ? + 'discussiontools-newtopicssubscription-button-subscribe-label' : + 'discussiontools-topicsubscription-button-subscribe' ); + element.setAttribute( 'title', mw.msg( isNewTopics ? + 'discussiontools-newtopicssubscription-button-subscribe-tooltip' : + 'discussiontools-topicsubscription-button-subscribe-tooltip' ) + ); } } /** * Update a subscribe button * - * @param {OO.ui.ButtonWidget} button Button Subscribe button + * @param {OO.ui.ButtonWidget} button Subscribe button * @param {number|null} state State constant (STATE_UNSUBSCRIBED, STATE_SUBSCRIBED or STATE_AUTOSUBSCRIBED) */ function updateSubscribeButton( button, state ) { @@ -206,6 +219,26 @@ function initTopicSubscriptions( $container, threadItemSet ) { } ); } ); } ); + + // New topics subscription button + ( function () { + // eslint-disable-next-line no-jquery/no-global-selector + var $button = $( '#ca-dt-page-subscribe > a' ); + var $label = $button.find( 'span' ); + var titleObj = mw.Title.newFromText( mw.config.get( 'wgRelevantPageName' ) ); + var name = utils.getNewTopicsSubscriptionId( titleObj ); + + $button.on( 'click', function ( e ) { + e.preventDefault(); + // Get latest subscribedState + var subscribedState = getSubscribedStateFromElement( $button[ 0 ] ); + + changeSubscription( titleObj.getPrefixedText(), name, !subscribedState, true ) + .then( function ( result ) { + updateSubscribeLink( $button[ 0 ], result.subscribe ? STATE_SUBSCRIBED : STATE_UNSUBSCRIBED, $label[ 0 ], true ); + } ); + } ); + }() ); } function initSpecialTopicSubscriptions() { diff --git a/modules/utils.js b/modules/utils.js index 4e85c528c..4c3ab93dc 100644 --- a/modules/utils.js +++ b/modules/utils.js @@ -626,6 +626,16 @@ function compareRangesAlmostEqualBoundaries( a, b, boundary ) { return !foundContent; } +/** + * Get the ID for a new topics subscription from a page title + * + * @param {mw.Title} title Page title + * @return {string} ID for a new topics subscription + */ +function getNewTopicsSubscriptionId( title ) { + return 'p-topics-' + title.getNamespaceId() + ':' + title.getMain(); +} + /** * Check whether a jQuery event represents a plain left click, without any modifiers * @@ -655,5 +665,6 @@ module.exports = { linearWalk: linearWalk, linearWalkBackwards: linearWalkBackwards, compareRanges: compareRanges, + getNewTopicsSubscriptionId: getNewTopicsSubscriptionId, isUnmodifiedLeftClick: isUnmodifiedLeftClick };