Realtime Preview: display manual-reload bar when previews are slow

After three slow preview requests, switch to showing a bar at the
top of the preview area that contains a manual 'reload' button.

The manual bar will be hidden when an error message is show, but
re-shown again after the error is dismissed. Closing and
re-opening the preview pane doesn't reset the manual mode.

Bug: T304568
Change-Id: Ia72bd1ceab68fdaed5de53137bd8ac5961db4714
This commit is contained in:
Sam Wilson 2022-04-06 10:48:31 +08:00
parent 94d4912103
commit 4c760f8634
8 changed files with 114 additions and 17 deletions

View file

@ -323,20 +323,23 @@
"wikieditor-realtimepreview-error",
"wikieditor-realtimepreview-reload",
"wikieditor-realtimepreview-reload-title",
"accesskey-wikieditor-realtimepreview"
"accesskey-wikieditor-realtimepreview",
"wikieditor-realtimepreview-manual"
],
"packageFiles": [
"realtimepreview/init.js",
"realtimepreview/RealtimePreview.js",
"realtimepreview/ResizingDragBar.js",
"realtimepreview/TwoPaneLayout.js",
"realtimepreview/ErrorLayout.js"
"realtimepreview/ErrorLayout.js",
"realtimepreview/ManualWidget.js"
],
"styles": [
"realtimepreview/RealtimePreview.less",
"realtimepreview/ResizingDragBar.less",
"realtimepreview/TwoPaneLayout.less",
"realtimepreview/ErrorLayout.less"
"realtimepreview/ErrorLayout.less",
"realtimepreview/ManualWidget.less"
]
}
},

View file

@ -204,5 +204,6 @@
"wikieditor-realtimepreview-beta-label": "Realtime Preview",
"wikieditor-realtimepreview-beta-desc": "See how wikitext changes will appear to readers inside the [[mw:Special:MyLanguage/Extension:WikiEditor|2010 editor]].",
"wikieditor-realtimepreview-reload-title": "Reload the realtime preview pane",
"accesskey-wikieditor-realtimepreview": ")"
"accesskey-wikieditor-realtimepreview": ")",
"wikieditor-realtimepreview-manual": "Please reload now to manually preview your edits."
}

View file

@ -231,9 +231,10 @@
"tag-wikieditor-description": "Long description of the wikieditor tag ({{msg-mw|Tag-wikieditor}}).\n\nShown on [[Special:Tags]].\n\nSee also:\n* {{msg-mw|Tag-wikieditor}}",
"wikieditor-realtimepreview-preview": "Label for the toolbar button to enable/disable realtime preview.",
"wikieditor-realtimepreview-error": "Header text for realtime preview errors.",
"wikieditor-realtimepreview-reload": "Button text for the 'reload' button on realtime preview errors.",
"wikieditor-realtimepreview-reload": "Button text for the realtime preview 'reload' buttons (the hover button, the manual-reload button, and the button shown in error messages).",
"wikieditor-realtimepreview-beta-label": "Used in [[Special:Preferences]].\n\nUsed as label for checkbox to enable Realtime Preview as a Beta Feature.\n\nThe description for this checkbox is: {{msg-mw|wikieditor-realtimepreview-beta-desc}}.",
"wikieditor-realtimepreview-beta-desc": "Used in [[Special:Preferences]].\n\nUsed as description for the checkbox to enable Realtime Preview as a Beta Feature.\n\nThe label for this checkbox is {{msg-mw|wikieditor-realtimepreview-beta-label}}.",
"wikieditor-realtimepreview-reload-title": "Tooltip text for the manual reload button.",
"accesskey-wikieditor-realtimepreview": "{{doc-accesskey}}\nAccesskey for the manual reload button."
"accesskey-wikieditor-realtimepreview": "{{doc-accesskey}}\nAccesskey for the manual reload button.",
"wikieditor-realtimepreview-manual": "Message displayed when realtime preview is in 'manual' mode."
}

View file

@ -0,0 +1,43 @@
/* global RealtimePreview */
/**
* @class
* @constructor
* @param {RealtimePreview} realtimePreview
* @param {OO.ui.ButtonWidget} reloadHoverButton
*/
function ManualWidget( realtimePreview, reloadHoverButton ) {
var config = {
classes: [ 'ext-WikiEditor-ManualWidget' ],
framed: true
};
ManualWidget.super.call( this, config );
this.reloadHoverButton = reloadHoverButton;
// UI elements.
var reloadIcon = new OO.ui.IconWidget( { icon: 'reload' } );
var $reloadLabel = $( '<span>' )
.text( mw.msg( 'wikieditor-realtimepreview-manual' ) );
this.reloadButton = new OO.ui.ButtonWidget( {
label: mw.msg( 'wikieditor-realtimepreview-reload' ),
framed: false,
flags: [ 'progressive' ]
} );
this.reloadButton.connect( realtimePreview, {
click: realtimePreview.doRealtimePreview.bind( realtimePreview )
} );
this.$element.append( reloadIcon.$element, $reloadLabel, this.reloadButton.$element );
}
OO.inheritClass( ManualWidget, OO.ui.Widget );
ManualWidget.prototype.toggle = function ( show ) {
ManualWidget.parent.prototype.toggle.call( this, show );
if ( show ) {
this.reloadHoverButton.$element.remove();
// Use the same access key as the hover reload button, because this won't ever be displayed at the same time as that.
this.reloadButton.setAccessKey( mw.msg( 'accesskey-wikieditor-realtimepreview' ) );
}
};
module.exports = ManualWidget;

View file

@ -0,0 +1,21 @@
@import 'mediawiki.ui/variables';
.ext-WikiEditor-ManualWidget {
background-color: @colorGray14;
display: flex;
align-items: center;
justify-content: space-between;
border-color: @colorGray12;
border-width: 1px 0;
border-style: solid;
padding: 6px 24px;
& > .oo-ui-iconWidget {
margin-right: 1em;
}
.oo-ui-buttonWidget {
margin-left: auto;
font-weight: bolder;
}
}

View file

@ -1,6 +1,7 @@
var ResizingDragBar = require( './ResizingDragBar.js' );
var TwoPaneLayout = require( './TwoPaneLayout.js' );
var ErrorLayout = require( './ErrorLayout.js' );
var ManualWidget = require( './ManualWidget.js' );
/**
* @class
@ -21,13 +22,19 @@ function RealtimePreview() {
.append( $previewContent );
// Loading bar.
this.$loadingBar = $( '<div>' ).addClass( 'ext-WikiEditor-realtimepreview-loadingbar' );
this.$loadingBar = $( '<div>' ).addClass( 'ext-WikiEditor-realtimepreview-loadingbar' ).append( '<div>' );
this.$loadingBar.hide();
// Error layout.
this.errorLayout = new ErrorLayout();
this.errorLayout.getReloadButton().connect( this, {
click: this.doRealtimePreview.bind( this )
click: function () {
// Re-show the manual message after the error message is closed.
if ( this.inManualMode ) {
this.manualWidget.toggle( true );
}
this.doRealtimePreview();
}.bind( this )
} );
// Manual reload button (visible on hover).
@ -47,7 +54,11 @@ function RealtimePreview() {
}.bind( this )
} );
this.twoPaneLayout.getPane2().append( reloadButton.$element, this.$loadingBar, this.$previewNode, this.errorLayout.$element );
// Manual mode widget.
this.manualWidget = new ManualWidget( this, reloadButton );
this.inManualMode = false;
this.twoPaneLayout.getPane2().append( this.manualWidget.$element, reloadButton.$element, this.$loadingBar, this.$previewNode, this.errorLayout.$element );
this.eventNames = 'change.realtimepreview input.realtimepreview cut.realtimepreview paste.realtimepreview';
// Used to ensure we wait for a response before making new requests.
this.isPreviewing = false;
@ -146,6 +157,9 @@ RealtimePreview.prototype.toggle = function () {
.off( this.eventNames )
.on( this.eventNames, this.getEventHandler() );
// Hide or show the manual-reload message bar.
this.manualWidget.toggle( this.inManualMode );
// Let other things happen after enabling.
mw.hook( 'ext.WikiEditor.realtimepreview.enable' ).fire( this );
}
@ -162,7 +176,12 @@ RealtimePreview.prototype.toggle = function () {
*/
RealtimePreview.prototype.getEventHandler = function () {
return mw.util.debounce(
this.doRealtimePreview.bind( this ),
function () {
// Only do preview if we're not in manual mode (as set in this.checkResponseTimes()).
if ( !this.inManualMode ) {
this.doRealtimePreview();
}
}.bind( this ),
this.configData.realtimeDebounce
);
};
@ -173,6 +192,7 @@ RealtimePreview.prototype.getEventHandler = function () {
*/
RealtimePreview.prototype.showError = function ( $msg ) {
this.$previewNode.hide();
this.manualWidget.toggle( false );
// There is no need for a default message because mw.Api.getErrorMessage() will
// always provide something (even for no network connection, server-side fatal errors, etc.).
this.errorLayout.setMessage( $msg );
@ -184,6 +204,11 @@ RealtimePreview.prototype.showError = function ( $msg ) {
* @param {number} time
*/
RealtimePreview.prototype.checkResponseTimes = function ( time ) {
// Don't track response times if we're already in manual mode.
if ( this.inManualMode ) {
return;
}
this.responseTimes.push( Date.now() - time );
if ( this.responseTimes.length < 3 ) {
return;
@ -194,10 +219,10 @@ RealtimePreview.prototype.checkResponseTimes = function ( time ) {
}, 0 );
if ( ( totalResponseTime / this.responseTimes.length ) > this.configData.realtimeDisableDuration ) {
// TODO: switch to the 'manual preview' workflow once designs/behaviour is finalized.
this.showError(
$( '<div>' ).text( '[PLACEHOLDER] Realtime preview is too slow' )
);
this.inManualMode = true;
// The error message might already be displayed if e.g. server timeout is greater than the disable-duration here.
this.errorLayout.toggle( false );
this.manualWidget.toggle( true );
}
this.responseTimes.shift();
@ -218,6 +243,7 @@ RealtimePreview.prototype.doRealtimePreview = function () {
this.$loadingBar.show();
var loadingSelectors = this.pagePreview.getLoadingSelectors();
loadingSelectors.push( '.ext-WikiEditor-realtimepreview-preview' );
loadingSelectors.push( '.ext-WikiEditor-ManualWidget' );
this.errorLayout.toggle( false );
var time = Date.now();

View file

@ -24,7 +24,7 @@
@loadingbar-width: 20%;
.ext-WikiEditor-realtimepreview-loadingbar {
.ext-WikiEditor-realtimepreview-loadingbar div {
position: absolute;
z-index: 5;
display: block;
@ -53,8 +53,8 @@
.ext-WikiEditor-twopanes-pane2 .ext-WikiEditor-reloadButton {
display: none;
position: absolute;
top: 12px;
right: 12px;
top: 8px;
right: 15px;
z-index: 5;
}

View file

@ -19,6 +19,8 @@
flex: 1 1 0;
overflow: hidden;
padding: 0;
display: flex;
flex-direction: column;
.ext-WikiEditor-realtimepreview-preview {
overflow: auto;