diff --git a/includes/MinervaHooks.php b/includes/MinervaHooks.php index ebd7086..42b2536 100644 --- a/includes/MinervaHooks.php +++ b/includes/MinervaHooks.php @@ -67,6 +67,13 @@ class MinervaHooks { $config->get( 'MinervaPageIssuesNewTreatment' ) ) ); + $featureManager->registerFeature( + new MobileFrontend\Features\Feature( + 'MinervaTalkAtTop', + 'skin-minerva', + $config->get( 'MinervaTalkAtTop' ) + ) + ); } catch ( RuntimeException $e ) { // features already registered... // due to a bug it's possible for this to run twice @@ -171,11 +178,17 @@ class MinervaHooks { ) { // setSkinOptions is not available if ( $skin instanceof SkinMinerva ) { - $featureManager = \MediaWiki\MediaWikiServices::getInstance() + $services = \MediaWiki\MediaWikiServices::getInstance(); + $featureManager = $services ->getService( 'MobileFrontend.FeaturesManager' ); + $userMode = $services->getService( 'MobileFrontend.AMC.UserMode' ); $isBeta = $mobileContext->isBetaGroupMember(); $skin->setSkinOptions( [ + SkinMinerva::OPTION_AMC => $userMode->isEnabled(), + SkinMinerva::OPTIONS_TALK_AT_TOP => $featureManager->isFeatureAvailableForCurrentUser( + 'MinervaTalkAtTop' + ), SkinMinerva::OPTIONS_MOBILE_BETA => $isBeta, SkinMinerva::OPTION_CATEGORIES diff --git a/includes/skins/MinervaTemplate.php b/includes/skins/MinervaTemplate.php index a3cf841..7058c09 100644 --- a/includes/skins/MinervaTemplate.php +++ b/includes/skins/MinervaTemplate.php @@ -231,6 +231,7 @@ class MinervaTemplate extends BaseTemplate { */ protected function render( $data ) { $templateParser = new TemplateParser( __DIR__ ); + $skin = $this->getSkin(); $internalBanner = $data[ 'internalBanner' ]; $preBodyHtml = isset( $data['prebodyhtml'] ) ? $data['prebodyhtml'] : ''; $hasHeadingHolder = $internalBanner || $preBodyHtml || isset( $data['page_actions'] ); @@ -270,7 +271,10 @@ class MinervaTemplate extends BaseTemplate { 'contenthtml' => $this->getContentHtml( $data ), 'secondaryactionshtml' => $this->getSecondaryActionsHtml(), 'footer' => $this->getFooterTemplateData( $data ), - 'isBeta' => $this->getSkin()->getSkinOption( SkinMinerva::OPTIONS_MOBILE_BETA ), + 'isBeta' => $skin->getSkinOption( SkinMinerva::OPTIONS_MOBILE_BETA ), + 'tabs' => !$this->isSpecialPage && $skin->getSkinOption( SkinMinerva::OPTIONS_TALK_AT_TOP ) ? [ + 'items' => array_values( $data['content_navigation']['namespaces'] ), + ] : false, ]; // begin rendering echo $templateParser->processTemplate( 'minerva', $templateData ); diff --git a/includes/skins/SkinMinerva.php b/includes/skins/SkinMinerva.php index 2c4a7aa..6428275 100644 --- a/includes/skins/SkinMinerva.php +++ b/includes/skins/SkinMinerva.php @@ -30,12 +30,15 @@ use MediaWiki\Minerva\SkinUserPageHelper; class SkinMinerva extends SkinTemplate { /** Set of keys for available skin options. See $skinOptions. */ const OPTION_MOBILE_OPTIONS = 'mobileOptionsLink'; + const OPTION_AMC = 'amc'; const OPTION_CATEGORIES = 'categories'; const OPTION_BACK_TO_TOP = 'backToTop'; const OPTION_PAGE_ISSUES = 'pageIssues'; const OPTION_SHARE_BUTTON = 'shareButton'; const OPTION_TOGGLING = 'toggling'; const OPTIONS_MOBILE_BETA = 'beta'; + const OPTIONS_TALK_AT_TOP = 'talkAtTop'; + /** @const LEAD_SECTION_NUMBER integer which corresponds to the lead section in editing mode */ const LEAD_SECTION_NUMBER = 0; @@ -106,6 +109,7 @@ class SkinMinerva extends SkinTemplate { /** Whether sections can be collapsed (requires MobileFrontend and MobileFormatter) */ self::OPTION_TOGGLING => false, self::OPTION_PAGE_ISSUES => false, + self::OPTIONS_TALK_AT_TOP => false, ]; /** @@ -144,6 +148,7 @@ class SkinMinerva extends SkinTemplate { */ protected function prepareQuickTemplate() { $out = $this->getOutput(); + // add head items $out->addMeta( 'viewport', 'initial-scale=1.0, user-scalable=yes, minimum-scale=0.25, ' . 'maximum-scale=5.0, width=device-width' @@ -1093,7 +1098,9 @@ class SkinMinerva extends SkinTemplate { // in stable it will link to the wikitext talk page $title = $this->getTitle(); $namespaces = $tpl->data['content_navigation']['namespaces']; - if ( !$this->getUserPageHelper()->isUserPage() && $this->isTalkAllowed() ) { + if ( !$this->getUserPageHelper()->isUserPage() && $this->isTalkAllowed() + && !$this->getSkinOption( self::OPTIONS_TALK_AT_TOP ) + ) { // FIXME [core]: This seems unnecessary.. $subjectId = $title->getNamespaceKey( '' ); $talkId = $subjectId === 'main' ? 'talk' : "{$subjectId}_talk"; @@ -1444,6 +1451,10 @@ class SkinMinerva extends SkinTemplate { $styles[] = 'skins.minerva.icons.loggedin'; } + if ( $this->getSkinOption( self::OPTION_AMC ) ) { + $styles[] = 'skins.minerva.amc.styles'; + } + return $styles; } } diff --git a/includes/skins/minerva.mustache b/includes/skins/minerva.mustache index df57f82..4c1b288 100644 --- a/includes/skins/minerva.mustache +++ b/includes/skins/minerva.mustache @@ -31,6 +31,11 @@
{{{headinghtml}}} {{{taglinehtml}}} + {{#tabs}} + {{#items}} + {{text}} + {{/items}} + {{/tabs}} {{{postheadinghtml}}} {{{subtitle}}} {{{pageactionshtml}}} @@ -47,4 +52,4 @@
- + diff --git a/minerva.less/minerva.variables.less b/minerva.less/minerva.variables.less index 4ad5bfa..eb08eb9 100644 --- a/minerva.less/minerva.variables.less +++ b/minerva.less/minerva.variables.less @@ -45,6 +45,12 @@ @titleSectionSpacingTop: 20px; @titleSectionSpacingBottom: 25px; +// Page actions +@pageActionsGutter: 0.5em; +@pageActionsHeight: @pageActionFontSize + (2 * @iconGutterWidth); +@tabBorderSize: ( 1em / 16px ) * 2; +@taglineFontSize: 0.85em; + // colors @chromeColor: @grayLightest; @semiTransparent: rgba( 0, 0, 0, 0.8 ); diff --git a/resources/skins.minerva.amc.styles/index.less b/resources/skins.minerva.amc.styles/index.less new file mode 100644 index 0000000..225376d --- /dev/null +++ b/resources/skins.minerva.amc.styles/index.less @@ -0,0 +1,4 @@ +@import '../../minerva.less/minerva.variables'; +@import '../../minerva.less/minerva.mixins'; + +@import "tabs.less"; diff --git a/resources/skins.minerva.amc.styles/tabs.less b/resources/skins.minerva.amc.styles/tabs.less new file mode 100644 index 0000000..e604836 --- /dev/null +++ b/resources/skins.minerva.amc.styles/tabs.less @@ -0,0 +1,23 @@ +.minerva__tab { + font-size: @taglineFontSize; + margin: 18px 10px 1px 0; + color: @colorGray5; + font-weight: bold; + padding-bottom: 6px; + display: inline-block; + + &:visited, + &:hover, + &:active, + &.new, + &.new:visited, + &.new:active, + &.new:hover { + color: @colorGray5; + text-decoration: none; + } + // note core doesn't use BEM. + &.selected { + border-bottom: @tabBorderSize solid @colorGray5; + } +} diff --git a/resources/skins.minerva.base.styles/pageactions.less b/resources/skins.minerva.base.styles/pageactions.less index 1ed5b3b..baf1c3d 100644 --- a/resources/skins.minerva.base.styles/pageactions.less +++ b/resources/skins.minerva.base.styles/pageactions.less @@ -26,7 +26,7 @@ .tagline { color: @colorGray5; - font-size: 0.85em; + font-size: @taglineFontSize; margin: 2px 0 12px; } } diff --git a/resources/skins.minerva.talk/init.js b/resources/skins.minerva.talk/init.js index fc93340..6789876 100644 --- a/resources/skins.minerva.talk/init.js +++ b/resources/skins.minerva.talk/init.js @@ -5,7 +5,7 @@ LoadingOverlay = mobile.LoadingOverlay, eventBus = new EventEmitter(), // eslint-disable-next-line jquery/no-global-selector - $talk = $( '.talk' ), + $talk = $( '.talk, [rel="discussion"]' ), // use the plain return value here - T128273 title = $talk.attr( 'data-title' ), overlayManager = M.require( 'skins.minerva.scripts/overlayManager' ), @@ -13,12 +13,10 @@ inTalkNamespace = false, pageTitle, talkTitle, talkNs, pageNs; - // if there's no title for any reason, don't do anything - if ( !title ) { - return; - } // T127190 - title = decodeURIComponent( title ); + if ( title ) { + title = decodeURIComponent( title ); + } // sanity check: the talk namespace needs to have the next higher integer // of the page namespace (the api should add topics only to the talk page of the current @@ -26,9 +24,13 @@ // (https://www.mediawiki.org/wiki/Manual:Using_custom_namespaces#Creating_a_custom_namespace) // The method to get associated namespaces will change later (maybe), see T487 pageTitle = mw.Title.newFromText( mw.config.get( 'wgPageName' ) ); - talkTitle = mw.Title.newFromText( title ); + talkTitle = title ? mw.Title.newFromText( title ) : pageTitle.getTalkPage(); - if ( !pageTitle || !talkTitle || pageTitle.getMainText() !== talkTitle.getMainText() ) { + // Check that there is a valid page and talk title + if ( !pageTitle || !talkTitle || + // the talk link points to something other than the current page + // so we chose to leave this as a normal link + pageTitle.getMainText() !== talkTitle.getMainText() ) { return; } talkNs = talkTitle.getNamespaceId(); @@ -42,7 +44,7 @@ overlayManager.add( /^\/talk\/?(.*)$/, function ( id ) { var talkOptions = { api: new mw.Api(), - title: title, + title: talkTitle.toText(), // T184273 using `getCurrentPage` because 'wgPageName' contains underscores instead of // spaces. currentPageTitle: M.getCurrentPage().title, diff --git a/skin.json b/skin.json index afeec2d..1c70258 100644 --- a/skin.json +++ b/skin.json @@ -35,6 +35,11 @@ "switch-language" ], "MinervaAlwaysShowLanguageButton": true, + "MinervaTalkAtTop": { + "base": false, + "beta": false, + "amc": true + }, "MinervaShowCategoriesButton": { "base": false, "beta": true @@ -207,6 +212,15 @@ "notifications": "resources/skins.minerva.icons.loggedin/bell.svg" } }, + "skins.minerva.amc.styles": { + "targets": [ + "mobile", + "desktop" + ], + "styles": [ + "resources/skins.minerva.amc.styles/index.less" + ] + }, "skins.minerva.icons.images": { "class": "ResourceLoaderImageModule", "selector": ".mw-ui-icon-minerva-{name}:before",