From 7ea6cdf326c6de2bdbae1051d8c1362a4660c8cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Dziewo=C5=84ski?= Date: Sat, 15 Feb 2020 05:31:06 +0100 Subject: [PATCH] Try to resolve edit conflicts The most common case of edit conflicts on talk pages is several people responding to the same comment at the same time.[citation needed] We can easily resolve this case by fetching the latest revision of the page and re-running our code to insert a reply on it. When we can't insert a reply, that probably means the parent comment was deleted or moved, so display an error message indicating that instead of the generic one. Bug: T240643 Change-Id: Ic686acc747580d46779960211a02e9830a6ae86f --- extension.json | 1 + i18n/en.json | 1 + i18n/qqq.json | 1 + modules/controller.js | 5 ++++- modules/dt.ui.ReplyWidget.js | 26 +++++++++++++++++++++++++- 5 files changed, 32 insertions(+), 2 deletions(-) diff --git a/extension.json b/extension.json index 8a5ff5e77..babe7213d 100644 --- a/extension.json +++ b/extension.json @@ -58,6 +58,7 @@ "ext.discussionTools.modifier" ], "messages": [ + "discussiontools-error-comment-disappeared", "discussiontools-defaultsummary-reply", "discussiontools-replylink", "discussiontools-replywidget-loading" diff --git a/i18n/en.json b/i18n/en.json index 1e7b5737c..15e5405c9 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -12,6 +12,7 @@ "discussiontools-replywidget-preview": "Preview", "discussiontools-replywidget-reply": "Reply", "discussiontools-replywidget-terms-click": "By clicking \"$1\", you agree to the terms of use for this wiki.", + "discussiontools-error-comment-disappeared": "Could not find the comment you're replying to on the page. It might have been deleted or moved to another page. Please reload the page and try again.", "tag-discussiontools": "DiscussionTools edit", "tag-discussiontools-description": "Edit made using DiscussionTools", "tag-discussiontools-edit": "edited comment", diff --git a/i18n/qqq.json b/i18n/qqq.json index f8bfed346..e01760e5a 100644 --- a/i18n/qqq.json +++ b/i18n/qqq.json @@ -16,6 +16,7 @@ "discussiontools-replywidget-preview": "Label for the preview area of the reply widget", "discussiontools-replywidget-reply": "Label for the button to submit a reply in the reply widget", "discussiontools-replywidget-terms-click": "Terms of use for posting a reply.\n\n* $1 is the label of the button to be clicked, e.g. {{msg-mw|discussiontools-replywidget-reply}}.", + "discussiontools-error-comment-disappeared": "Error message.", "tag-discussiontools": "Short description of the discussiontools tag.\n\nShown on lists of changes (history, recentchanges, etc.) for each edit made using DiscussionTools.\n\nSee also:\n* {{msg-mw|Tag-discussiontools-description}}", "tag-discussiontools-description": "Long description of the discussiontools tag ({{msg-mw|Tag-discussiontools}}).\n\nShown on [[Special:Tags]].\n\nSee also:\n* {{msg-mw|Tag-discussiontools}}", "tag-discussiontools-edit": "Short description of the discussiontools-edit tag.\n\nShown on lists of changes (history, recentchanges, etc.) for each edit to an existing comment made using DiscussionTools.\n\nThis message is always shown directly after {{msg-mw|tag-discussiontools}} and a comma.\n\nSee also:\n* {{msg-mw|Tag-discussiontools-edit-description}}\n* {{Related|tag-discussiontools}}", diff --git a/modules/controller.js b/modules/controller.js index f456ac5dc..44e154040 100644 --- a/modules/controller.js +++ b/modules/controller.js @@ -212,7 +212,10 @@ function getParsoidCommentData( pageName, oldId, commentId ) { parsoidCommentsById = commentsById( parsoidComments ); if ( !parsoidCommentsById[ commentId ] ) { - throw new Error( 'Could not find comment in Parsoid HTML' ); + return $.Deferred().reject( 'comment-disappeared', { errors: [ { + code: 'comment-disappeared', + html: mw.message( 'discussiontools-error-comment-disappeared' ).parse() + } ] } ).promise(); } return { diff --git a/modules/dt.ui.ReplyWidget.js b/modules/dt.ui.ReplyWidget.js index 8c26aa791..c82fc7aa4 100644 --- a/modules/dt.ui.ReplyWidget.js +++ b/modules/dt.ui.ReplyWidget.js @@ -253,6 +253,30 @@ ReplyWidget.prototype.onReplyClick = function () { // 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 ); + } ).catch( function ( code, data ) { + // Handle edit conflicts. Load the latest revision of the page, then try again. If the parent + // comment has been deleted from the page, or if retry also fails for some other reason, the + // error is handled as normal below. + if ( code === 'editconflict' ) { + return widget.api.get( { + action: 'query', + prop: 'revisions', + rvprop: 'ids', + rvlimit: 1, + titles: mw.config.get( 'wgRelevantPageName' ), + formatversion: 2 + } ).then( function ( resp ) { + var latestRevId = resp.query.pages[ 0 ].revisions[ 0 ].revid; + mw.config.set( { + wgCurRevisionId: latestRevId, + wgRevisionId: latestRevId + } ); + return widget.getParsoidCommentData().then( function ( parsoidData ) { + return controller.postReply( widget, parsoidData ); + } ); + } ); + } + return $.Deferred().reject( code, data ).promise(); } ).then( function ( data ) { // eslint-disable-next-line no-jquery/no-global-selector var $container = $( '#mw-content-text' ); @@ -279,7 +303,7 @@ ReplyWidget.prototype.onReplyClick = function () { }, function ( code, data ) { widget.errorMessage = new OO.ui.MessageWidget( { type: 'error', - label: ( new mw.Api() ).getErrorMessage( data ) + label: widget.api.getErrorMessage( data ) } ); widget.errorMessage.$element.insertBefore( widget.replyBodyWidget.$element ); } ).always( function () {