diff --git a/extension.json b/extension.json index 28587c887..a0d8fb127 100644 --- a/extension.json +++ b/extension.json @@ -399,7 +399,6 @@ "RevisionDataUpdates": "dataupdates", "LoadExtensionSchemaUpdates": "installer", "ParserAfterTidy": "parser", - "ArticleViewHeader": "page", "BeforeDisplayNoArticleText": "page", "BeforePageDisplay": "page", "GetActionName": "page", diff --git a/includes/Hooks/PageHooks.php b/includes/Hooks/PageHooks.php index f96fb299f..0d5c45409 100644 --- a/includes/Hooks/PageHooks.php +++ b/includes/Hooks/PageHooks.php @@ -23,7 +23,6 @@ use MediaWiki\Hook\BeforePageDisplayHook; use MediaWiki\Hook\OutputPageBeforeHTMLHook; use MediaWiki\Hook\TitleGetEditNoticesHook; use MediaWiki\MediaWikiServices; -use MediaWiki\Page\Hook\ArticleViewHeaderHook; use MediaWiki\Page\Hook\BeforeDisplayNoArticleTextHook; use MediaWiki\User\UserNameUtils; use MediaWiki\User\UserOptionsLookup; @@ -31,14 +30,12 @@ use OOUI\ButtonWidget; use OOUI\HtmlSnippet; use OOUI\MessageWidget; use OutputPage; -use ParserOutput; use RequestContext; use Skin; use SpecialPage; use Title; class PageHooks implements - ArticleViewHeaderHook, BeforeDisplayNoArticleTextHook, BeforePageDisplayHook, GetActionNameHook, @@ -269,6 +266,39 @@ class PageHooks implements } } + if ( $output->getSkin()->getSkinName() === 'minerva' ) { + $title = $output->getTitle(); + + if ( + HookUtils::isFeatureEnabledForOutput( $output, HookUtils::NEWTOPICTOOL ) && + // Only add the button if "New section" tab would be shown in a normal skin. + HookUtils::shouldShowNewSectionTab( $output->getContext() ) + ) { + $output->enableOOUI(); + $output->addModuleStyles( [ + // For speechBubbleAdd + 'oojs-ui.styles.icons-alerts', + ] ); + $output->addBodyClasses( 'ext-discussiontools-init-new-topic-opened' ); + + // Minerva doesn't show a new topic button by default, unless the MobileFrontend + // talk page feature is enabled, but we shouldn't depend on code from there. + $text .= Html::rawElement( 'div', + [ 'class' => 'ext-discussiontools-init-new-topic' ], + ( new ButtonWidget( [ + 'classes' => [ 'ext-discussiontools-init-new-topic-button' ], + 'href' => $title->getLinkURL( [ 'action' => 'edit', 'section' => 'new' ] ), + 'icon' => 'speechBubbleAdd', + 'label' => $output->getContext()->msg( 'skin-action-addsection' )->text(), + 'flags' => [ 'progressive', 'primary' ], + 'infusable' => true, + ] ) ) + // For compatibility with Minerva click tracking (T295490) + ->setAttributes( [ 'data-event-name' => 'talkpage.add-topic' ] ) + ); + } + } + return true; } @@ -412,44 +442,6 @@ class PageHooks implements return $wrapped; } - /** - * @param Article $article - * @param bool|ParserOutput &$outputDone - * @param bool &$pcache - * @return bool|void - */ - public function onArticleViewHeader( $article, &$outputDone, &$pcache ) { - $context = $article->getContext(); - $output = $context->getOutput(); - - if ( $output->getSkin()->getSkinName() === 'minerva' ) { - $title = $article->getTitle(); - - if ( - HookUtils::isFeatureEnabledForOutput( $output, HookUtils::NEWTOPICTOOL ) && - // Only add the button if "New section" tab would be shown in a normal skin. - HookUtils::shouldShowNewSectionTab( $context ) - ) { - $output->enableOOUI(); - - // Minerva doesn't show a new topic button by default, unless the MobileFrontend - // talk page feature is enabled, but we shouldn't depend on code from there. - $output->addHTML( - Html::rawElement( 'div', - [ 'class' => 'ext-discussiontools-init-new-topic' ], - ( new ButtonWidget( [ - 'href' => $title->getLinkURL( [ 'action' => 'edit', 'section' => 'new' ] ), - 'label' => $context->msg( 'skin-action-addsection' )->text(), - 'flags' => [ 'progressive', 'primary' ], - ] ) ) - // For compatibility with Minerva click tracking (T295490) - ->setAttributes( [ 'data-event-name' => 'talkpage.add-topic' ] ) - ) - ); - } - } - } - /** * @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/dt.init.less b/modules/dt.init.less index 7a4496038..9b4c65230 100644 --- a/modules/dt.init.less +++ b/modules/dt.init.less @@ -185,10 +185,6 @@ h1, h2, h3, h4, h5, h6 { } } -.ext-discussiontools-init-new-topic { - margin: 0.5em 0; -} - // Topic subscriptions (link) .ext-discussiontools-init-section-subscribe { display: none; @@ -728,20 +724,78 @@ h1, h2, h3, h4, h5, h6 { .ext-discussiontools-init-readAsWikiPage { display: block; - /* Not sticky per T309889 */ - width: 100%; - padding: 1em; - color: @colorProgressive; - background: @colorGray15; - border-top: 1px solid @colorGray14; - text-align: center; - // Match Minerva styles - max-width: 993.3px; - margin-left: auto; - margin-right: auto; - // Avoid smooshing with content / empty state (T320755) - margin-top: 32px; - margin-bottom: -32px; // stylelint-disable-line declaration-block-no-redundant-longhand-properties + } +} + +.minerva-footer-button() { + width: 100%; + padding: 1em; + box-sizing: content-box; + color: @colorProgressive; + background: @colorGray15; + border-top: 1px solid @colorGray14; + text-align: center; + // Match Minerva styles + max-width: 993.3px; + // stylelint-disable-next-line declaration-no-important + margin-left: -16px !important; + // stylelint-disable-next-line declaration-no-important + margin-right: -16px !important; +} + +.ext-discussiontools-init-readAsWikiPage { + /* Not sticky per T309889 */ + .minerva-footer-button(); + // Avoid smooshing with content / empty state (T320755) + margin-top: 32px; + margin-bottom: -32px; +} + +.ext-discussiontools-init-new-topic { + .minerva-footer-button(); + // margin: 0.5em 0 !important; + // stylelint-disable-next-line declaration-no-important + display: block !important; + // stylelint-disable-next-line plugin/no-unsupported-browser-features + position: sticky; + // Required for IntersectionObserver trick + bottom: -1px; + transition: transform 250ms, opacity 250ms; + transform: translateY( 100% ); + opacity: 0; + // Avoid smooshing with content / empty state (T320755) + margin-top: 32px; + + & + .ext-discussiontools-init-readAsWikiPage { + margin-top: 0; + } + + .ext-discussiontools-init-new-topic-open &, + .ext-discussiontools-init-new-topic-opened &, + .client-nojs & { + transform: translateY( 0 ); + opacity: 1; + } + + .ext-discussiontools-init-new-topic-close & { + transform: translateY( 100% ); + opacity: 0; + } + + .ext-discussiontools-init-new-topic-closed &, + .ext-discussiontools-init-virtual-keyboard-open &, + &-pinned.oo-ui-buttonElement { + transform: translateY( 0 ); + opacity: 1; + position: static; + transition: none; + } + + .ext-discussiontools-init-replylink-open & { + .ext-discussiontools-fake-disabled(); + transform: translateY( 0 ); + opacity: 1; + position: static; } // Always hide the table of content. This is usually hidden by the mf-section-0 rules, diff --git a/modules/mobile.js b/modules/mobile.js index ba507fa18..5f6855dc9 100644 --- a/modules/mobile.js +++ b/modules/mobile.js @@ -1,4 +1,4 @@ -var $readAsWikiPage, ledeSectionDialog; +var newTopicButton, $readAsWikiPage, ledeSectionDialog; var viewportScrollContainer = null; var wasKeyboardOpen = null; var initialClientHeight = null; @@ -81,6 +81,53 @@ function init( $container ) { } ); } ); } + + if ( + !newTopicButton && + // eslint-disable-next-line no-jquery/no-global-selector + $( '.ext-discussiontools-init-new-topic-button' ).length + ) { + // eslint-disable-next-line no-jquery/no-global-selector + newTopicButton = OO.ui.infuse( $( '.ext-discussiontools-init-new-topic-button' ) ); + // For compatibility with Minerva click tracking (T295490) + newTopicButton.$element.attr( 'data-event-name', 'talkpage.add-topic' ); + var $scrollContainer = $( OO.ui.Element.static.getClosestScrollableContainer( document.body ) ); + var $scrollListener = $scrollContainer.is( 'html, body' ) ? $( OO.ui.Element.static.getWindow( $scrollContainer[ 0 ] ) ) : $scrollContainer; + var lastScrollTop = $scrollContainer.scrollTop(); + var wasScrollDown = null; + var $body = $( document.body ); + // TODO: Use ve.addPassiveEventListener + $scrollListener.on( 'scroll', OO.ui.throttle( function () { + var scrollTop = $scrollContainer.scrollTop(); + var isScrollDown = scrollTop > lastScrollTop; + if ( isScrollDown !== wasScrollDown ) { + if ( !isScrollDown ) { + newTopicButton.$element.css( 'transition', 'none' ); + } + $body.removeClass( [ 'ext-discussiontools-init-new-topic-closed', 'ext-discussiontools-init-new-topic-opened' ] ); + requestAnimationFrame( function () { + newTopicButton.$element.css( 'transition', '' ); + $body.addClass( isScrollDown ? 'ext-discussiontools-init-new-topic-close' : 'ext-discussiontools-init-new-topic-open' ); + setTimeout( function () { + $body.removeClass( [ 'ext-discussiontools-init-new-topic-close', 'ext-discussiontools-init-new-topic-open' ] ); + $body.addClass( isScrollDown ? 'ext-discussiontools-init-new-topic-closed' : 'ext-discussiontools-init-new-topic-opened' ); + }, 250 ); + } ); + } + + var observer = new IntersectionObserver( + function ( entries ) { + newTopicButton.$element.toggleClass( 'ext-discussiontools-init-new-topic-pinned', entries[ 0 ].intersectionRatio === 1 ); + }, + { threshold: [ 1 ] } + ); + + observer.observe( newTopicButton.$element[ 0 ] ); + + lastScrollTop = scrollTop; + wasScrollDown = isScrollDown; + }, 200 ) ); + } if ( !$readAsWikiPage ) { // Read as wiki page button, copied from renderReadAsWikiPageButton in Minerva $readAsWikiPage = $( '