mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/DiscussionTools
synced 2024-11-24 00:13:36 +00:00
Support reply tool on mobile
Bug: T270536 Change-Id: I94d04e9cd442f9a4e0c5924da67c43a768417a8b
This commit is contained in:
parent
54471b5037
commit
900a01772f
|
@ -82,7 +82,6 @@
|
|||
"moment",
|
||||
"rangefix",
|
||||
"oojs-ui-windows",
|
||||
"mediawiki.action.view.postEdit",
|
||||
"mediawiki.api",
|
||||
"mediawiki.Title",
|
||||
"mediawiki.Uri",
|
||||
|
@ -119,6 +118,10 @@
|
|||
"discussiontools-topicsubscription-notify-subscribed-title",
|
||||
"discussiontools-topicsubscription-notify-unsubscribed-body",
|
||||
"discussiontools-topicsubscription-notify-unsubscribed-title"
|
||||
],
|
||||
"targets": [
|
||||
"desktop",
|
||||
"mobile"
|
||||
]
|
||||
},
|
||||
"ext.discussionTools.debug": {
|
||||
|
@ -129,6 +132,10 @@
|
|||
"styles": "highlighter.less",
|
||||
"dependencies": [
|
||||
"ext.discussionTools.init"
|
||||
],
|
||||
"targets": [
|
||||
"desktop",
|
||||
"mobile"
|
||||
]
|
||||
},
|
||||
"ext.discussionTools.ReplyWidget": {
|
||||
|
@ -185,7 +192,11 @@
|
|||
"optionalDependencies": {
|
||||
"ConfirmEdit": "ext.confirmEdit.CaptchaInputWidget"
|
||||
},
|
||||
"factory": "\\MediaWiki\\Extension\\DiscussionTools\\Data::addOptionalDependencies"
|
||||
"factory": "\\MediaWiki\\Extension\\DiscussionTools\\Data::addOptionalDependencies",
|
||||
"targets": [
|
||||
"desktop",
|
||||
"mobile"
|
||||
]
|
||||
},
|
||||
"ext.discussionTools.ReplyWidgetPlain": {
|
||||
"packageFiles": [
|
||||
|
@ -195,7 +206,12 @@
|
|||
"ext.discussionTools.ReplyWidget",
|
||||
"mediawiki.editfont.styles",
|
||||
"mediawiki.user",
|
||||
"mediawiki.jqueryMsg"
|
||||
"mediawiki.jqueryMsg",
|
||||
"ext.visualEditor.switching"
|
||||
],
|
||||
"targets": [
|
||||
"desktop",
|
||||
"mobile"
|
||||
]
|
||||
},
|
||||
"ext.discussionTools.ReplyWidgetVisual": {
|
||||
|
@ -224,9 +240,11 @@
|
|||
"ext.visualEditor.mwcore",
|
||||
"ext.visualEditor.mwsignature",
|
||||
"ext.visualEditor.mwwikitext",
|
||||
"ext.visualEditor.core.desktop",
|
||||
"ext.visualEditor.desktopTarget",
|
||||
"ext.visualEditor.mwextensions.desktop"
|
||||
"ext.visualEditor.articleTarget"
|
||||
],
|
||||
"targets": [
|
||||
"desktop",
|
||||
"mobile"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -489,6 +507,10 @@
|
|||
"value": false,
|
||||
"description": "A/B test DiscussionTools features for logged in users. false, 'replytool', 'newtopictool', or 'all'"
|
||||
},
|
||||
"DiscussionToolsEnableMobile": {
|
||||
"value": false,
|
||||
"description": "Enable DiscussionTools on mobile talk pages."
|
||||
},
|
||||
"DiscussionTools_replytool": {
|
||||
"value": "default",
|
||||
"description": "Override availability of DiscussionTools reply tool. 'default', 'available', or 'unavailable'."
|
||||
|
|
|
@ -211,12 +211,21 @@ class HookUtils {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Don't show on mobile
|
||||
$isMobile = false;
|
||||
if ( ExtensionRegistry::getInstance()->isLoaded( 'MobileFrontend' ) ) {
|
||||
$mobFrontContext = MediaWikiServices::getInstance()->getService( 'MobileFrontend.Context' );
|
||||
if ( $mobFrontContext->shouldDisplayMobileView() ) {
|
||||
return false;
|
||||
}
|
||||
$isMobile = $mobFrontContext->shouldDisplayMobileView();
|
||||
}
|
||||
|
||||
$dtConfig = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'discussiontools' );
|
||||
|
||||
if ( $isMobile && (
|
||||
!$dtConfig->get( 'DiscussionToolsEnableMobile' ) ||
|
||||
// Still disable some features for now
|
||||
$feature === self::TOPICSUBSCRIPTION ||
|
||||
$feature === self::NEWTOPICTOOL
|
||||
) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Topic subscription is not available on your own talk page, as you will
|
||||
|
@ -230,7 +239,6 @@ class HookUtils {
|
|||
// Extra hack for parses from API, where this parameter isn't passed to derivative requests
|
||||
RequestContext::getMain()->getRequest()->getRawVal( 'dtenable' );
|
||||
|
||||
$dtConfig = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'discussiontools' );
|
||||
if (
|
||||
$feature === self::TOPICSUBSCRIPTION &&
|
||||
!$dtConfig->get( 'DiscussionToolsEnableTopicSubscriptionBackend' )
|
||||
|
|
|
@ -12,6 +12,19 @@ var
|
|||
.concat( conf.pluginModules.filter( mw.loader.getState ) ),
|
||||
plainModules = [ 'ext.discussionTools.ReplyWidgetPlain' ];
|
||||
|
||||
if ( OO.ui.isMobile() ) {
|
||||
visualModules = [
|
||||
'ext.visualEditor.core.mobile',
|
||||
'ext.visualEditor.mwextensions'
|
||||
].concat( visualModules );
|
||||
} else {
|
||||
visualModules = [
|
||||
'ext.visualEditor.core.desktop',
|
||||
'ext.visualEditor.desktopTarget',
|
||||
'ext.visualEditor.mwextensions.desktop'
|
||||
].concat( visualModules );
|
||||
}
|
||||
|
||||
// Start loading reply widget code
|
||||
if ( defaultVisual || enable2017Wikitext ) {
|
||||
mw.loader.using( visualModules );
|
||||
|
|
|
@ -389,6 +389,9 @@ function init( $container, state ) {
|
|||
threadItemsById = {},
|
||||
threadItems = [];
|
||||
|
||||
// Lazy-load postEdit module, may be required later (on desktop)
|
||||
mw.loader.using( 'mediawiki.action.view.postEdit' );
|
||||
|
||||
$pageContainer = $container;
|
||||
linksController = new ReplyLinksController( $pageContainer );
|
||||
var parser = new Parser( $pageContainer[ 0 ] );
|
||||
|
@ -549,9 +552,14 @@ function init( $container, state ) {
|
|||
var repliedToComment = threadItemsById[ state.repliedTo ];
|
||||
$highlight = highlight( repliedToComment.replies[ repliedToComment.replies.length - 1 ] );
|
||||
|
||||
mw.hook( 'postEdit' ).fire( {
|
||||
message: mw.msg( 'discussiontools-postedit-confirmation-published', mw.user )
|
||||
} );
|
||||
if ( OO.ui.isMobile() ) {
|
||||
mw.notify( mw.msg( 'discussiontools-postedit-confirmation-published', mw.user ) );
|
||||
} else {
|
||||
// postEdit is currently desktop only
|
||||
mw.hook( 'postEdit' ).fire( {
|
||||
message: mw.msg( 'discussiontools-postedit-confirmation-published', mw.user )
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $highlight ) {
|
||||
|
|
|
@ -30,21 +30,57 @@ CommentTarget.static.name = 'discussionTools';
|
|||
|
||||
CommentTarget.static.modes = [ 'visual', 'source' ];
|
||||
|
||||
CommentTarget.static.toolbarGroups = [
|
||||
{
|
||||
name: 'style',
|
||||
title: OO.ui.deferMsg( 'visualeditor-toolbar-style-tooltip' ),
|
||||
include: [ 'bold', 'italic', 'moreTextStyle' ]
|
||||
},
|
||||
{
|
||||
name: 'link',
|
||||
include: [ 'link' ]
|
||||
},
|
||||
{
|
||||
name: 'other',
|
||||
include: [ 'usernameCompletion' ]
|
||||
}
|
||||
];
|
||||
if ( OO.ui.isMobile() ) {
|
||||
// Mobile currently expects one tool per group
|
||||
CommentTarget.static.toolbarGroups = [
|
||||
{
|
||||
name: 'style',
|
||||
classes: [ 've-test-toolbar-style' ],
|
||||
type: 'list',
|
||||
icon: 'textStyle',
|
||||
title: OO.ui.deferMsg( 'visualeditor-toolbar-style-tooltip' ),
|
||||
label: OO.ui.deferMsg( 'visualeditor-toolbar-style-tooltip' ),
|
||||
invisibleLabel: true,
|
||||
include: [ { group: 'textStyle' }, 'language', 'clear' ],
|
||||
forceExpand: [ 'bold', 'italic', 'clear' ],
|
||||
promote: [ 'bold', 'italic' ],
|
||||
demote: [ 'strikethrough', 'code', 'underline', 'language', 'clear' ]
|
||||
},
|
||||
{
|
||||
name: 'link',
|
||||
include: [ 'link' ]
|
||||
},
|
||||
{
|
||||
name: 'other',
|
||||
include: [ 'usernameCompletion' ]
|
||||
},
|
||||
{
|
||||
name: 'editMode',
|
||||
type: 'list',
|
||||
icon: 'edit',
|
||||
title: OO.ui.deferMsg( 'visualeditor-mweditmode-tooltip' ),
|
||||
label: OO.ui.deferMsg( 'visualeditor-mweditmode-tooltip' ),
|
||||
invisibleLabel: true,
|
||||
include: [ 'editModeVisual', 'editModeSource' ]
|
||||
}
|
||||
];
|
||||
} else {
|
||||
CommentTarget.static.toolbarGroups = [
|
||||
{
|
||||
name: 'style',
|
||||
title: OO.ui.deferMsg( 'visualeditor-toolbar-style-tooltip' ),
|
||||
include: [ 'bold', 'italic', 'moreTextStyle' ]
|
||||
},
|
||||
{
|
||||
name: 'link',
|
||||
include: [ 'link' ]
|
||||
},
|
||||
{
|
||||
name: 'other',
|
||||
include: [ 'usernameCompletion' ]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
CommentTarget.static.importRules = ve.copy( CommentTarget.static.importRules );
|
||||
|
||||
|
@ -73,7 +109,7 @@ CommentTarget.static.importRules.external.blacklist = ve.extendObject(
|
|||
|
||||
CommentTarget.prototype.attachToolbar = function () {
|
||||
this.replyWidget.$headerWrapper.append(
|
||||
this.getToolbar().$element.append( this.replyWidget.modeTabSelect.$element )
|
||||
this.getToolbar().$element.append( this.replyWidget.modeTabSelect ? this.replyWidget.modeTabSelect.$element : null )
|
||||
);
|
||||
this.getToolbar().$element.prepend( this.getSurface().getToolbarDialogs().$element );
|
||||
};
|
||||
|
@ -89,6 +125,14 @@ CommentTarget.prototype.getSurfaceConfig = function ( config ) {
|
|||
}, config ) );
|
||||
};
|
||||
|
||||
CommentTarget.prototype.editSource = function () {
|
||||
this.replyWidget.switch( 'source' );
|
||||
};
|
||||
|
||||
CommentTarget.prototype.switchToVisualEditor = function () {
|
||||
this.replyWidget.switch( 'visual' );
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.init.mw.targetFactory.register( CommentTarget );
|
||||
|
|
|
@ -33,6 +33,12 @@ span[ data-mw-comment-start ] {
|
|||
}
|
||||
}
|
||||
|
||||
// No support for reply links in the mobile talk overlay
|
||||
// stylelint-disable-next-line no-descending-specificity, selector-class-pattern
|
||||
.talk-overlay & {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ext-discussiontools-init-replylink {
|
||||
&-reply {
|
||||
cursor: pointer;
|
||||
|
|
|
@ -84,30 +84,35 @@ function ReplyWidget( commentController, comment, commentDetails, config ) {
|
|||
)
|
||||
} );
|
||||
|
||||
this.modeTabSelect = new ModeTabSelectWidget( {
|
||||
classes: [ 'ext-discussiontools-ui-replyWidget-modeTabs' ],
|
||||
items: [
|
||||
new ModeTabOptionWidget( {
|
||||
label: mw.msg( 'discussiontools-replywidget-mode-visual' ),
|
||||
data: 'visual'
|
||||
} ),
|
||||
new ModeTabOptionWidget( {
|
||||
label: mw.msg( 'discussiontools-replywidget-mode-source' ),
|
||||
data: 'source'
|
||||
} )
|
||||
],
|
||||
framed: false
|
||||
} );
|
||||
this.modeTabSelect.$element.attr( 'aria-label', mw.msg( 'visualeditor-mweditmode-tooltip' ) );
|
||||
// Make the option for the current mode disabled, to make it un-interactable
|
||||
// (we override the styles to make it look as if it was selected)
|
||||
this.modeTabSelect.findItemFromData( this.getMode() ).setDisabled( true );
|
||||
|
||||
this.$headerWrapper = $( '<div>' ).addClass( 'ext-discussiontools-ui-replyWidget-headerWrapper' );
|
||||
this.$headerWrapper.append(
|
||||
// Visual mode toolbar attached here by CommentTarget#attachToolbar
|
||||
this.modeTabSelect.$element
|
||||
);
|
||||
|
||||
if ( !OO.ui.isMobile() ) {
|
||||
this.modeTabSelect = new ModeTabSelectWidget( {
|
||||
classes: [ 'ext-discussiontools-ui-replyWidget-modeTabs' ],
|
||||
items: [
|
||||
new ModeTabOptionWidget( {
|
||||
label: mw.msg( 'discussiontools-replywidget-mode-visual' ),
|
||||
data: 'visual'
|
||||
} ),
|
||||
new ModeTabOptionWidget( {
|
||||
label: mw.msg( 'discussiontools-replywidget-mode-source' ),
|
||||
data: 'source'
|
||||
} )
|
||||
],
|
||||
framed: false
|
||||
} );
|
||||
this.modeTabSelect.$element.attr( 'aria-label', mw.msg( 'visualeditor-mweditmode-tooltip' ) );
|
||||
// Make the option for the current mode disabled, to make it un-interactable
|
||||
// (we override the styles to make it look as if it was selected)
|
||||
this.modeTabSelect.findItemFromData( this.getMode() ).setDisabled( true );
|
||||
this.modeTabSelect.connect( this, {
|
||||
choose: 'onModeTabSelectChoose'
|
||||
} );
|
||||
this.$headerWrapper.append(
|
||||
// Visual mode toolbar attached here by CommentTarget#attachToolbar
|
||||
this.modeTabSelect.$element
|
||||
);
|
||||
}
|
||||
|
||||
this.$preview = $( '<div>' )
|
||||
.addClass( 'ext-discussiontools-ui-replyWidget-preview' )
|
||||
|
@ -195,9 +200,6 @@ function ReplyWidget( commentController, comment, commentDetails, config ) {
|
|||
this.beforeUnloadHandler = this.onBeforeUnload.bind( this );
|
||||
this.unloadHandler = this.onUnload.bind( this );
|
||||
this.onWatchToggleHandler = this.onWatchToggle.bind( this );
|
||||
this.modeTabSelect.connect( this, {
|
||||
choose: 'onModeTabSelectChoose'
|
||||
} );
|
||||
this.advancedToggle.connect( this, { click: 'onAdvancedToggleClick' } );
|
||||
this.editSummaryInput.connect( this, { change: 'onEditSummaryChange' } );
|
||||
this.editSummaryInput.$input.on( 'keydown', this.onKeyDown.bind( this, false ) );
|
||||
|
@ -503,6 +505,11 @@ ReplyWidget.prototype.setup = function ( data ) {
|
|||
|
||||
if ( this.isNewTopic ) {
|
||||
this.commentController.sectionTitle.connect( this, { change: 'onInputChangeThrottled' } );
|
||||
} else {
|
||||
// De-indent replies on mobile
|
||||
if ( OO.ui.isMobile() ) {
|
||||
this.$element.css( 'margin-left', -this.$element.position().left );
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-jquery/no-global-selector
|
||||
|
@ -581,7 +588,9 @@ ReplyWidget.prototype.teardown = function ( abandoned ) {
|
|||
}
|
||||
// Make sure that the selector is blurred before it gets removed from the document, otherwise
|
||||
// event handlers for arrow keys are not removed, and it keeps trying to switch modes (T274423)
|
||||
this.modeTabSelect.blur();
|
||||
if ( this.modeTabSelect ) {
|
||||
this.modeTabSelect.blur();
|
||||
}
|
||||
this.unbindBeforeUnloadHandler();
|
||||
// eslint-disable-next-line no-jquery/no-global-selector
|
||||
$( '#ca-watch, #ca-unwatch' ).off( 'watchpage.mw', this.onWatchToggleHandler );
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
@width-breakpoint-tablet: 720px;
|
||||
|
||||
.ext-discussiontools-ui-replyWidget {
|
||||
margin-bottom: 1em;
|
||||
position: relative;
|
||||
|
@ -37,6 +39,8 @@
|
|||
background: none;
|
||||
box-shadow: none;
|
||||
border: 0;
|
||||
// Stretch to all available space
|
||||
flex-grow: 1;
|
||||
|
||||
> .oo-ui-toolbar-actions {
|
||||
display: none;
|
||||
|
@ -63,12 +67,19 @@
|
|||
}
|
||||
}
|
||||
|
||||
.skin-minerva & .ve-ui-surface-visual .ve-ce-paragraphNode {
|
||||
// Reduce paragraph spacing in editor, as replies will actually generate <dd> not <p>
|
||||
margin: 0;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
&-modeTabs {
|
||||
box-shadow: none;
|
||||
height: 3em;
|
||||
text-align: right;
|
||||
// Stretch to all available space
|
||||
flex-grow: 1;
|
||||
// Hide outline that can appear after switching modes via keyboard
|
||||
outline: 0;
|
||||
|
||||
|
@ -91,6 +102,19 @@
|
|||
}
|
||||
}
|
||||
|
||||
&-editSwitch {
|
||||
text-align: right;
|
||||
|
||||
.oo-ui-toolbar-bar { /* stylelint-disable-line no-descending-specificity */
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.oo-ui-toolbar-popups {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
&-actionsWrapper {
|
||||
margin-top: 0.5em;
|
||||
display: flex;
|
||||
|
@ -164,6 +188,10 @@
|
|||
|
||||
.ext-discussiontools-ui-replyWidget:not( .ext-discussiontools-ui-replyWidget-newTopic ) & > .mw-parser-output {
|
||||
margin-left: -1.6em;
|
||||
|
||||
.skin-minerva & {
|
||||
margin-left: -1em;
|
||||
}
|
||||
}
|
||||
|
||||
> .mw-parser-output > h2:first-child {
|
||||
|
@ -244,6 +272,23 @@
|
|||
// Force to a separate line
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// Change field layout and order on mobile
|
||||
@media all and ( max-width: @width-breakpoint-tablet ) {
|
||||
.oo-ui-fieldLayout-header {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.oo-ui-fieldLayout-field {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.ext-discussiontools-ui-replyWidget-checkboxes {
|
||||
order: 3;
|
||||
flex-grow: 1;
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-editSummary {
|
||||
|
@ -267,3 +312,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.skin-minerva .ext-discussiontools-init-replylink-open dl dd {
|
||||
// Disable overflow hack for scrolling images in <dd> in Minerva
|
||||
// while the tool is open, as we need negative margins on the widget.
|
||||
overflow: visible;
|
||||
}
|
||||
|
|
|
@ -12,9 +12,40 @@ var utils = require( 'ext.discussionTools.init' ).utils;
|
|||
* @param {Object} [config]
|
||||
*/
|
||||
function ReplyWidgetPlain() {
|
||||
var widget = this;
|
||||
|
||||
// Parent constructor
|
||||
ReplyWidgetPlain.super.apply( this, arguments );
|
||||
|
||||
if ( OO.ui.isMobile() ) {
|
||||
var toolFactory = new OO.ui.ToolFactory(),
|
||||
toolGroupFactory = new OO.ui.ToolGroupFactory();
|
||||
|
||||
toolFactory.register( mw.libs.ve.MWEditModeVisualTool );
|
||||
toolFactory.register( mw.libs.ve.MWEditModeSourceTool );
|
||||
this.switchToolbar = new OO.ui.Toolbar( toolFactory, toolGroupFactory, {
|
||||
classes: [ 'ext-discussiontools-ui-replyWidget-editSwitch' ]
|
||||
} );
|
||||
|
||||
this.switchToolbar.on( 'switchEditor', function ( mode ) {
|
||||
widget.switch( mode );
|
||||
} );
|
||||
|
||||
this.switchToolbar.setup( [ {
|
||||
name: 'editMode',
|
||||
type: 'list',
|
||||
icon: 'edit',
|
||||
title: mw.msg( 'visualeditor-mweditmode-tooltip' ),
|
||||
label: mw.msg( 'visualeditor-mweditmode-tooltip' ),
|
||||
invisibleLabel: true,
|
||||
include: [ 'editModeVisual', 'editModeSource' ]
|
||||
} ] );
|
||||
|
||||
this.switchToolbar.emit( 'updateState' );
|
||||
|
||||
this.$headerWrapper.append( this.switchToolbar.$element );
|
||||
}
|
||||
|
||||
this.$element.addClass( 'ext-discussiontools-ui-replyWidget-plain' );
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue