diff --git a/includes/MinervaHooks.php b/includes/MinervaHooks.php
index ebd70866a..42b253635 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 a3cf84109..7058c093a 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 2c4a7aaa0..64282752e 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 df57f821e..4c1b288f5 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 4ad5bfa63..eb08eb90c 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 000000000..225376df6
--- /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 000000000..e6048362a
--- /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 1ed5b3b84..baf1c3d68 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 fc9334087..6789876e8 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 afeec2dd5..1c702587f 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",