mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/DiscussionTools
synced 2024-11-27 17:51:09 +00:00
Merge "Add autosave support to visual mode"
This commit is contained in:
commit
d0dd4cf036
|
@ -18,6 +18,8 @@ if ( defaultVisual ) {
|
|||
}
|
||||
|
||||
function CommentController( $pageContainer, comment, thread ) {
|
||||
var mode;
|
||||
|
||||
this.$pageContainer = $pageContainer;
|
||||
this.comment = comment;
|
||||
this.thread = thread;
|
||||
|
@ -40,8 +42,9 @@ function CommentController( $pageContainer, comment, thread ) {
|
|||
this.$replyLinkButtons.append( this.$replyLink );
|
||||
modifier.addReplyLink( comment, this.$replyLinkButtons[ 0 ] );
|
||||
|
||||
if ( storage.get( 'reply/' + comment.id + '/body' ) ) {
|
||||
this.setup();
|
||||
if ( storage.get( 'reply/' + comment.id + '/saveable' ) ) {
|
||||
mode = storage.get( 'reply/' + comment.id + '/mode' );
|
||||
this.setup( mode );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,10 +117,19 @@ CommentController.prototype.onReplyLinkClick = function ( e ) {
|
|||
this.setup();
|
||||
};
|
||||
|
||||
CommentController.prototype.setup = function () {
|
||||
/**
|
||||
* Create and setup the reply widget
|
||||
*
|
||||
* @param {string} [mode] Optionally force a mode, 'visual' or 'source'
|
||||
*/
|
||||
CommentController.prototype.setup = function ( mode ) {
|
||||
var parsoidPromise,
|
||||
commentController = this;
|
||||
|
||||
if ( mode === undefined ) {
|
||||
mode = defaultVisual ? 'visual' : 'source';
|
||||
}
|
||||
|
||||
// TODO: Allow users to use multiple reply widgets simultaneously.
|
||||
// Currently submitting a reply from one widget would also destroy the other ones.
|
||||
// eslint-disable-next-line no-jquery/no-class-state
|
||||
|
@ -142,7 +154,7 @@ CommentController.prototype.setup = function () {
|
|||
mechanism: 'click',
|
||||
// TODO: Use 'wikitext-2017' when config.enable2017Wikitext is set
|
||||
// eslint-disable-next-line camelcase
|
||||
editor_interface: defaultVisual ? 'visual' : 'wikitext'
|
||||
editor_interface: mode === 'visual' ? 'visual' : 'wikitext'
|
||||
} );
|
||||
|
||||
this.$replyLinkButtons.addClass( 'dt-init-replylink-active' );
|
||||
|
@ -151,7 +163,7 @@ CommentController.prototype.setup = function () {
|
|||
parsoidPromise = getParsoidTranscludedCommentData( this.comment.id );
|
||||
|
||||
this.replyWidgetPromise = parsoidPromise.then( function ( parsoidData ) {
|
||||
return commentController.createReplyWidget( parsoidData );
|
||||
return commentController.createReplyWidget( parsoidData, mode === 'visual' );
|
||||
}, function ( code, data ) {
|
||||
commentController.teardown();
|
||||
|
||||
|
@ -182,7 +194,7 @@ CommentController.prototype.setup = function () {
|
|||
}
|
||||
$( commentController.newListItem ).empty().append( replyWidget.$element );
|
||||
|
||||
commentController.setupReplyWidget( replyWidget, true );
|
||||
commentController.setupReplyWidget( replyWidget, null, true );
|
||||
|
||||
logger( { action: 'ready' } );
|
||||
logger( { action: 'loaded' } );
|
||||
|
@ -215,10 +227,10 @@ CommentController.prototype.createReplyWidget = function ( parsoidData, visual )
|
|||
} );
|
||||
};
|
||||
|
||||
CommentController.prototype.setupReplyWidget = function ( replyWidget, scrollIntoView ) {
|
||||
CommentController.prototype.setupReplyWidget = function ( replyWidget, initialValue, scrollIntoView ) {
|
||||
replyWidget.connect( this, { teardown: 'teardown' } );
|
||||
|
||||
replyWidget.setup();
|
||||
replyWidget.setup( initialValue );
|
||||
if ( scrollIntoView ) {
|
||||
replyWidget.scrollElementIntoView( { padding: scrollPadding } );
|
||||
}
|
||||
|
@ -406,8 +418,7 @@ CommentController.prototype.switchToWikitext = function () {
|
|||
oldWidget.disconnect( commentController );
|
||||
oldWidget.teardown();
|
||||
|
||||
replyWidget.setValue( controller.sanitizeWikitextLinebreaks( wikitext ) );
|
||||
commentController.setupReplyWidget( replyWidget );
|
||||
commentController.setupReplyWidget( replyWidget, controller.sanitizeWikitextLinebreaks( wikitext ) );
|
||||
} );
|
||||
};
|
||||
|
||||
|
@ -454,8 +465,7 @@ CommentController.prototype.switchToVisual = function () {
|
|||
modifier.unwrapList( doc.body.children[ 0 ] );
|
||||
}
|
||||
|
||||
replyWidget.setValue( doc );
|
||||
commentController.setupReplyWidget( replyWidget );
|
||||
commentController.setupReplyWidget( replyWidget, doc );
|
||||
} );
|
||||
};
|
||||
|
||||
|
|
|
@ -250,12 +250,13 @@ function init( $container, state ) {
|
|||
pageThreads = parser.groupThreads( pageComments );
|
||||
pageCommentsById = commentsById( pageComments );
|
||||
|
||||
$pageContainer.removeClass( 'dt-init-replylink-open' );
|
||||
|
||||
pageThreads.forEach( function ( thread ) {
|
||||
traverseNode( thread, thread );
|
||||
} );
|
||||
|
||||
$pageContainer.addClass( 'dt-init-done' );
|
||||
$pageContainer.removeClass( 'dt-init-replylink-open' );
|
||||
|
||||
// For debugging
|
||||
mw.dt.pageThreads = pageThreads;
|
||||
|
|
|
@ -32,6 +32,7 @@ function ReplyWidget( commentController, parsoidData, config ) {
|
|||
this.context = contextNode ? contextNode.nodeName.toLowerCase() : 'dl';
|
||||
// TODO: Should storagePrefix include pageName?
|
||||
this.storagePrefix = 'reply/' + comment.id;
|
||||
this.storage = mw.storage.session;
|
||||
|
||||
inputConfig = $.extend(
|
||||
{ placeholder: mw.msg( 'discussiontools-replywidget-placeholder-reply', comment.author ) },
|
||||
|
@ -146,8 +147,6 @@ function ReplyWidget( commentController, parsoidData, config ) {
|
|||
widget.$actions.prepend( widget.$checkboxes );
|
||||
}
|
||||
} );
|
||||
|
||||
this.initAutoSave();
|
||||
}
|
||||
|
||||
/* Inheritance */
|
||||
|
@ -169,22 +168,10 @@ ReplyWidget.prototype.focus = null;
|
|||
|
||||
ReplyWidget.prototype.getValue = null;
|
||||
|
||||
/**
|
||||
* Set the reply widget's value
|
||||
*
|
||||
* @method
|
||||
* @chainable
|
||||
* @param {Mixed} value Value
|
||||
* @return {ReplyWidget}
|
||||
*/
|
||||
ReplyWidget.prototype.setValue = null;
|
||||
|
||||
ReplyWidget.prototype.isEmpty = null;
|
||||
|
||||
ReplyWidget.prototype.getMode = null;
|
||||
|
||||
ReplyWidget.prototype.initAutoSave = null;
|
||||
|
||||
ReplyWidget.prototype.clear = function () {
|
||||
if ( this.errorMessage ) {
|
||||
this.errorMessage.$element.remove();
|
||||
|
@ -199,6 +186,7 @@ ReplyWidget.prototype.setPending = function ( pending ) {
|
|||
} else {
|
||||
this.replyButton.setDisabled( false );
|
||||
this.cancelButton.setDisabled( false );
|
||||
this.updateButtons();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -238,6 +226,7 @@ ReplyWidget.prototype.onModeTabSelectChoose = function ( option ) {
|
|||
/**
|
||||
* Setup the widget
|
||||
*
|
||||
* @param {Mixed} initialValue Initial value
|
||||
* @chainable
|
||||
* @return {ReplyWidget}
|
||||
*/
|
||||
|
@ -247,9 +236,15 @@ ReplyWidget.prototype.setup = function () {
|
|||
this.modeTabSelect.selectItemByData( this.getMode() );
|
||||
this.saveEditMode( this.getMode() );
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
ReplyWidget.prototype.afterSetup = function () {
|
||||
// Init preview and button state
|
||||
this.onInputChange();
|
||||
return this;
|
||||
// Autosave
|
||||
this.storage.set( this.storagePrefix + '/mode', this.getMode() );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -298,6 +293,8 @@ ReplyWidget.prototype.tryTeardown = function () {
|
|||
ReplyWidget.prototype.teardown = function () {
|
||||
this.unbindBeforeUnloadHandler();
|
||||
this.clear();
|
||||
this.storage.remove( this.storagePrefix + '/mode' );
|
||||
this.storage.remove( this.storagePrefix + '/saveable' );
|
||||
this.$preview.empty();
|
||||
this.emit( 'teardown' );
|
||||
return this;
|
||||
|
@ -319,7 +316,8 @@ ReplyWidget.prototype.onInputChange = function () {
|
|||
ol: '#'
|
||||
}[ this.context ];
|
||||
|
||||
this.replyButton.setDisabled( this.isEmpty() );
|
||||
this.updateButtons();
|
||||
this.storage.set( this.storagePrefix + '/saveable', this.isEmpty() ? '' : '1' );
|
||||
|
||||
if ( this.getMode() !== 'source' ) {
|
||||
return;
|
||||
|
@ -361,6 +359,10 @@ ReplyWidget.prototype.onInputChange = function () {
|
|||
} );
|
||||
};
|
||||
|
||||
ReplyWidget.prototype.updateButtons = function () {
|
||||
this.replyButton.setDisabled( this.isEmpty() );
|
||||
};
|
||||
|
||||
ReplyWidget.prototype.onFirstTransaction = function () {
|
||||
logger( { action: 'firstChange' } );
|
||||
};
|
||||
|
|
|
@ -45,7 +45,7 @@ ReplyWidgetPlain.prototype.clear = function () {
|
|||
// Parent method
|
||||
ReplyWidgetPlain.super.prototype.clear.apply( this, arguments );
|
||||
|
||||
this.setValue( '' );
|
||||
this.replyBodyWidget.setValue( '' );
|
||||
};
|
||||
|
||||
ReplyWidgetPlain.prototype.isEmpty = function () {
|
||||
|
@ -56,15 +56,6 @@ ReplyWidgetPlain.prototype.getMode = function () {
|
|||
return 'source';
|
||||
};
|
||||
|
||||
ReplyWidgetPlain.prototype.initAutoSave = function () {
|
||||
var body;
|
||||
this.storage = mw.storage.session;
|
||||
this.storage.set( this.storagePrefix + '/class', 'ReplyWidgetPlain' );
|
||||
if ( ( body = this.storage.get( this.storagePrefix + '/body' ) ) ) {
|
||||
this.replyBodyWidget.setValue( body );
|
||||
}
|
||||
};
|
||||
|
||||
ReplyWidgetPlain.prototype.onInputChange = function () {
|
||||
var wikitext;
|
||||
|
||||
|
@ -75,7 +66,9 @@ ReplyWidgetPlain.prototype.onInputChange = function () {
|
|||
this.storage.set( this.storagePrefix + '/body', wikitext );
|
||||
};
|
||||
|
||||
ReplyWidgetPlain.prototype.setup = function () {
|
||||
ReplyWidgetPlain.prototype.setup = function ( initialValue ) {
|
||||
var autosaveValue = this.storage.get( this.storagePrefix + '/body' );
|
||||
|
||||
// Parent method
|
||||
ReplyWidgetPlain.super.prototype.setup.call( this );
|
||||
|
||||
|
@ -83,6 +76,10 @@ ReplyWidgetPlain.prototype.setup = function () {
|
|||
this.replyBodyWidget.connect( this, { change: this.onInputChangeThrottled } );
|
||||
this.replyBodyWidget.once( 'change', this.onFirstTransaction.bind( this ) );
|
||||
|
||||
this.replyBodyWidget.setValue( initialValue || autosaveValue );
|
||||
|
||||
this.afterSetup();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
@ -90,7 +87,6 @@ ReplyWidgetPlain.prototype.teardown = function () {
|
|||
this.replyBodyWidget.disconnect( this );
|
||||
this.replyBodyWidget.off( 'change' );
|
||||
|
||||
this.storage.remove( this.storagePrefix + '/class' );
|
||||
this.storage.remove( this.storagePrefix + '/body' );
|
||||
|
||||
// Parent method
|
||||
|
@ -124,9 +120,4 @@ ReplyWidgetPlain.prototype.getValue = function () {
|
|||
return this.replyBodyWidget.getValue();
|
||||
};
|
||||
|
||||
ReplyWidgetPlain.prototype.setValue = function ( value ) {
|
||||
this.replyBodyWidget.setValue( value );
|
||||
return this;
|
||||
};
|
||||
|
||||
module.exports = ReplyWidgetPlain;
|
||||
|
|
|
@ -44,17 +44,6 @@ ReplyWidgetVisual.prototype.getValue = function () {
|
|||
}
|
||||
};
|
||||
|
||||
ReplyWidgetVisual.prototype.setValue = function ( value ) {
|
||||
var target = this.replyBodyWidget.target;
|
||||
if ( target && target.getSurface() ) {
|
||||
target.setDocument( value );
|
||||
} else {
|
||||
// #setup hasn't been called yet, just save the value for when it is
|
||||
this.initialValue = value;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
ReplyWidgetVisual.prototype.clear = function () {
|
||||
// Parent method
|
||||
ReplyWidgetVisual.super.prototype.clear.apply( this, arguments );
|
||||
|
@ -73,13 +62,28 @@ ReplyWidgetVisual.prototype.getMode = function () {
|
|||
this.defaultMode;
|
||||
};
|
||||
|
||||
ReplyWidgetVisual.prototype.initAutoSave = function () {
|
||||
// TODO: Implement
|
||||
};
|
||||
ReplyWidgetVisual.prototype.setup = function ( initialValue ) {
|
||||
var htmlOrDoc,
|
||||
widget = this,
|
||||
target = this.replyBodyWidget.target;
|
||||
|
||||
ReplyWidgetVisual.prototype.setup = function () {
|
||||
this.replyBodyWidget.setDocument( this.initialValue || '<p></p>' );
|
||||
this.initialValue = null;
|
||||
if ( this.storage.get( this.storagePrefix + '/saveable' ) ) {
|
||||
htmlOrDoc = this.storage.get( this.storagePrefix + '/ve-dochtml' );
|
||||
target.recovered = true;
|
||||
} else {
|
||||
htmlOrDoc = initialValue || '<p></p>';
|
||||
}
|
||||
|
||||
target.originalHtml = htmlOrDoc instanceof HTMLDocument ? ve.properInnerHtml( htmlOrDoc.body ) : htmlOrDoc;
|
||||
target.fromEditedState = !!initialValue;
|
||||
|
||||
this.replyBodyWidget.setDocument( htmlOrDoc );
|
||||
|
||||
target.once( 'surfaceReady', function () {
|
||||
target.getSurface().getModel().setAutosaveDocId( widget.storagePrefix );
|
||||
target.initAutosave();
|
||||
widget.afterSetup();
|
||||
} );
|
||||
|
||||
// Parent method
|
||||
ReplyWidgetVisual.super.prototype.setup.call( this );
|
||||
|
@ -97,6 +101,8 @@ ReplyWidgetVisual.prototype.setup = function () {
|
|||
ReplyWidgetVisual.prototype.teardown = function () {
|
||||
this.replyBodyWidget.disconnect( this );
|
||||
this.replyBodyWidget.off( 'change' );
|
||||
// TODO: Just teardown the whole target?
|
||||
this.replyBodyWidget.target.clearDocState();
|
||||
|
||||
// Parent method
|
||||
return ReplyWidgetVisual.super.prototype.teardown.call( this );
|
||||
|
|
Loading…
Reference in a new issue