mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/DiscussionTools
synced 2024-11-27 17:51:09 +00:00
Rebuild Parsoid document before attempting to save
Previously, we only built the Parsoid document once (on page load) and kept it around forever. Every time we tried to post a reply, it was added to this document, even if it wasn't saved due to some error. This resulted in duplicate replies when the user managed to actually save. Now we only keep around the HTML string and some metadata fetched from the API, and rebuild the actual document every time before adding a reply. Bug: T245333 Change-Id: Ib1c344a7d613cdf67644aa243147c5e699c2c1e7
This commit is contained in:
parent
80cddf549c
commit
6f404e5ce2
|
@ -3,6 +3,7 @@
|
|||
var
|
||||
parser = require( 'ext.discussionTools.parser' ),
|
||||
modifier = require( 'ext.discussionTools.modifier' ),
|
||||
pageDataCache = {},
|
||||
$pageContainer,
|
||||
scrollPadding = { top: 10, bottom: 10 },
|
||||
config = require( './config.json' ),
|
||||
|
@ -153,16 +154,79 @@ function commentsById( comments ) {
|
|||
return byId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Parsoid document HTML and metadata needed to edit this page from the API.
|
||||
*
|
||||
* This method caches responses. If you call it again with the same parameters, you'll get the exact
|
||||
* same Promise object, and no API request will be made.
|
||||
*
|
||||
* @param {string} pageName Page title
|
||||
* @param {number} oldId Revision ID
|
||||
* @return {jQuery.Promise}
|
||||
*/
|
||||
function getPageData( pageName, oldId ) {
|
||||
pageDataCache[ pageName ] = pageDataCache[ pageName ] || {};
|
||||
if ( pageDataCache[ pageName ][ oldId ] ) {
|
||||
return pageDataCache[ pageName ][ oldId ];
|
||||
}
|
||||
pageDataCache[ pageName ][ oldId ] = mw.loader.using( 'ext.visualEditor.targetLoader' ).then( function () {
|
||||
return mw.libs.ve.targetLoader.requestPageData(
|
||||
'visual', pageName, { oldId: oldId }
|
||||
);
|
||||
}, function () {
|
||||
// Clear on failure
|
||||
pageDataCache[ pageName ][ oldId ] = null;
|
||||
} );
|
||||
return pageDataCache[ pageName ][ oldId ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Parsoid document DOM, parse comments and threads, and find a specific comment in it.
|
||||
*
|
||||
* @param {string} pageName Page title
|
||||
* @param {number} oldId Revision ID
|
||||
* @param {string} commentId Comment ID, from a comment parsed in the local document
|
||||
* @return {jQuery.Promise}
|
||||
*/
|
||||
function getParsoidCommentData( pageName, oldId, commentId ) {
|
||||
var parsoidPageData, parsoidDoc, parsoidComments, parsoidCommentsById;
|
||||
|
||||
return getPageData( pageName, oldId )
|
||||
.then( function ( response ) {
|
||||
var data = response.visualeditor;
|
||||
// TODO: error handling
|
||||
parsoidDoc = ve.createDocumentFromHtml( data.content );
|
||||
parsoidComments = parser.getComments( parsoidDoc.body );
|
||||
|
||||
parsoidPageData = {
|
||||
pageName: pageName,
|
||||
oldId: oldId
|
||||
};
|
||||
parsoidPageData.baseTimeStamp = data.basetimestamp;
|
||||
parsoidPageData.startTimeStamp = data.starttimestamp;
|
||||
parsoidPageData.etag = data.etag;
|
||||
|
||||
// getThreads build the tree structure, currently only
|
||||
// used to set 'replies'
|
||||
parser.groupThreads( parsoidComments );
|
||||
parsoidCommentsById = commentsById( parsoidComments );
|
||||
|
||||
if ( !parsoidCommentsById[ commentId ] ) {
|
||||
throw new Error( 'Could not find comment in Parsoid HTML' );
|
||||
}
|
||||
|
||||
return {
|
||||
comment: parsoidCommentsById[ commentId ],
|
||||
doc: parsoidDoc,
|
||||
pageData: parsoidPageData
|
||||
};
|
||||
} );
|
||||
}
|
||||
|
||||
function init( $container, state ) {
|
||||
var
|
||||
parsoidPromise, parsoidDoc,
|
||||
parsoidComments, parsoidCommentsById,
|
||||
pageComments, pageThreads, pageCommentsById,
|
||||
repliedToComment,
|
||||
parsoidPageData = {
|
||||
pageName: mw.config.get( 'wgRelevantPageName' ),
|
||||
oldId: mw.config.get( 'wgRevisionId' )
|
||||
};
|
||||
repliedToComment;
|
||||
|
||||
state = state || {};
|
||||
$pageContainer = $container;
|
||||
|
@ -184,43 +248,17 @@ function init( $container, state ) {
|
|||
highlight( repliedToComment.replies[ repliedToComment.replies.length - 1 ] );
|
||||
}
|
||||
|
||||
parsoidPromise = mw.loader.using( 'ext.visualEditor.targetLoader' ).then( function () {
|
||||
return mw.libs.ve.targetLoader.requestPageData(
|
||||
'visual', parsoidPageData.pageName, { oldId: parsoidPageData.oldId }
|
||||
).then( function ( response ) {
|
||||
var data = response.visualeditor;
|
||||
// TODO: error handling
|
||||
parsoidDoc = ve.createDocumentFromHtml( data.content );
|
||||
parsoidComments = parser.getComments( parsoidDoc.body );
|
||||
|
||||
parsoidPageData.baseTimeStamp = data.basetimestamp;
|
||||
parsoidPageData.startTimeStamp = data.starttimestamp;
|
||||
parsoidPageData.etag = data.etag;
|
||||
|
||||
// getThreads build the tree structure, currently only
|
||||
// used to set 'replies'
|
||||
parser.groupThreads( parsoidComments );
|
||||
parsoidCommentsById = commentsById( parsoidComments );
|
||||
} );
|
||||
} );
|
||||
|
||||
// Map PHP comments to Parsoid comments.
|
||||
pageComments.forEach( function ( comment ) {
|
||||
comment.parsoidPromise = parsoidPromise.then( function () {
|
||||
if ( !parsoidCommentsById[ comment.id ] ) {
|
||||
throw new Error( 'Could not find comment in Parsoid HTML' );
|
||||
}
|
||||
return {
|
||||
comment: parsoidCommentsById[ comment.id ],
|
||||
doc: parsoidDoc,
|
||||
pageData: parsoidPageData
|
||||
};
|
||||
} );
|
||||
} );
|
||||
// Preload the Parsoid document.
|
||||
// TODO: Isn't this too early to load it? We will only need it if the user tries replying...
|
||||
getPageData(
|
||||
mw.config.get( 'wgRelevantPageName' ),
|
||||
mw.config.get( 'wgRevisionId' )
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: init,
|
||||
getParsoidCommentData: getParsoidCommentData,
|
||||
postReply: postReply,
|
||||
autoSignWikitext: autoSignWikitext
|
||||
};
|
||||
|
|
|
@ -233,9 +233,16 @@ ReplyWidget.prototype.onBeforeUnload = function ( e ) {
|
|||
}
|
||||
};
|
||||
|
||||
ReplyWidget.prototype.getParsoidCommentData = function () {
|
||||
return controller.getParsoidCommentData(
|
||||
mw.config.get( 'wgRelevantPageName' ),
|
||||
mw.config.get( 'wgRevisionId' ),
|
||||
this.comment.id
|
||||
);
|
||||
};
|
||||
|
||||
ReplyWidget.prototype.onReplyClick = function () {
|
||||
var repliedTo,
|
||||
widget = this;
|
||||
var widget = this;
|
||||
|
||||
if ( this.errorMessage ) {
|
||||
this.errorMessage.$element.remove();
|
||||
|
@ -243,8 +250,8 @@ ReplyWidget.prototype.onReplyClick = function () {
|
|||
|
||||
this.setPending( true );
|
||||
|
||||
this.comment.parsoidPromise.then( function ( parsoidData ) {
|
||||
repliedTo = parsoidData.comment.id;
|
||||
// We must get a new copy of the document every time, otherwise any unsaved replies will pile up
|
||||
this.getParsoidCommentData().then( function ( parsoidData ) {
|
||||
return controller.postReply( widget, parsoidData );
|
||||
} ).then( function ( data ) {
|
||||
// eslint-disable-next-line no-jquery/no-global-selector
|
||||
|
@ -266,7 +273,7 @@ ReplyWidget.prototype.onReplyClick = function () {
|
|||
|
||||
// Re-initialize
|
||||
controller.init( $container.find( '.mw-parser-output' ), {
|
||||
repliedTo: repliedTo
|
||||
repliedTo: widget.comment.id
|
||||
} );
|
||||
mw.hook( 'wikipage.content' ).fire( $container );
|
||||
}, function ( code, data ) {
|
||||
|
|
Loading…
Reference in a new issue