Skip to content

Commit 2cc855c

Browse files
committed
Allow suggestion editing
1 parent c2df5e1 commit 2cc855c

5 files changed

Lines changed: 144 additions & 4 deletions

File tree

commentManager.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,3 +207,52 @@ exports.changeCommentText = async (padId, commentId, commentText, authorId) => {
207207
// save the comment updated back
208208
await db.set(prefix + padId, comments);
209209
};
210+
211+
exports.changeComment = async (padId, commentId, commentText, changeFrom, changeTo, authorId, state) => { // eslint-disable max-len
212+
if (commentText.length <= 0) {
213+
logger.debug(`ignoring attempt to change comment ${commentId} to the empty string`);
214+
throw new Error('comment_cannot_be_empty');
215+
}
216+
217+
// Given a comment we update the comment text
218+
219+
// If we're dealing with comment replies we need to a different query
220+
let prefix = 'comments:';
221+
if (commentId.substring(0, 7) === 'c-reply') {
222+
prefix = 'comment-replies:';
223+
}
224+
225+
// get the entry
226+
const comments = await db.get(prefix + padId);
227+
if (comments == null || comments[commentId] == null) {
228+
logger.debug(`ignoring attempt to edit non-existent comment ${commentId}`);
229+
throw new Error('no_such_comment');
230+
}
231+
if (comments[commentId].author !== authorId) {
232+
logger.debug(`author ${authorId} attempted to edit comment ${commentId} ` +
233+
`belonging to author ${comments[commentId].author}`);
234+
throw new Error('unauth');
235+
}
236+
// update the comment text
237+
comments[commentId].text = commentText;
238+
if (changeTo) {
239+
comments[commentId].changeTo = changeTo;
240+
if (!comments[commentId].changeFrom) {
241+
comments[commentId].changeFrom = changeFrom;
242+
}
243+
}
244+
245+
if (comments[commentId].changeTo && !changeTo) {
246+
comments[commentId].changeTo = null;
247+
}
248+
249+
if (state) {
250+
comments[commentId].changeAccepted = true;
251+
comments[commentId].changeReverted = false;
252+
} else {
253+
comments[commentId].changeAccepted = false;
254+
comments[commentId].changeReverted = true;
255+
}
256+
// save the comment updated back
257+
await db.set(prefix + padId, comments);
258+
};

exportHTML.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ exports.getLineHTMLForExport = async (hookName, context) => {
3030
let hasPlugin = false;
3131
// Load the HTML into a throwaway div instead of calling $.load() to avoid
3232
// https://github.com/cheeriojs/cheerio/issues/1031
33-
//const content = $('<div>').html(context.lineContent);
33+
// const content = $('<div>').html(context.lineContent);
3434
// include links for each comment which we will add content later.
3535
content.find('span').each(function () {
3636
const span = $(this);

index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,13 @@ exports.socketio = (hookName, args, cb) => {
137137
socket.broadcast.to(padId).emit('textCommentUpdated', commentId, commentText);
138138
}));
139139

140+
socket.on('updateComment', handler(async (data) => {
141+
const {commentId, commentText, authorId, changeFrom, changeTo, changeAcceptedState} = data;
142+
const {padId} = await readOnlyManager.getIds(data.padId);
143+
await commentManager.changeComment(padId, commentId, commentText, changeFrom, changeTo, authorId, changeAcceptedState); // eslint-disable max-len
144+
socket.broadcast.to(padId).emit('commentUpdated', commentId, commentText, changeFrom, changeTo); // eslint-disable max-len
145+
}));
146+
140147
socket.on('addCommentReply', handler(async (data) => {
141148
const {padId} = await readOnlyManager.getIds(data.padId);
142149
const [replyId, reply] = await commentManager.addCommentReply(padId, data);

static/js/index.js

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,20 +166,46 @@ EpComments.prototype.init = async function () {
166166
// Here, it adds a form to edit the comment text
167167
this.container.parent().on('click', '.comment-edit', function () {
168168
const $commentBox = $(this).closest('.comment-container');
169-
$commentBox.addClass('editing');
170169

170+
const changeAcceptedState = $commentBox.hasClass('change-accepted');
171+
$commentBox.addClass('editing');
172+
const changeTo = $commentBox.find('.to-value').first().text();
173+
const changeFrom = $commentBox.find('.from-value').first().text();
171174
const textBox = self.findCommentText($commentBox).last();
175+
const commentId = $commentBox.data('commentid');
172176

173177
// if edit form not already there
174178
if (textBox.siblings('.comment-edit-form').length === 0) {
175179
// add a form to edit the field
176180
const data = {};
181+
data.changeAcceptedState = changeAcceptedState;
182+
data.commentId = commentId;
177183
data.text = textBox.text();
184+
data.changeFrom = changeFrom;
185+
data.changeTo = changeTo;
178186
const content = $('#editCommentTemplate').tmpl(data);
179187
// localize the comment/reply edit form
180188
commentL10n.localize(content);
181189
// insert form
182190
textBox.before(content);
191+
const editForm = textBox.parent().find('.comment-edit-form').first();
192+
if (changeTo) {
193+
editForm.find('.suggestion').show();
194+
editForm.find('.label-suggestion-checkbox')
195+
.siblings('input[type="checkbox"]')
196+
.prop('checked', true);
197+
}
198+
editForm.on('change', '.suggestion-checkbox', function () {
199+
if ($(this).is(':checked')) {
200+
editForm.find('.suggestion').show();
201+
} else {
202+
editForm.find('.suggestion').hide();
203+
}
204+
});
205+
206+
editForm.find('.label-suggestion-checkbox').click(function () {
207+
$(this).siblings('input[type="checkbox"]').click();
208+
});
183209
}
184210
});
185211

@@ -191,14 +217,25 @@ EpComments.prototype.init = async function () {
191217
const $commentForm = $(this).closest('.comment-edit-form');
192218
const commentId = $commentBox.data('commentid');
193219
const commentText = $commentForm.find('.comment-edit-text').val();
220+
const changeFrom = $commentForm.find('.from-value').first().text();
221+
const changeTo = $commentForm.find('.to-value').val();
222+
194223
const data = {};
195224
data.commentId = commentId;
196225
data.padId = clientVars.padId;
197226
data.commentText = commentText;
198227
data.authorId = clientVars.userId;
228+
data.changeFrom = null;
229+
data.changeTo = null;
230+
if ($commentForm.find('.label-suggestion-checkbox')
231+
.siblings('input[type="checkbox"]')
232+
.is(':checked')) {
233+
data.changeFrom = changeFrom;
234+
data.changeTo = changeTo;
235+
}
199236

200237
try {
201-
await self._send('updateCommentText', data);
238+
await self._send('updateComment', data);
202239
} catch (err) {
203240
if (err.message !== 'unauth') throw err; // Let the uncaught error handler handle it.
204241
$.gritter.add({
@@ -212,7 +249,7 @@ EpComments.prototype.init = async function () {
212249
$commentForm.remove();
213250
$commentBox.removeClass('editing');
214251
self.updateCommentBoxText(commentId, commentText);
215-
252+
self.updateCommentBoxChangeTo(commentId, data);
216253
// although the comment or reply was saved on the data base successfully, it needs
217254
// to update the comment or comment reply variable with the new text saved
218255
self.setCommentOrReplyNewText(commentId, commentText);
@@ -381,6 +418,7 @@ EpComments.prototype.findCommentText = function ($commentBox) {
381418
return $commentBox.find('.compact-display-content .comment-text, ' +
382419
'.full-display-content .comment-title-wrapper .comment-text');
383420
};
421+
384422
// This function is useful to collect new comments on the collaborators
385423
EpComments.prototype.collectCommentsAfterSomeIntervalsOfTime = async function () {
386424
await new Promise((resolve) => window.setTimeout(resolve, 300));
@@ -1165,6 +1203,33 @@ EpComments.prototype.updateCommentBoxText = function (commentId, commentText) {
11651203
textBox.text(commentText);
11661204
};
11671205

1206+
EpComments.prototype.updateCommentBoxChangeTo = function (commentId, data) {
1207+
const $comment = this.container.parent().find(`[data-commentid='${commentId}']`);
1208+
const $suggest = $('#display-suggestion').tmpl(data);
1209+
if (data.changeTo) {
1210+
$($suggest[2]).find('.from-label').each((key, item) => {
1211+
if (item.dataset) {
1212+
item.dataset.l10nArgs = JSON.stringify({
1213+
changeFrom: data.changeFrom,
1214+
changeTo: data.changeTo,
1215+
});
1216+
}
1217+
});
1218+
1219+
commentL10n.localize($($suggest[2]));
1220+
if (!$comment.find('.comment-changeTo-form').length) {
1221+
const textBox = this.findCommentText($comment);
1222+
textBox.after($suggest);
1223+
}
1224+
$comment.find('.comment-changeTo-form').replaceWith($suggest);
1225+
1226+
1227+
this.container.parent().find(`[data-commentid='${commentId}']`).replaceWith($comment);
1228+
} else {
1229+
$comment.find('.comment-changeTo-form').remove();
1230+
}
1231+
};
1232+
11681233
EpComments.prototype.showChangeAsAccepted = function (commentId) {
11691234
const self = this;
11701235

@@ -1197,6 +1262,12 @@ EpComments.prototype.pushComment = function (eventType, callback) {
11971262
this.updateCommentBoxText(commentId, commentText);
11981263
});
11991264

1265+
socket.on('commentUpdated', (commentId, commentText, changeFrom, changeTo) => {
1266+
console.log('commentUpdated', changeFrom, changeTo);
1267+
this.updateCommentBoxText(commentId, commentText);
1268+
this.updateCommentBoxChangeTo(commentId, {commentId, commentText, changeFrom, changeTo});
1269+
});
1270+
12001271
socket.on('commentDeleted', (commentId) => {
12011272
this.deleteComment(commentId);
12021273
});

templates/comments.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,19 @@ <h1 data-l10n-id="ep_comments_page.comment">Comment</h1>
131131
<script id="editCommentTemplate" type="text/html">
132132
<div class="comment-edit-form">
133133
<textarea class="comment-edit-text">${text}</textarea>
134+
{{if !changeAcceptedState}}
135+
<div class="form-more">
136+
<p class="comment-suggest">
137+
<input type="checkbox" id="suggestion-checkbox-${commentId}" name="suggestion-checkbox-${commentId}" class="suggestion-checkbox">
138+
<label for="suggestion-checkbox-${commentId}" class="label-suggestion-checkbox" data-l10n-id="ep_comments_page.comments_template.include_suggestion">Include suggested change</label>
139+
</p>
140+
<div class="suggestion suggestion-create">
141+
<span class="from-label" data-l10n-id="ep_comments_page.comments_template.suggest_change_from" data-l10n-args='{"changeFrom": "${changeFrom}"}'>Suggest Change From</span>
142+
<span class="hidden from-value">${changeFrom}</span>
143+
<textarea class="to-value">${changeTo}</textarea>
144+
</div>
145+
</div>
146+
{{/if}}
134147
<p>
135148
<button class="btn btn-primary comment-edit-submit" data-l10n-id="ep_comments_page.comments_template.edit_comment.save">Save</button>
136149
<button class="btn btn-default comment-edit-cancel" data-l10n-id="ep_comments_page.comments_template.edit_comment.cancel">Cancel</button>

0 commit comments

Comments
 (0)