From bd799ecc2e4762137743d49c1380c6e0d8c1fb5b Mon Sep 17 00:00:00 2001 From: bwang Date: Mon, 15 Nov 2021 17:09:51 -0600 Subject: [PATCH] Add watchstar to sticky header (alternative) Bug: T294759 Depends-on: I88af8585e8fc75f77ebef867d267199aeb2c6592 Change-Id: I15c409830ef8970ff7319b4dd447904443949b8d --- includes/SkinVector.php | 11 ++++ jsdoc.json | 1 + resources/skins.vector.es6/stickyHeader.js | 59 ++++++++++++++++++++-- skin.json | 8 ++- 4 files changed, 73 insertions(+), 6 deletions(-) diff --git a/includes/SkinVector.php b/includes/SkinVector.php index 2ec3951..e3ef812 100644 --- a/includes/SkinVector.php +++ b/includes/SkinVector.php @@ -68,6 +68,16 @@ class SkinVector extends SkinMustache { 'tabindex' => '-1', 'class' => 'sticky-header-icon' ]; + // Event and icon will be updated depending on watchstar state + private const WATCHSTAR_ICON = [ + 'href' => '#', + 'id' => 'ca-watchstar-sticky-header', + 'event' => 'watch-sticky-header', + 'icon' => 'wikimedia-star', + 'is-quiet' => true, + 'tabindex' => '-1', + 'class' => 'sticky-header-icon mw-watchlink' + ]; private const EDIT_VE_ICON = [ 'href' => '#', 'id' => 'ca-ve-edit-sticky-header', @@ -466,6 +476,7 @@ class SkinVector extends SkinMustache { $btns = [ self::TALK_ICON, self::HISTORY_ICON, + self::WATCHSTAR_ICON, ]; if ( $includeEditIcons ) { $btns[] = self::EDIT_WIKITEXT_ICON; diff --git a/jsdoc.json b/jsdoc.json index 741d5e3..d01fc4f 100644 --- a/jsdoc.json +++ b/jsdoc.json @@ -18,6 +18,7 @@ "wmf": { "linkMap": { "\"addEventListener\"": "https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener", + "jQuery": "https://api.jquery.com", "Document": "https://developer.mozilla.org/docs/Web/API/Document", "Element": "https://developer.mozilla.org/docs/Web/API/Element", "Event": "https://developer.mozilla.org/docs/Web/API/Event", diff --git a/resources/skins.vector.es6/stickyHeader.js b/resources/skins.vector.es6/stickyHeader.js index 9fee96b..2e629be 100644 --- a/resources/skins.vector.es6/stickyHeader.js +++ b/resources/skins.vector.es6/stickyHeader.js @@ -117,18 +117,52 @@ function removeClassFromNodes( nodes, className ) { } ); } +/** + * Ensures a sticky header button has the correct attributes + * + * @param {HTMLElement} watchSticky + * @param {string} status 'watched', 'unwatched', or 'temporary' + */ +function updateStickyWatchlink( watchSticky, status ) { + /* eslint-disable mediawiki/class-doc */ + watchSticky.classList.toggle( 'mw-ui-icon-wikimedia-star', status === 'unwatched' ); + watchSticky.classList.toggle( 'mw-ui-icon-wikimedia-unStar', status === 'watched' ); + watchSticky.classList.toggle( 'mw-ui-icon-wikimedia-halfStar', status === 'temporary' ); + /* eslint-enable mediawiki/class-doc */ + + watchSticky.setAttribute( 'data-event-name', status === 'unwatched' ? 'watch-sticky-header' : 'unwatch-sticky-header' ); +} + +/** + * Callback for watchsar + * + * @param {jQuery} $link Watchstar link + * @param {boolean} isWatched The page is watched + * @param {string} [expiry] Optional expiry time + */ +function watchstarCallback( $link, isWatched, expiry ) { + updateStickyWatchlink( + // @ts-ignore + $link[ 0 ], + expiry !== 'infinity' ? 'temporary' : + isWatched ? 'watched' : 'unwatched' + ); +} + /** * Makes sticky header icons functional for modern Vector. * * @param {HTMLElement} headerElement * @param {HTMLElement|null} history * @param {HTMLElement|null} talk + * @param {HTMLElement|null} watch */ -function prepareIcons( headerElement, history, talk ) { +function prepareIcons( headerElement, history, talk, watch ) { const historySticky = headerElement.querySelector( '#ca-history-sticky-header' ), - talkSticky = headerElement.querySelector( '#ca-talk-sticky-header' ); + talkSticky = headerElement.querySelector( '#ca-talk-sticky-header' ), + watchSticky = headerElement.querySelector( '#ca-watchstar-sticky-header' ); - if ( !historySticky || !talkSticky ) { + if ( !historySticky || !talkSticky || !watchSticky ) { throw new Error( 'Sticky header has unexpected HTML' ); } @@ -144,6 +178,22 @@ function prepareIcons( headerElement, history, talk ) { // @ts-ignore talkSticky.parentNode.removeChild( talkSticky ); } + if ( watch && watch.parentNode instanceof HTMLElement ) { + const watchContainer = watch.parentNode; + copyButtonAttributes( watch, watchSticky ); + updateStickyWatchlink( + // @ts-ignore + watchSticky, + watchContainer.classList.contains( 'mw-watchlink-temp' ) ? 'temporary' : + watchContainer.getAttribute( 'id' ) === 'ca-watch' ? 'unwatched' : 'watched' + ); + + const watchLib = require( /** @type {string} */( 'mediawiki.page.watch.ajax' ) ); + watchLib.watchstar( $( watchSticky ), mw.config.get( 'wgRelevantPageName' ), watchstarCallback ); + } else { + // @ts-ignore + watchSticky.parentNode.removeChild( watchSticky ); + } } /** @@ -331,7 +381,8 @@ function makeStickyHeaderFunctional( prepareIcons( headerElement, document.querySelector( '#ca-history a' ), - document.querySelector( '#ca-talk a' ) + document.querySelector( '#ca-talk a' ), + document.querySelector( '#ca-watch a, #ca-unwatch a' ) ); const veEdit = document.querySelector( '#ca-ve-edit a' ); diff --git a/skin.json b/skin.json index d7e07c2..f4bf537 100644 --- a/skin.json +++ b/skin.json @@ -179,10 +179,13 @@ "class": "ResourceLoaderOOUIIconPackModule", "variants": [], "icons": [ - "history", - "speechBubbles", "edit", "editLock", + "halfStar", + "history", + "speechBubbles", + "star", + "unStar", "wikiText" ] }, @@ -231,6 +234,7 @@ "dependencies": [ "skins.vector.icons.js", "mediawiki.page.ready", + "mediawiki.page.watch.ajax", "mediawiki.util", "mediawiki.experiments" ]