Consolidate watchstar icon updating logic under watchstar.js

Depends-on: Ib11177df52d46ecda2ace50ac78672ed3d5fd5c9
Bug: T336640
Bug: T336641
Change-Id: If2573409cd1af4580f89b33c32cd0441e7a80735
This commit is contained in:
bwang 2023-05-15 14:05:38 -05:00
parent 440843d84c
commit 27e821a486
6 changed files with 73 additions and 63 deletions

View file

@ -9,26 +9,22 @@ use MessageLocalizer;
*/
class VectorComponentStickyHeader implements VectorComponent {
private const TALK_ICON = [
'label' => '',
'icon' => 'speechBubbles',
'id' => 'ca-talk-sticky-header',
'event' => 'talk-sticky-header'
];
private const SUBJECT_ICON = [
'label' => '',
'icon' => 'article',
'id' => 'ca-subject-sticky-header',
'event' => 'subject-sticky-header'
];
private const HISTORY_ICON = [
'label' => '',
'icon' => 'wikimedia-history',
'id' => 'ca-history-sticky-header',
'event' => 'history-sticky-header',
];
// Event and icon will be updated depending on watchstar state
private const WATCHSTAR_ICON = [
'label' => '',
'id' => 'ca-watchstar-sticky-header',
'event' => 'watch-sticky-header',
'icon' => 'wikimedia-star',
@ -40,19 +36,16 @@ class VectorComponentStickyHeader implements VectorComponent {
'class' => 'mw-watchlink'
];
private const EDIT_VE_ICON = [
'label' => '',
'id' => 'ca-ve-edit-sticky-header',
'event' => 've-edit-sticky-header',
'icon' => 'wikimedia-edit',
];
private const EDIT_WIKITEXT_ICON = [
'label' => '',
'id' => 'ca-edit-sticky-header',
'event' => 'wikitext-edit-sticky-header',
'icon' => 'wikimedia-wikiText',
];
private const EDIT_PROTECTED_ICON = [
'label' => '',
'href' => '#',
'id' => 'ca-viewsource-sticky-header',
'event' => 've-edit-protected-sticky-header',
@ -114,7 +107,8 @@ class VectorComponentStickyHeader implements VectorComponent {
$iconButtons = [];
foreach ( $icons as $icon ) {
$iconButtons[] = new VectorComponentButton(
$icon[ 'label' ],
// Button labels will be populated in stickyHeader.js
"",
$icon[ 'icon' ],
$icon[ 'id' ],
$icon[ 'class' ] ?? '',

View file

@ -7,7 +7,7 @@ const languageButton = require( './languageButton.js' ),
ABTestConfig = require( /** @type {string} */ ( './activeABTest.json' ) ),
initSearchLoader = require( './searchLoader.js' ).initSearchLoader,
dropdownMenus = require( './dropdownMenus.js' ).dropdownMenus,
watchstar = require( './watchstar.js' ),
watchstar = require( './watchstar.js' ).init,
setupIntersectionObservers = require( './setupIntersectionObservers.js' ),
menuTabs = require( './menuTabs.js' );

View file

@ -3,6 +3,7 @@
*/
const
initSearchToggle = require( './searchToggle.js' ),
updateWatchIcon = require( './watchstar.js' ).updateWatchIcon,
STICKY_HEADER_ID = 'vector-sticky-header',
STICKY_HEADER_APPENDED_ID = '-sticky-header',
STICKY_HEADER_APPENDED_PARAM = [ 'wvprov', 'sticky-header' ],
@ -55,6 +56,10 @@ function hide() {
function copyButtonAttributes( from, to ) {
copyAttribute( from, to, 'href' );
copyAttribute( from, to, 'title' );
// Copy button labels
if ( to.lastElementChild && from.lastElementChild ) {
to.lastElementChild.innerHTML = from.lastElementChild.textContent || '';
}
}
/**
@ -120,6 +125,16 @@ function removeNode( node ) {
}
}
/**
* Ensures a sticky header button has the correct attributes
*
* @param {Element} watchLink
* @param {boolean} isWatched The page is watched
*/
function updateStickyWatchlink( watchLink, isWatched ) {
watchLink.setAttribute( 'data-event-name', isWatched ? 'watch-sticky-header' : 'unwatch-sticky-header' );
}
/**
* @param {NodeList} nodes
* @param {string} className
@ -139,32 +154,14 @@ function removeNodes( nodes ) {
} );
}
/**
* Ensures a sticky header button has the correct attributes
*
* @param {Element} watchSticky
* @param {string} status 'watched', 'unwatched', or 'temporary'
*/
function updateStickyWatchlink( watchSticky, status ) {
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' );
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(
/** @type {HTMLAnchorElement} */( $link[ 0 ] ),
expiry !== 'infinity' ? 'temporary' :
isWatched ? 'watched' : 'unwatched'
);
function watchstarCallback( $link, isWatched ) {
updateStickyWatchlink( /** @type {HTMLAnchorElement} */( $link[ 0 ] ), isWatched );
}
/**
@ -206,12 +203,14 @@ function prepareIcons( header, history, talk, subject, watch ) {
if ( watch && watch.parentNode instanceof Element ) {
const watchContainer = watch.parentNode;
const isTemporaryWatch = watchContainer.classList.contains( 'mw-watchlink-temp' );
const isWatched = isTemporaryWatch || watchContainer.getAttribute( 'id' ) === 'ca-unwatch';
const watchIcon = /** @type {HTMLElement} */ ( watchSticky.querySelector( '.mw-ui-icon' ) );
// Initialize sticky watchlink
copyButtonAttributes( watch, watchSticky );
updateStickyWatchlink(
watchSticky,
watchContainer.classList.contains( 'mw-watchlink-temp' ) ? 'temporary' :
watchContainer.getAttribute( 'id' ) === 'ca-watch' ? 'unwatched' : 'watched'
);
updateWatchIcon( watchIcon, isWatched, isTemporaryWatch ? '' : 'infinity' );
updateStickyWatchlink( watchSticky, isWatched );
const watchLib = require( /** @type {string} */( 'mediawiki.page.watch.ajax' ) );
watchLib.watchstar( $( watchSticky ), mw.config.get( 'wgRelevantPageName' ), watchstarCallback );

View file

@ -1,30 +1,45 @@
module.exports = function () {
/**
* @param {HTMLElement} watchIcon
* @param {boolean} isWatched
* @param {string} expiry
*/
const updateWatchIcon = ( watchIcon, isWatched, expiry ) => {
watchIcon.classList.remove(
// Vector attaches two icon classes to the element.
// Remove the mw-ui-icon one rather than managing both.
'mw-ui-icon-star',
'mw-ui-icon-unStar',
'mw-ui-icon-wikimedia-unStar',
'mw-ui-icon-wikimedia-star',
'mw-ui-icon-wikimedia-halfStar'
);
if ( isWatched ) {
if ( expiry === 'infinity' ) {
watchIcon.classList.add( 'mw-ui-icon-wikimedia-unStar' );
} else {
watchIcon.classList.add( 'mw-ui-icon-wikimedia-halfStar' );
}
} else {
watchIcon.classList.add( 'mw-ui-icon-wikimedia-star' );
}
};
const init = () => {
mw.hook( 'wikipage.watchlistChange' ).add(
function ( /** @type {boolean} */ isWatched, /** @type {string} */ expiry ) {
const watchIcon = document.querySelectorAll( '#ca-watch .mw-ui-icon, #ca-unwatch .mw-ui-icon' )[ 0 ];
if ( !watchIcon ) {
const watchIcons = document.querySelectorAll( '.mw-watchlink .mw-ui-icon' );
if ( !watchIcons ) {
return;
}
watchIcon.classList.remove(
// Vector attaches two icon classes to the element.
// Remove the mw-ui-icon one rather than managing both.
'mw-ui-icon-star',
'mw-ui-icon-unStar',
'mw-ui-icon-wikimedia-unStar',
'mw-ui-icon-wikimedia-star',
'mw-ui-icon-wikimedia-halfStar'
);
if ( isWatched ) {
if ( expiry === 'infinity' ) {
watchIcon.classList.add( 'mw-ui-icon-wikimedia-unStar' );
} else {
watchIcon.classList.add( 'mw-ui-icon-wikimedia-halfStar' );
}
} else {
watchIcon.classList.add( 'mw-ui-icon-wikimedia-star' );
}
Array.from( watchIcons ).forEach( ( watchIcon ) => {
updateWatchIcon( /** @type {HTMLElement} */ ( watchIcon ), isWatched, expiry );
} );
}
);
};
module.exports = {
updateWatchIcon,
init
};

View file

@ -7,10 +7,12 @@
* (in which case it is inside `.vector-menu-dropdown` instead of `.vector-menu-tabs`). */
// Note: there's no watchstar for anon users so no need to worry about cached HTML when changing this class
// Loading watchstar link class.
.mw-watchlink .mw-ui-icon::before {
transition: transform 500ms;
}
.mw-watchlink {
.mw-ui-icon {
transition: transform 500ms;
}
.mw-ui-icon-wikimedia-unStar::before {
transform: rotate( 72deg );
.mw-ui-icon-wikimedia-unStar {
transform: rotate( 72deg );
}
}

View file

@ -39,7 +39,7 @@
@import './components/PageTitlebar.less';
@import './components/PageToolbar.less';
@import './components/PopupNotification.less';
@import './components/TabWatchstarLink.less';
@import './components/Watchstar.less';
@import './components/Icon.less';
}