Merge "Fix auto-saving of new comment threads"
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
index 25a1a00..1c00c95 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
@@ -37,6 +37,7 @@
} from '../../../utils/patch-set-util';
import {
CommentThread,
+ equalLocation,
isInBaseOfPatchRange,
isInRevisionOfPatchRange,
} from '../../../utils/comment-util';
@@ -738,15 +739,36 @@
_threadsChanged(threads: CommentThread[]) {
const rootIdToThreadEl = new Map<UrlEncodedCommentId, GrCommentThread>();
+ const unsavedThreadEls: GrCommentThread[] = [];
for (const threadEl of this.getThreadEls()) {
if (threadEl.rootId) {
rootIdToThreadEl.set(threadEl.rootId, threadEl);
+ } else {
+ // Unsaved thread els must have editing:true, just being defensive here.
+ if (threadEl.editing) unsavedThreadEls.push(threadEl);
}
}
const dontRemove = new Set<GrCommentThread>();
for (const thread of threads) {
- const existingThreadEl =
+ // Let's find an existing DOM element matching the thread. Normally this
+ // is as simple as matching the rootIds.
+ let existingThreadEl =
thread.rootId && rootIdToThreadEl.get(thread.rootId);
+ // But unsaved threads don't have rootIds. The incoming thread might be
+ // the saved version of the unsaved thread element. To verify that we
+ // check that the thread only has one comment and that their location is
+ // identical.
+ // TODO(brohlfs): This matching is not perfect. You could quickly create
+ // two new threads on the same line/range. Then this code just makes a
+ // random guess.
+ if (!existingThreadEl && thread.comments?.length === 1) {
+ for (const unsavedThreadEl of unsavedThreadEls) {
+ if (equalLocation(unsavedThreadEl.thread, thread)) {
+ existingThreadEl = unsavedThreadEl;
+ break;
+ }
+ }
+ }
if (existingThreadEl) {
existingThreadEl.thread = thread;
dontRemove.add(existingThreadEl);
@@ -759,6 +781,10 @@
// Remove all threads that are no longer existing.
for (const threadEl of this.getThreadEls()) {
if (dontRemove.has(threadEl)) continue;
+ // The user may have opened a couple of comment boxes for editing. They
+ // might be unsaved and thus not be reflected in `threads` yet, so let's
+ // keep them open.
+ if (threadEl.editing && threadEl.thread?.comments.length === 0) continue;
threadEl.remove();
}
const portedThreadsCount = threads.filter(thread => thread.ported).length;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.js b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.js
index 6149c82..888dce3 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.js
@@ -1166,6 +1166,47 @@
assert.equal(threads.length, 2);
});
+ test('unsaved thread changes to draft', async () => {
+ element.patchRange = {
+ basePatchNum: 2,
+ patchNum: 3,
+ };
+ element.file = {basePath: 'file_renamed.txt', path: element.path};
+ element.threads = [];
+ await flush();
+
+ element.dispatchEvent(new CustomEvent('create-comment', {
+ detail: {
+ side: Side.RIGHT,
+ path: element.path,
+ lineNum: 13,
+ },
+ }));
+ await flush();
+ assert.equal(element.getThreadEls().length, 1);
+ const threadEl = element.getThreadEls()[0];
+ assert.equal(threadEl.thread.line, 13);
+ assert.isDefined(threadEl.unsavedComment);
+ assert.equal(threadEl.thread.comments.length, 0);
+
+ const draftThread = createCommentThread([{
+ path: element.path,
+ patch_set: 3,
+ line: 13,
+ __draft: true,
+ }]);
+ element.threads = [draftThread];
+ await flush();
+
+ // We expect that no additional thread element was created.
+ assert.equal(element.getThreadEls().length, 1);
+ // In fact the thread element must still be the same.
+ assert.equal(element.getThreadEls()[0], threadEl);
+ // But it must have been updated from unsaved to draft:
+ assert.isUndefined(threadEl.unsavedComment);
+ assert.equal(threadEl.thread.comments.length, 1);
+ });
+
test('thread should use new file path if first created ' +
'on patch set (left) but is base', async () => {
element.patchRange = {
diff --git a/polygerrit-ui/app/utils/comment-util.ts b/polygerrit-ui/app/utils/comment-util.ts
index 966a75c..49e9674 100644
--- a/polygerrit-ui/app/utils/comment-util.ts
+++ b/polygerrit-ui/app/utils/comment-util.ts
@@ -205,6 +205,21 @@
rangeInfoLost?: boolean; // if BE was unable to determine a range for this
}
+export function equalLocation(t1?: CommentThread, t2?: CommentThread) {
+ if (t1 === t2) return true;
+ if (t1 === undefined || t2 === undefined) return false;
+ return (
+ t1.path === t2.path &&
+ t1.patchNum === t2.patchNum &&
+ t1.commentSide === t2.commentSide &&
+ t1.line === t2.line &&
+ t1.range?.start_line === t2.range?.start_line &&
+ t1.range?.start_character === t2.range?.start_character &&
+ t1.range?.end_line === t2.range?.end_line &&
+ t1.range?.end_character === t2.range?.end_character
+ );
+}
+
export function getLastComment(thread: CommentThread): CommentInfo | undefined {
const len = thread.comments.length;
return thread.comments[len - 1];