Remove IIFEs from diff_notes_bundle.js
This commit is contained in:
parent
eeaeb2752a
commit
e8561287dc
|
@ -3,65 +3,63 @@
|
|||
|
||||
import Vue from 'vue';
|
||||
|
||||
(() => {
|
||||
const CommentAndResolveBtn = Vue.extend({
|
||||
props: {
|
||||
discussionId: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
textareaIsEmpty: true,
|
||||
discussion: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
showButton: function () {
|
||||
if (this.discussion) {
|
||||
return this.discussion.isResolvable();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
isDiscussionResolved: function () {
|
||||
return this.discussion.isResolved();
|
||||
},
|
||||
buttonText: function () {
|
||||
if (this.isDiscussionResolved) {
|
||||
if (this.textareaIsEmpty) {
|
||||
return "Unresolve discussion";
|
||||
} else {
|
||||
return "Comment & unresolve discussion";
|
||||
}
|
||||
} else {
|
||||
if (this.textareaIsEmpty) {
|
||||
return "Resolve discussion";
|
||||
} else {
|
||||
return "Comment & resolve discussion";
|
||||
}
|
||||
}
|
||||
const CommentAndResolveBtn = Vue.extend({
|
||||
props: {
|
||||
discussionId: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
textareaIsEmpty: true,
|
||||
discussion: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
showButton: function () {
|
||||
if (this.discussion) {
|
||||
return this.discussion.isResolvable();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.discussionId) {
|
||||
this.discussion = CommentsStore.state[this.discussionId];
|
||||
isDiscussionResolved: function () {
|
||||
return this.discussion.isResolved();
|
||||
},
|
||||
buttonText: function () {
|
||||
if (this.isDiscussionResolved) {
|
||||
if (this.textareaIsEmpty) {
|
||||
return "Unresolve discussion";
|
||||
} else {
|
||||
return "Comment & unresolve discussion";
|
||||
}
|
||||
} else {
|
||||
if (this.textareaIsEmpty) {
|
||||
return "Resolve discussion";
|
||||
} else {
|
||||
return "Comment & resolve discussion";
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
if (!this.discussionId) return;
|
||||
|
||||
const $textarea = $(`.js-discussion-note-form[data-discussion-id=${this.discussionId}] .note-textarea`);
|
||||
this.textareaIsEmpty = $textarea.val() === '';
|
||||
|
||||
$textarea.on('input.comment-and-resolve-btn', () => {
|
||||
this.textareaIsEmpty = $textarea.val() === '';
|
||||
});
|
||||
},
|
||||
destroyed: function () {
|
||||
if (!this.discussionId) return;
|
||||
|
||||
$(`.js-discussion-note-form[data-discussion-id=${this.discussionId}] .note-textarea`).off('input.comment-and-resolve-btn');
|
||||
}
|
||||
});
|
||||
},
|
||||
created() {
|
||||
if (this.discussionId) {
|
||||
this.discussion = CommentsStore.state[this.discussionId];
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
if (!this.discussionId) return;
|
||||
|
||||
Vue.component('comment-and-resolve-btn', CommentAndResolveBtn);
|
||||
})(window);
|
||||
const $textarea = $(`.js-discussion-note-form[data-discussion-id=${this.discussionId}] .note-textarea`);
|
||||
this.textareaIsEmpty = $textarea.val() === '';
|
||||
|
||||
$textarea.on('input.comment-and-resolve-btn', () => {
|
||||
this.textareaIsEmpty = $textarea.val() === '';
|
||||
});
|
||||
},
|
||||
destroyed: function () {
|
||||
if (!this.discussionId) return;
|
||||
|
||||
$(`.js-discussion-note-form[data-discussion-id=${this.discussionId}] .note-textarea`).off('input.comment-and-resolve-btn');
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('comment-and-resolve-btn', CommentAndResolveBtn);
|
||||
|
|
|
@ -4,155 +4,153 @@
|
|||
import Vue from 'vue';
|
||||
import collapseIcon from '../icons/collapse_icon.svg';
|
||||
|
||||
(() => {
|
||||
const DiffNoteAvatars = Vue.extend({
|
||||
props: ['discussionId'],
|
||||
data() {
|
||||
return {
|
||||
isVisible: false,
|
||||
lineType: '',
|
||||
storeState: CommentsStore.state,
|
||||
shownAvatars: 3,
|
||||
collapseIcon,
|
||||
};
|
||||
},
|
||||
template: `
|
||||
<div class="diff-comment-avatar-holders"
|
||||
v-show="notesCount !== 0">
|
||||
<div v-if="!isVisible">
|
||||
<img v-for="note in notesSubset"
|
||||
class="avatar diff-comment-avatar has-tooltip js-diff-comment-avatar"
|
||||
width="19"
|
||||
height="19"
|
||||
role="button"
|
||||
data-container="body"
|
||||
data-placement="top"
|
||||
data-html="true"
|
||||
:data-line-type="lineType"
|
||||
:title="note.authorName + ': ' + note.noteTruncated"
|
||||
:src="note.authorAvatar"
|
||||
@click="clickedAvatar($event)" />
|
||||
<span v-if="notesCount > shownAvatars"
|
||||
class="diff-comments-more-count has-tooltip js-diff-comment-avatar"
|
||||
data-container="body"
|
||||
data-placement="top"
|
||||
ref="extraComments"
|
||||
role="button"
|
||||
:data-line-type="lineType"
|
||||
:title="extraNotesTitle"
|
||||
@click="clickedAvatar($event)">{{ moreText }}</span>
|
||||
</div>
|
||||
<button class="diff-notes-collapse js-diff-comment-avatar"
|
||||
type="button"
|
||||
aria-label="Show comments"
|
||||
const DiffNoteAvatars = Vue.extend({
|
||||
props: ['discussionId'],
|
||||
data() {
|
||||
return {
|
||||
isVisible: false,
|
||||
lineType: '',
|
||||
storeState: CommentsStore.state,
|
||||
shownAvatars: 3,
|
||||
collapseIcon,
|
||||
};
|
||||
},
|
||||
template: `
|
||||
<div class="diff-comment-avatar-holders"
|
||||
v-show="notesCount !== 0">
|
||||
<div v-if="!isVisible">
|
||||
<img v-for="note in notesSubset"
|
||||
class="avatar diff-comment-avatar has-tooltip js-diff-comment-avatar"
|
||||
width="19"
|
||||
height="19"
|
||||
role="button"
|
||||
data-container="body"
|
||||
data-placement="top"
|
||||
data-html="true"
|
||||
:data-line-type="lineType"
|
||||
@click="clickedAvatar($event)"
|
||||
v-if="isVisible"
|
||||
v-html="collapseIcon">
|
||||
</button>
|
||||
:title="note.authorName + ': ' + note.noteTruncated"
|
||||
:src="note.authorAvatar"
|
||||
@click="clickedAvatar($event)" />
|
||||
<span v-if="notesCount > shownAvatars"
|
||||
class="diff-comments-more-count has-tooltip js-diff-comment-avatar"
|
||||
data-container="body"
|
||||
data-placement="top"
|
||||
ref="extraComments"
|
||||
role="button"
|
||||
:data-line-type="lineType"
|
||||
:title="extraNotesTitle"
|
||||
@click="clickedAvatar($event)">{{ moreText }}</span>
|
||||
</div>
|
||||
`,
|
||||
mounted() {
|
||||
<button class="diff-notes-collapse js-diff-comment-avatar"
|
||||
type="button"
|
||||
aria-label="Show comments"
|
||||
:data-line-type="lineType"
|
||||
@click="clickedAvatar($event)"
|
||||
v-if="isVisible"
|
||||
v-html="collapseIcon">
|
||||
</button>
|
||||
</div>
|
||||
`,
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.addNoCommentClass();
|
||||
this.setDiscussionVisible();
|
||||
|
||||
this.lineType = $(this.$el).closest('.diff-line-num').hasClass('old_line') ? 'old' : 'new';
|
||||
});
|
||||
|
||||
$(document).on('toggle.comments', () => {
|
||||
this.$nextTick(() => {
|
||||
this.setDiscussionVisible();
|
||||
});
|
||||
});
|
||||
},
|
||||
destroyed() {
|
||||
$(document).off('toggle.comments');
|
||||
},
|
||||
watch: {
|
||||
storeState: {
|
||||
handler() {
|
||||
this.$nextTick(() => {
|
||||
$('.has-tooltip', this.$el).tooltip('fixTitle');
|
||||
|
||||
// We need to add/remove a class to an element that is outside the Vue instance
|
||||
this.addNoCommentClass();
|
||||
});
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
notesSubset() {
|
||||
let notes = [];
|
||||
|
||||
if (this.discussion) {
|
||||
notes = Object.keys(this.discussion.notes)
|
||||
.slice(0, this.shownAvatars)
|
||||
.map(noteId => this.discussion.notes[noteId]);
|
||||
}
|
||||
|
||||
return notes;
|
||||
},
|
||||
extraNotesTitle() {
|
||||
if (this.discussion) {
|
||||
const extra = this.discussion.notesCount() - this.shownAvatars;
|
||||
|
||||
return `${extra} more comment${extra > 1 ? 's' : ''}`;
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
discussion() {
|
||||
return this.storeState[this.discussionId];
|
||||
},
|
||||
notesCount() {
|
||||
if (this.discussion) {
|
||||
return this.discussion.notesCount();
|
||||
}
|
||||
|
||||
return 0;
|
||||
},
|
||||
moreText() {
|
||||
const plusSign = this.notesCount < 100 ? '+' : '';
|
||||
|
||||
return `${plusSign}${this.notesCount - this.shownAvatars}`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
clickedAvatar(e) {
|
||||
notes.addDiffNote(e);
|
||||
|
||||
// Toggle the active state of the toggle all button
|
||||
this.toggleDiscussionsToggleState();
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.addNoCommentClass();
|
||||
this.setDiscussionVisible();
|
||||
|
||||
this.lineType = $(this.$el).closest('.diff-line-num').hasClass('old_line') ? 'old' : 'new';
|
||||
});
|
||||
|
||||
$(document).on('toggle.comments', () => {
|
||||
this.$nextTick(() => {
|
||||
this.setDiscussionVisible();
|
||||
});
|
||||
$('.has-tooltip', this.$el).tooltip('fixTitle');
|
||||
$('.has-tooltip', this.$el).tooltip('hide');
|
||||
});
|
||||
},
|
||||
destroyed() {
|
||||
$(document).off('toggle.comments');
|
||||
addNoCommentClass() {
|
||||
const notesCount = this.notesCount;
|
||||
|
||||
$(this.$el).closest('.js-avatar-container')
|
||||
.toggleClass('js-no-comment-btn', notesCount > 0)
|
||||
.nextUntil('.js-avatar-container')
|
||||
.toggleClass('js-no-comment-btn', notesCount > 0);
|
||||
},
|
||||
watch: {
|
||||
storeState: {
|
||||
handler() {
|
||||
this.$nextTick(() => {
|
||||
$('.has-tooltip', this.$el).tooltip('fixTitle');
|
||||
toggleDiscussionsToggleState() {
|
||||
const $notesHolders = $(this.$el).closest('.code').find('.notes_holder');
|
||||
const $visibleNotesHolders = $notesHolders.filter(':visible');
|
||||
const $toggleDiffCommentsBtn = $(this.$el).closest('.diff-file').find('.js-toggle-diff-comments');
|
||||
|
||||
// We need to add/remove a class to an element that is outside the Vue instance
|
||||
this.addNoCommentClass();
|
||||
});
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
$toggleDiffCommentsBtn.toggleClass('active', $notesHolders.length === $visibleNotesHolders.length);
|
||||
},
|
||||
computed: {
|
||||
notesSubset() {
|
||||
let notes = [];
|
||||
|
||||
if (this.discussion) {
|
||||
notes = Object.keys(this.discussion.notes)
|
||||
.slice(0, this.shownAvatars)
|
||||
.map(noteId => this.discussion.notes[noteId]);
|
||||
}
|
||||
|
||||
return notes;
|
||||
},
|
||||
extraNotesTitle() {
|
||||
if (this.discussion) {
|
||||
const extra = this.discussion.notesCount() - this.shownAvatars;
|
||||
|
||||
return `${extra} more comment${extra > 1 ? 's' : ''}`;
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
discussion() {
|
||||
return this.storeState[this.discussionId];
|
||||
},
|
||||
notesCount() {
|
||||
if (this.discussion) {
|
||||
return this.discussion.notesCount();
|
||||
}
|
||||
|
||||
return 0;
|
||||
},
|
||||
moreText() {
|
||||
const plusSign = this.notesCount < 100 ? '+' : '';
|
||||
|
||||
return `${plusSign}${this.notesCount - this.shownAvatars}`;
|
||||
},
|
||||
setDiscussionVisible() {
|
||||
this.isVisible = $(`.diffs .notes[data-discussion-id="${this.discussion.id}"]`).is(':visible');
|
||||
},
|
||||
methods: {
|
||||
clickedAvatar(e) {
|
||||
notes.addDiffNote(e);
|
||||
},
|
||||
});
|
||||
|
||||
// Toggle the active state of the toggle all button
|
||||
this.toggleDiscussionsToggleState();
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.setDiscussionVisible();
|
||||
|
||||
$('.has-tooltip', this.$el).tooltip('fixTitle');
|
||||
$('.has-tooltip', this.$el).tooltip('hide');
|
||||
});
|
||||
},
|
||||
addNoCommentClass() {
|
||||
const notesCount = this.notesCount;
|
||||
|
||||
$(this.$el).closest('.js-avatar-container')
|
||||
.toggleClass('js-no-comment-btn', notesCount > 0)
|
||||
.nextUntil('.js-avatar-container')
|
||||
.toggleClass('js-no-comment-btn', notesCount > 0);
|
||||
},
|
||||
toggleDiscussionsToggleState() {
|
||||
const $notesHolders = $(this.$el).closest('.code').find('.notes_holder');
|
||||
const $visibleNotesHolders = $notesHolders.filter(':visible');
|
||||
const $toggleDiffCommentsBtn = $(this.$el).closest('.diff-file').find('.js-toggle-diff-comments');
|
||||
|
||||
$toggleDiffCommentsBtn.toggleClass('active', $notesHolders.length === $visibleNotesHolders.length);
|
||||
},
|
||||
setDiscussionVisible() {
|
||||
this.isVisible = $(`.diffs .notes[data-discussion-id="${this.discussion.id}"]`).is(':visible');
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Vue.component('diff-note-avatars', DiffNoteAvatars);
|
||||
})();
|
||||
Vue.component('diff-note-avatars', DiffNoteAvatars);
|
||||
|
|
|
@ -4,192 +4,190 @@
|
|||
|
||||
import Vue from 'vue';
|
||||
|
||||
(() => {
|
||||
const JumpToDiscussion = Vue.extend({
|
||||
mixins: [DiscussionMixins],
|
||||
props: {
|
||||
discussionId: String
|
||||
const JumpToDiscussion = Vue.extend({
|
||||
mixins: [DiscussionMixins],
|
||||
props: {
|
||||
discussionId: String
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
discussions: CommentsStore.state,
|
||||
discussion: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
allResolved: function () {
|
||||
return this.unresolvedDiscussionCount === 0;
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
discussions: CommentsStore.state,
|
||||
discussion: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
allResolved: function () {
|
||||
return this.unresolvedDiscussionCount === 0;
|
||||
},
|
||||
showButton: function () {
|
||||
if (this.discussionId) {
|
||||
if (this.unresolvedDiscussionCount > 1) {
|
||||
return true;
|
||||
} else {
|
||||
return this.discussionId !== this.lastResolvedId;
|
||||
}
|
||||
showButton: function () {
|
||||
if (this.discussionId) {
|
||||
if (this.unresolvedDiscussionCount > 1) {
|
||||
return true;
|
||||
} else {
|
||||
return this.unresolvedDiscussionCount >= 1;
|
||||
return this.discussionId !== this.lastResolvedId;
|
||||
}
|
||||
},
|
||||
lastResolvedId: function () {
|
||||
let lastId;
|
||||
for (const discussionId in this.discussions) {
|
||||
const discussion = this.discussions[discussionId];
|
||||
|
||||
if (!discussion.isResolved()) {
|
||||
lastId = discussion.id;
|
||||
}
|
||||
}
|
||||
return lastId;
|
||||
} else {
|
||||
return this.unresolvedDiscussionCount >= 1;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
jumpToNextUnresolvedDiscussion: function () {
|
||||
let discussionsSelector;
|
||||
let discussionIdsInScope;
|
||||
let firstUnresolvedDiscussionId;
|
||||
let nextUnresolvedDiscussionId;
|
||||
let activeTab = window.mrTabs.currentAction;
|
||||
let hasDiscussionsToJumpTo = true;
|
||||
let jumpToFirstDiscussion = !this.discussionId;
|
||||
lastResolvedId: function () {
|
||||
let lastId;
|
||||
for (const discussionId in this.discussions) {
|
||||
const discussion = this.discussions[discussionId];
|
||||
|
||||
const discussionIdsForElements = function(elements) {
|
||||
return elements.map(function() {
|
||||
return $(this).attr('data-discussion-id');
|
||||
}).toArray();
|
||||
};
|
||||
|
||||
const discussions = this.discussions;
|
||||
|
||||
if (activeTab === 'diffs') {
|
||||
discussionsSelector = '.diffs .notes[data-discussion-id]';
|
||||
discussionIdsInScope = discussionIdsForElements($(discussionsSelector));
|
||||
|
||||
let unresolvedDiscussionCount = 0;
|
||||
|
||||
for (let i = 0; i < discussionIdsInScope.length; i += 1) {
|
||||
const discussionId = discussionIdsInScope[i];
|
||||
const discussion = discussions[discussionId];
|
||||
if (discussion && !discussion.isResolved()) {
|
||||
unresolvedDiscussionCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.discussionId && !this.discussion.isResolved()) {
|
||||
// If this is the last unresolved discussion on the diffs tab,
|
||||
// there are no discussions to jump to.
|
||||
if (unresolvedDiscussionCount === 1) {
|
||||
hasDiscussionsToJumpTo = false;
|
||||
}
|
||||
} else {
|
||||
// If there are no unresolved discussions on the diffs tab at all,
|
||||
// there are no discussions to jump to.
|
||||
if (unresolvedDiscussionCount === 0) {
|
||||
hasDiscussionsToJumpTo = false;
|
||||
}
|
||||
}
|
||||
} else if (activeTab !== 'notes') {
|
||||
// If we are on the commits or builds tabs,
|
||||
// there are no discussions to jump to.
|
||||
hasDiscussionsToJumpTo = false;
|
||||
if (!discussion.isResolved()) {
|
||||
lastId = discussion.id;
|
||||
}
|
||||
}
|
||||
return lastId;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
jumpToNextUnresolvedDiscussion: function () {
|
||||
let discussionsSelector;
|
||||
let discussionIdsInScope;
|
||||
let firstUnresolvedDiscussionId;
|
||||
let nextUnresolvedDiscussionId;
|
||||
let activeTab = window.mrTabs.currentAction;
|
||||
let hasDiscussionsToJumpTo = true;
|
||||
let jumpToFirstDiscussion = !this.discussionId;
|
||||
|
||||
if (!hasDiscussionsToJumpTo) {
|
||||
// If there are no discussions to jump to on the current page,
|
||||
// switch to the notes tab and jump to the first disucssion there.
|
||||
window.mrTabs.activateTab('notes');
|
||||
activeTab = 'notes';
|
||||
jumpToFirstDiscussion = true;
|
||||
}
|
||||
const discussionIdsForElements = function(elements) {
|
||||
return elements.map(function() {
|
||||
return $(this).attr('data-discussion-id');
|
||||
}).toArray();
|
||||
};
|
||||
|
||||
if (activeTab === 'notes') {
|
||||
discussionsSelector = '.discussion[data-discussion-id]';
|
||||
discussionIdsInScope = discussionIdsForElements($(discussionsSelector));
|
||||
}
|
||||
const discussions = this.discussions;
|
||||
|
||||
if (activeTab === 'diffs') {
|
||||
discussionsSelector = '.diffs .notes[data-discussion-id]';
|
||||
discussionIdsInScope = discussionIdsForElements($(discussionsSelector));
|
||||
|
||||
let unresolvedDiscussionCount = 0;
|
||||
|
||||
let currentDiscussionFound = false;
|
||||
for (let i = 0; i < discussionIdsInScope.length; i += 1) {
|
||||
const discussionId = discussionIdsInScope[i];
|
||||
const discussion = discussions[discussionId];
|
||||
|
||||
if (!discussion) {
|
||||
// Discussions for comments on commits in this MR don't have a resolved status.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!firstUnresolvedDiscussionId && !discussion.isResolved()) {
|
||||
firstUnresolvedDiscussionId = discussionId;
|
||||
|
||||
if (jumpToFirstDiscussion) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!jumpToFirstDiscussion) {
|
||||
if (currentDiscussionFound) {
|
||||
if (!discussion.isResolved()) {
|
||||
nextUnresolvedDiscussionId = discussionId;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (discussionId === this.discussionId) {
|
||||
currentDiscussionFound = true;
|
||||
}
|
||||
if (discussion && !discussion.isResolved()) {
|
||||
unresolvedDiscussionCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
nextUnresolvedDiscussionId = nextUnresolvedDiscussionId || firstUnresolvedDiscussionId;
|
||||
|
||||
if (!nextUnresolvedDiscussionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
let $target = $(`${discussionsSelector}[data-discussion-id="${nextUnresolvedDiscussionId}"]`);
|
||||
|
||||
if (activeTab === 'notes') {
|
||||
$target = $target.closest('.note-discussion');
|
||||
|
||||
// If the next discussion is closed, toggle it open.
|
||||
if ($target.find('.js-toggle-content').is(':hidden')) {
|
||||
$target.find('.js-toggle-button i').trigger('click');
|
||||
if (this.discussionId && !this.discussion.isResolved()) {
|
||||
// If this is the last unresolved discussion on the diffs tab,
|
||||
// there are no discussions to jump to.
|
||||
if (unresolvedDiscussionCount === 1) {
|
||||
hasDiscussionsToJumpTo = false;
|
||||
}
|
||||
} else if (activeTab === 'diffs') {
|
||||
// Resolved discussions are hidden in the diffs tab by default.
|
||||
// If they are marked unresolved on the notes tab, they will still be hidden on the diffs tab.
|
||||
// When jumping between unresolved discussions on the diffs tab, we show them.
|
||||
$target.closest(".content").show();
|
||||
|
||||
$target = $target.closest("tr.notes_holder");
|
||||
$target.show();
|
||||
|
||||
// If we are on the diffs tab, we don't scroll to the discussion itself, but to
|
||||
// 4 diff lines above it: the line the discussion was in response to + 3 context
|
||||
let prevEl;
|
||||
for (let i = 0; i < 4; i += 1) {
|
||||
prevEl = $target.prev();
|
||||
|
||||
// If the discussion doesn't have 4 lines above it, we'll have to do with fewer.
|
||||
if (!prevEl.hasClass("line_holder")) {
|
||||
break;
|
||||
}
|
||||
|
||||
$target = prevEl;
|
||||
} else {
|
||||
// If there are no unresolved discussions on the diffs tab at all,
|
||||
// there are no discussions to jump to.
|
||||
if (unresolvedDiscussionCount === 0) {
|
||||
hasDiscussionsToJumpTo = false;
|
||||
}
|
||||
}
|
||||
|
||||
$.scrollTo($target, {
|
||||
offset: 0
|
||||
});
|
||||
} else if (activeTab !== 'notes') {
|
||||
// If we are on the commits or builds tabs,
|
||||
// there are no discussions to jump to.
|
||||
hasDiscussionsToJumpTo = false;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.discussion = this.discussions[this.discussionId];
|
||||
},
|
||||
});
|
||||
|
||||
Vue.component('jump-to-discussion', JumpToDiscussion);
|
||||
})();
|
||||
if (!hasDiscussionsToJumpTo) {
|
||||
// If there are no discussions to jump to on the current page,
|
||||
// switch to the notes tab and jump to the first disucssion there.
|
||||
window.mrTabs.activateTab('notes');
|
||||
activeTab = 'notes';
|
||||
jumpToFirstDiscussion = true;
|
||||
}
|
||||
|
||||
if (activeTab === 'notes') {
|
||||
discussionsSelector = '.discussion[data-discussion-id]';
|
||||
discussionIdsInScope = discussionIdsForElements($(discussionsSelector));
|
||||
}
|
||||
|
||||
let currentDiscussionFound = false;
|
||||
for (let i = 0; i < discussionIdsInScope.length; i += 1) {
|
||||
const discussionId = discussionIdsInScope[i];
|
||||
const discussion = discussions[discussionId];
|
||||
|
||||
if (!discussion) {
|
||||
// Discussions for comments on commits in this MR don't have a resolved status.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!firstUnresolvedDiscussionId && !discussion.isResolved()) {
|
||||
firstUnresolvedDiscussionId = discussionId;
|
||||
|
||||
if (jumpToFirstDiscussion) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!jumpToFirstDiscussion) {
|
||||
if (currentDiscussionFound) {
|
||||
if (!discussion.isResolved()) {
|
||||
nextUnresolvedDiscussionId = discussionId;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (discussionId === this.discussionId) {
|
||||
currentDiscussionFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextUnresolvedDiscussionId = nextUnresolvedDiscussionId || firstUnresolvedDiscussionId;
|
||||
|
||||
if (!nextUnresolvedDiscussionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
let $target = $(`${discussionsSelector}[data-discussion-id="${nextUnresolvedDiscussionId}"]`);
|
||||
|
||||
if (activeTab === 'notes') {
|
||||
$target = $target.closest('.note-discussion');
|
||||
|
||||
// If the next discussion is closed, toggle it open.
|
||||
if ($target.find('.js-toggle-content').is(':hidden')) {
|
||||
$target.find('.js-toggle-button i').trigger('click');
|
||||
}
|
||||
} else if (activeTab === 'diffs') {
|
||||
// Resolved discussions are hidden in the diffs tab by default.
|
||||
// If they are marked unresolved on the notes tab, they will still be hidden on the diffs tab.
|
||||
// When jumping between unresolved discussions on the diffs tab, we show them.
|
||||
$target.closest(".content").show();
|
||||
|
||||
$target = $target.closest("tr.notes_holder");
|
||||
$target.show();
|
||||
|
||||
// If we are on the diffs tab, we don't scroll to the discussion itself, but to
|
||||
// 4 diff lines above it: the line the discussion was in response to + 3 context
|
||||
let prevEl;
|
||||
for (let i = 0; i < 4; i += 1) {
|
||||
prevEl = $target.prev();
|
||||
|
||||
// If the discussion doesn't have 4 lines above it, we'll have to do with fewer.
|
||||
if (!prevEl.hasClass("line_holder")) {
|
||||
break;
|
||||
}
|
||||
|
||||
$target = prevEl;
|
||||
}
|
||||
}
|
||||
|
||||
$.scrollTo($target, {
|
||||
offset: 0
|
||||
});
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.discussion = this.discussions[this.discussionId];
|
||||
},
|
||||
});
|
||||
|
||||
Vue.component('jump-to-discussion', JumpToDiscussion);
|
||||
|
|
|
@ -2,29 +2,27 @@
|
|||
|
||||
import Vue from 'vue';
|
||||
|
||||
(() => {
|
||||
const NewIssueForDiscussion = Vue.extend({
|
||||
props: {
|
||||
discussionId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
const NewIssueForDiscussion = Vue.extend({
|
||||
props: {
|
||||
discussionId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
discussions: CommentsStore.state,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
discussions: CommentsStore.state,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
discussion() {
|
||||
return this.discussions[this.discussionId];
|
||||
},
|
||||
computed: {
|
||||
discussion() {
|
||||
return this.discussions[this.discussionId];
|
||||
},
|
||||
showButton() {
|
||||
if (this.discussion) return !this.discussion.isResolved();
|
||||
return false;
|
||||
},
|
||||
showButton() {
|
||||
if (this.discussion) return !this.discussion.isResolved();
|
||||
return false;
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
Vue.component('new-issue-for-discussion-btn', NewIssueForDiscussion);
|
||||
})();
|
||||
Vue.component('new-issue-for-discussion-btn', NewIssueForDiscussion);
|
||||
|
|
|
@ -5,117 +5,115 @@
|
|||
|
||||
import Vue from 'vue';
|
||||
|
||||
(() => {
|
||||
const ResolveBtn = Vue.extend({
|
||||
props: {
|
||||
noteId: Number,
|
||||
discussionId: String,
|
||||
resolved: Boolean,
|
||||
canResolve: Boolean,
|
||||
resolvedBy: String,
|
||||
authorName: String,
|
||||
authorAvatar: String,
|
||||
noteTruncated: String,
|
||||
const ResolveBtn = Vue.extend({
|
||||
props: {
|
||||
noteId: Number,
|
||||
discussionId: String,
|
||||
resolved: Boolean,
|
||||
canResolve: Boolean,
|
||||
resolvedBy: String,
|
||||
authorName: String,
|
||||
authorAvatar: String,
|
||||
noteTruncated: String,
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
discussions: CommentsStore.state,
|
||||
loading: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'discussions': {
|
||||
handler: 'updateTooltip',
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
discussion: function () {
|
||||
return this.discussions[this.discussionId];
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
discussions: CommentsStore.state,
|
||||
loading: false
|
||||
};
|
||||
note: function () {
|
||||
return this.discussion ? this.discussion.getNote(this.noteId) : {};
|
||||
},
|
||||
watch: {
|
||||
'discussions': {
|
||||
handler: 'updateTooltip',
|
||||
deep: true
|
||||
buttonText: function () {
|
||||
if (this.isResolved) {
|
||||
return `Resolved by ${this.resolvedByName}`;
|
||||
} else if (this.canResolve) {
|
||||
return 'Mark as resolved';
|
||||
} else {
|
||||
return 'Unable to resolve';
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
discussion: function () {
|
||||
return this.discussions[this.discussionId];
|
||||
},
|
||||
note: function () {
|
||||
return this.discussion ? this.discussion.getNote(this.noteId) : {};
|
||||
},
|
||||
buttonText: function () {
|
||||
if (this.isResolved) {
|
||||
return `Resolved by ${this.resolvedByName}`;
|
||||
} else if (this.canResolve) {
|
||||
return 'Mark as resolved';
|
||||
} else {
|
||||
return 'Unable to resolve';
|
||||
}
|
||||
},
|
||||
isResolved: function () {
|
||||
if (this.note) {
|
||||
return this.note.resolved;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
resolvedByName: function () {
|
||||
return this.note.resolved_by;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateTooltip: function () {
|
||||
this.$nextTick(() => {
|
||||
$(this.$refs.button)
|
||||
.tooltip('hide')
|
||||
.tooltip('fixTitle');
|
||||
});
|
||||
},
|
||||
resolve: function () {
|
||||
if (!this.canResolve) return;
|
||||
|
||||
let promise;
|
||||
this.loading = true;
|
||||
|
||||
if (this.isResolved) {
|
||||
promise = ResolveService
|
||||
.unresolve(this.noteId);
|
||||
} else {
|
||||
promise = ResolveService
|
||||
.resolve(this.noteId);
|
||||
}
|
||||
|
||||
promise.then((response) => {
|
||||
this.loading = false;
|
||||
|
||||
if (response.status === 200) {
|
||||
const data = response.json();
|
||||
const resolved_by = data ? data.resolved_by : null;
|
||||
|
||||
CommentsStore.update(this.discussionId, this.noteId, !this.isResolved, resolved_by);
|
||||
this.discussion.updateHeadline(data);
|
||||
} else {
|
||||
new Flash('An error occurred when trying to resolve a comment. Please try again.', 'alert');
|
||||
}
|
||||
|
||||
this.updateTooltip();
|
||||
});
|
||||
isResolved: function () {
|
||||
if (this.note) {
|
||||
return this.note.resolved;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
$(this.$refs.button).tooltip({
|
||||
container: 'body'
|
||||
resolvedByName: function () {
|
||||
return this.note.resolved_by;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateTooltip: function () {
|
||||
this.$nextTick(() => {
|
||||
$(this.$refs.button)
|
||||
.tooltip('hide')
|
||||
.tooltip('fixTitle');
|
||||
});
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
CommentsStore.delete(this.discussionId, this.noteId);
|
||||
},
|
||||
created: function () {
|
||||
CommentsStore.create({
|
||||
discussionId: this.discussionId,
|
||||
noteId: this.noteId,
|
||||
canResolve: this.canResolve,
|
||||
resolved: this.resolved,
|
||||
resolvedBy: this.resolvedBy,
|
||||
authorName: this.authorName,
|
||||
authorAvatar: this.authorAvatar,
|
||||
noteTruncated: this.noteTruncated,
|
||||
resolve: function () {
|
||||
if (!this.canResolve) return;
|
||||
|
||||
let promise;
|
||||
this.loading = true;
|
||||
|
||||
if (this.isResolved) {
|
||||
promise = ResolveService
|
||||
.unresolve(this.noteId);
|
||||
} else {
|
||||
promise = ResolveService
|
||||
.resolve(this.noteId);
|
||||
}
|
||||
|
||||
promise.then((response) => {
|
||||
this.loading = false;
|
||||
|
||||
if (response.status === 200) {
|
||||
const data = response.json();
|
||||
const resolved_by = data ? data.resolved_by : null;
|
||||
|
||||
CommentsStore.update(this.discussionId, this.noteId, !this.isResolved, resolved_by);
|
||||
this.discussion.updateHeadline(data);
|
||||
} else {
|
||||
new Flash('An error occurred when trying to resolve a comment. Please try again.', 'alert');
|
||||
}
|
||||
|
||||
this.updateTooltip();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
mounted: function () {
|
||||
$(this.$refs.button).tooltip({
|
||||
container: 'body'
|
||||
});
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
CommentsStore.delete(this.discussionId, this.noteId);
|
||||
},
|
||||
created: function () {
|
||||
CommentsStore.create({
|
||||
discussionId: this.discussionId,
|
||||
noteId: this.noteId,
|
||||
canResolve: this.canResolve,
|
||||
resolved: this.resolved,
|
||||
resolvedBy: this.resolvedBy,
|
||||
authorName: this.authorName,
|
||||
authorAvatar: this.authorAvatar,
|
||||
noteTruncated: this.noteTruncated,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('resolve-btn', ResolveBtn);
|
||||
})();
|
||||
Vue.component('resolve-btn', ResolveBtn);
|
||||
|
|
|
@ -4,24 +4,22 @@
|
|||
|
||||
import Vue from 'vue';
|
||||
|
||||
((w) => {
|
||||
w.ResolveCount = Vue.extend({
|
||||
mixins: [DiscussionMixins],
|
||||
props: {
|
||||
loggedOut: Boolean
|
||||
window.ResolveCount = Vue.extend({
|
||||
mixins: [DiscussionMixins],
|
||||
props: {
|
||||
loggedOut: Boolean
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
discussions: CommentsStore.state
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
allResolved: function () {
|
||||
return this.resolvedDiscussionCount === this.discussionCount;
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
discussions: CommentsStore.state
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
allResolved: function () {
|
||||
return this.resolvedDiscussionCount === this.discussionCount;
|
||||
},
|
||||
resolvedCountText() {
|
||||
return this.discussionCount === 1 ? 'discussion' : 'discussions';
|
||||
}
|
||||
resolvedCountText() {
|
||||
return this.discussionCount === 1 ? 'discussion' : 'discussions';
|
||||
}
|
||||
});
|
||||
})(window);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -4,59 +4,57 @@
|
|||
|
||||
import Vue from 'vue';
|
||||
|
||||
(() => {
|
||||
const ResolveDiscussionBtn = Vue.extend({
|
||||
props: {
|
||||
discussionId: String,
|
||||
mergeRequestId: Number,
|
||||
canResolve: Boolean,
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
discussion: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
showButton: function () {
|
||||
if (this.discussion) {
|
||||
return this.discussion.isResolvable();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
isDiscussionResolved: function () {
|
||||
if (this.discussion) {
|
||||
return this.discussion.isResolved();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
buttonText: function () {
|
||||
if (this.isDiscussionResolved) {
|
||||
return "Unresolve discussion";
|
||||
} else {
|
||||
return "Resolve discussion";
|
||||
}
|
||||
},
|
||||
loading: function () {
|
||||
if (this.discussion) {
|
||||
return this.discussion.loading;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
const ResolveDiscussionBtn = Vue.extend({
|
||||
props: {
|
||||
discussionId: String,
|
||||
mergeRequestId: Number,
|
||||
canResolve: Boolean,
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
discussion: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
showButton: function () {
|
||||
if (this.discussion) {
|
||||
return this.discussion.isResolvable();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resolve: function () {
|
||||
ResolveService.toggleResolveForDiscussion(this.mergeRequestId, this.discussionId);
|
||||
isDiscussionResolved: function () {
|
||||
if (this.discussion) {
|
||||
return this.discussion.isResolved();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
CommentsStore.createDiscussion(this.discussionId, this.canResolve);
|
||||
|
||||
this.discussion = CommentsStore.state[this.discussionId];
|
||||
buttonText: function () {
|
||||
if (this.isDiscussionResolved) {
|
||||
return "Unresolve discussion";
|
||||
} else {
|
||||
return "Resolve discussion";
|
||||
}
|
||||
},
|
||||
loading: function () {
|
||||
if (this.discussion) {
|
||||
return this.discussion.loading;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
resolve: function () {
|
||||
ResolveService.toggleResolveForDiscussion(this.mergeRequestId, this.discussionId);
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
CommentsStore.createDiscussion(this.discussionId, this.canResolve);
|
||||
|
||||
Vue.component('resolve-discussion-btn', ResolveDiscussionBtn);
|
||||
})();
|
||||
this.discussion = CommentsStore.state[this.discussionId];
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('resolve-discussion-btn', ResolveDiscussionBtn);
|
||||
|
|
|
@ -1,37 +1,35 @@
|
|||
/* eslint-disable object-shorthand, func-names, guard-for-in, no-restricted-syntax, comma-dangle, no-param-reassign, max-len */
|
||||
|
||||
((w) => {
|
||||
w.DiscussionMixins = {
|
||||
computed: {
|
||||
discussionCount: function () {
|
||||
return Object.keys(this.discussions).length;
|
||||
},
|
||||
resolvedDiscussionCount: function () {
|
||||
let resolvedCount = 0;
|
||||
window.DiscussionMixins = {
|
||||
computed: {
|
||||
discussionCount: function () {
|
||||
return Object.keys(this.discussions).length;
|
||||
},
|
||||
resolvedDiscussionCount: function () {
|
||||
let resolvedCount = 0;
|
||||
|
||||
for (const discussionId in this.discussions) {
|
||||
const discussion = this.discussions[discussionId];
|
||||
for (const discussionId in this.discussions) {
|
||||
const discussion = this.discussions[discussionId];
|
||||
|
||||
if (discussion.isResolved()) {
|
||||
resolvedCount += 1;
|
||||
}
|
||||
if (discussion.isResolved()) {
|
||||
resolvedCount += 1;
|
||||
}
|
||||
|
||||
return resolvedCount;
|
||||
},
|
||||
unresolvedDiscussionCount: function () {
|
||||
let unresolvedCount = 0;
|
||||
|
||||
for (const discussionId in this.discussions) {
|
||||
const discussion = this.discussions[discussionId];
|
||||
|
||||
if (!discussion.isResolved()) {
|
||||
unresolvedCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return unresolvedCount;
|
||||
}
|
||||
|
||||
return resolvedCount;
|
||||
},
|
||||
unresolvedDiscussionCount: function () {
|
||||
let unresolvedCount = 0;
|
||||
|
||||
for (const discussionId in this.discussions) {
|
||||
const discussion = this.discussions[discussionId];
|
||||
|
||||
if (!discussion.isResolved()) {
|
||||
unresolvedCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return unresolvedCount;
|
||||
}
|
||||
};
|
||||
})(window);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -9,76 +9,74 @@ require('../../vue_shared/vue_resource_interceptor');
|
|||
|
||||
Vue.use(VueResource);
|
||||
|
||||
(() => {
|
||||
window.gl = window.gl || {};
|
||||
window.gl = window.gl || {};
|
||||
|
||||
class ResolveServiceClass {
|
||||
constructor(root) {
|
||||
this.noteResource = Vue.resource(`${root}/notes{/noteId}/resolve`);
|
||||
this.discussionResource = Vue.resource(`${root}/merge_requests{/mergeRequestId}/discussions{/discussionId}/resolve`);
|
||||
}
|
||||
|
||||
resolve(noteId) {
|
||||
return this.noteResource.save({ noteId }, {});
|
||||
}
|
||||
|
||||
unresolve(noteId) {
|
||||
return this.noteResource.delete({ noteId }, {});
|
||||
}
|
||||
|
||||
toggleResolveForDiscussion(mergeRequestId, discussionId) {
|
||||
const discussion = CommentsStore.state[discussionId];
|
||||
const isResolved = discussion.isResolved();
|
||||
let promise;
|
||||
|
||||
if (isResolved) {
|
||||
promise = this.unResolveAll(mergeRequestId, discussionId);
|
||||
} else {
|
||||
promise = this.resolveAll(mergeRequestId, discussionId);
|
||||
}
|
||||
|
||||
promise.then((response) => {
|
||||
discussion.loading = false;
|
||||
|
||||
if (response.status === 200) {
|
||||
const data = response.json();
|
||||
const resolved_by = data ? data.resolved_by : null;
|
||||
|
||||
if (isResolved) {
|
||||
discussion.unResolveAllNotes();
|
||||
} else {
|
||||
discussion.resolveAllNotes(resolved_by);
|
||||
}
|
||||
|
||||
discussion.updateHeadline(data);
|
||||
} else {
|
||||
new Flash('An error occurred when trying to resolve a discussion. Please try again.', 'alert');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
resolveAll(mergeRequestId, discussionId) {
|
||||
const discussion = CommentsStore.state[discussionId];
|
||||
|
||||
discussion.loading = true;
|
||||
|
||||
return this.discussionResource.save({
|
||||
mergeRequestId,
|
||||
discussionId
|
||||
}, {});
|
||||
}
|
||||
|
||||
unResolveAll(mergeRequestId, discussionId) {
|
||||
const discussion = CommentsStore.state[discussionId];
|
||||
|
||||
discussion.loading = true;
|
||||
|
||||
return this.discussionResource.delete({
|
||||
mergeRequestId,
|
||||
discussionId
|
||||
}, {});
|
||||
}
|
||||
class ResolveServiceClass {
|
||||
constructor(root) {
|
||||
this.noteResource = Vue.resource(`${root}/notes{/noteId}/resolve`);
|
||||
this.discussionResource = Vue.resource(`${root}/merge_requests{/mergeRequestId}/discussions{/discussionId}/resolve`);
|
||||
}
|
||||
|
||||
gl.DiffNotesResolveServiceClass = ResolveServiceClass;
|
||||
})();
|
||||
resolve(noteId) {
|
||||
return this.noteResource.save({ noteId }, {});
|
||||
}
|
||||
|
||||
unresolve(noteId) {
|
||||
return this.noteResource.delete({ noteId }, {});
|
||||
}
|
||||
|
||||
toggleResolveForDiscussion(mergeRequestId, discussionId) {
|
||||
const discussion = CommentsStore.state[discussionId];
|
||||
const isResolved = discussion.isResolved();
|
||||
let promise;
|
||||
|
||||
if (isResolved) {
|
||||
promise = this.unResolveAll(mergeRequestId, discussionId);
|
||||
} else {
|
||||
promise = this.resolveAll(mergeRequestId, discussionId);
|
||||
}
|
||||
|
||||
promise.then((response) => {
|
||||
discussion.loading = false;
|
||||
|
||||
if (response.status === 200) {
|
||||
const data = response.json();
|
||||
const resolved_by = data ? data.resolved_by : null;
|
||||
|
||||
if (isResolved) {
|
||||
discussion.unResolveAllNotes();
|
||||
} else {
|
||||
discussion.resolveAllNotes(resolved_by);
|
||||
}
|
||||
|
||||
discussion.updateHeadline(data);
|
||||
} else {
|
||||
new Flash('An error occurred when trying to resolve a discussion. Please try again.', 'alert');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
resolveAll(mergeRequestId, discussionId) {
|
||||
const discussion = CommentsStore.state[discussionId];
|
||||
|
||||
discussion.loading = true;
|
||||
|
||||
return this.discussionResource.save({
|
||||
mergeRequestId,
|
||||
discussionId
|
||||
}, {});
|
||||
}
|
||||
|
||||
unResolveAll(mergeRequestId, discussionId) {
|
||||
const discussion = CommentsStore.state[discussionId];
|
||||
|
||||
discussion.loading = true;
|
||||
|
||||
return this.discussionResource.delete({
|
||||
mergeRequestId,
|
||||
discussionId
|
||||
}, {});
|
||||
}
|
||||
}
|
||||
|
||||
gl.DiffNotesResolveServiceClass = ResolveServiceClass;
|
||||
|
|
|
@ -3,56 +3,54 @@
|
|||
|
||||
import Vue from 'vue';
|
||||
|
||||
((w) => {
|
||||
w.CommentsStore = {
|
||||
state: {},
|
||||
get: function (discussionId, noteId) {
|
||||
return this.state[discussionId].getNote(noteId);
|
||||
},
|
||||
createDiscussion: function (discussionId, canResolve) {
|
||||
let discussion = this.state[discussionId];
|
||||
if (!this.state[discussionId]) {
|
||||
discussion = new DiscussionModel(discussionId);
|
||||
Vue.set(this.state, discussionId, discussion);
|
||||
}
|
||||
|
||||
if (canResolve !== undefined) {
|
||||
discussion.canResolve = canResolve;
|
||||
}
|
||||
|
||||
return discussion;
|
||||
},
|
||||
create: function (noteObj) {
|
||||
const discussion = this.createDiscussion(noteObj.discussionId);
|
||||
|
||||
discussion.createNote(noteObj);
|
||||
},
|
||||
update: function (discussionId, noteId, resolved, resolved_by) {
|
||||
const discussion = this.state[discussionId];
|
||||
const note = discussion.getNote(noteId);
|
||||
note.resolved = resolved;
|
||||
note.resolved_by = resolved_by;
|
||||
},
|
||||
delete: function (discussionId, noteId) {
|
||||
const discussion = this.state[discussionId];
|
||||
discussion.deleteNote(noteId);
|
||||
|
||||
if (discussion.notesCount() === 0) {
|
||||
Vue.delete(this.state, discussionId);
|
||||
}
|
||||
},
|
||||
unresolvedDiscussionIds: function () {
|
||||
const ids = [];
|
||||
|
||||
for (const discussionId in this.state) {
|
||||
const discussion = this.state[discussionId];
|
||||
|
||||
if (!discussion.isResolved()) {
|
||||
ids.push(discussion.id);
|
||||
}
|
||||
}
|
||||
|
||||
return ids;
|
||||
window.CommentsStore = {
|
||||
state: {},
|
||||
get: function (discussionId, noteId) {
|
||||
return this.state[discussionId].getNote(noteId);
|
||||
},
|
||||
createDiscussion: function (discussionId, canResolve) {
|
||||
let discussion = this.state[discussionId];
|
||||
if (!this.state[discussionId]) {
|
||||
discussion = new DiscussionModel(discussionId);
|
||||
Vue.set(this.state, discussionId, discussion);
|
||||
}
|
||||
};
|
||||
})(window);
|
||||
|
||||
if (canResolve !== undefined) {
|
||||
discussion.canResolve = canResolve;
|
||||
}
|
||||
|
||||
return discussion;
|
||||
},
|
||||
create: function (noteObj) {
|
||||
const discussion = this.createDiscussion(noteObj.discussionId);
|
||||
|
||||
discussion.createNote(noteObj);
|
||||
},
|
||||
update: function (discussionId, noteId, resolved, resolved_by) {
|
||||
const discussion = this.state[discussionId];
|
||||
const note = discussion.getNote(noteId);
|
||||
note.resolved = resolved;
|
||||
note.resolved_by = resolved_by;
|
||||
},
|
||||
delete: function (discussionId, noteId) {
|
||||
const discussion = this.state[discussionId];
|
||||
discussion.deleteNote(noteId);
|
||||
|
||||
if (discussion.notesCount() === 0) {
|
||||
Vue.delete(this.state, discussionId);
|
||||
}
|
||||
},
|
||||
unresolvedDiscussionIds: function () {
|
||||
const ids = [];
|
||||
|
||||
for (const discussionId in this.state) {
|
||||
const discussion = this.state[discussionId];
|
||||
|
||||
if (!discussion.isResolved()) {
|
||||
ids.push(discussion.id);
|
||||
}
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -5,129 +5,127 @@ require('~/diff_notes/models/discussion');
|
|||
require('~/diff_notes/models/note');
|
||||
require('~/diff_notes/stores/comments');
|
||||
|
||||
(() => {
|
||||
function createDiscussion(noteId = 1, resolved = true) {
|
||||
CommentsStore.create({
|
||||
discussionId: 'a',
|
||||
noteId,
|
||||
canResolve: true,
|
||||
resolved,
|
||||
resolvedBy: 'test',
|
||||
authorName: 'test',
|
||||
authorAvatar: 'test',
|
||||
noteTruncated: 'test...',
|
||||
});
|
||||
}
|
||||
function createDiscussion(noteId = 1, resolved = true) {
|
||||
CommentsStore.create({
|
||||
discussionId: 'a',
|
||||
noteId,
|
||||
canResolve: true,
|
||||
resolved,
|
||||
resolvedBy: 'test',
|
||||
authorName: 'test',
|
||||
authorAvatar: 'test',
|
||||
noteTruncated: 'test...',
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
CommentsStore.state = {};
|
||||
});
|
||||
|
||||
describe('New discussion', () => {
|
||||
it('creates new discussion', () => {
|
||||
expect(Object.keys(CommentsStore.state).length).toBe(0);
|
||||
createDiscussion();
|
||||
expect(Object.keys(CommentsStore.state).length).toBe(1);
|
||||
});
|
||||
|
||||
it('creates new note in discussion', () => {
|
||||
createDiscussion();
|
||||
createDiscussion(2);
|
||||
|
||||
const discussion = CommentsStore.state['a'];
|
||||
expect(Object.keys(discussion.notes).length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Get note', () => {
|
||||
beforeEach(() => {
|
||||
CommentsStore.state = {};
|
||||
expect(Object.keys(CommentsStore.state).length).toBe(0);
|
||||
createDiscussion();
|
||||
});
|
||||
|
||||
describe('New discussion', () => {
|
||||
it('creates new discussion', () => {
|
||||
expect(Object.keys(CommentsStore.state).length).toBe(0);
|
||||
createDiscussion();
|
||||
expect(Object.keys(CommentsStore.state).length).toBe(1);
|
||||
});
|
||||
it('gets note by ID', () => {
|
||||
const note = CommentsStore.get('a', 1);
|
||||
expect(note).toBeDefined();
|
||||
expect(note.id).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('creates new note in discussion', () => {
|
||||
createDiscussion();
|
||||
createDiscussion(2);
|
||||
|
||||
const discussion = CommentsStore.state['a'];
|
||||
expect(Object.keys(discussion.notes).length).toBe(2);
|
||||
});
|
||||
describe('Delete discussion', () => {
|
||||
beforeEach(() => {
|
||||
expect(Object.keys(CommentsStore.state).length).toBe(0);
|
||||
createDiscussion();
|
||||
});
|
||||
|
||||
describe('Get note', () => {
|
||||
beforeEach(() => {
|
||||
expect(Object.keys(CommentsStore.state).length).toBe(0);
|
||||
createDiscussion();
|
||||
});
|
||||
|
||||
it('gets note by ID', () => {
|
||||
const note = CommentsStore.get('a', 1);
|
||||
expect(note).toBeDefined();
|
||||
expect(note.id).toBe(1);
|
||||
});
|
||||
it('deletes discussion by ID', () => {
|
||||
CommentsStore.delete('a', 1);
|
||||
expect(Object.keys(CommentsStore.state).length).toBe(0);
|
||||
});
|
||||
|
||||
describe('Delete discussion', () => {
|
||||
beforeEach(() => {
|
||||
expect(Object.keys(CommentsStore.state).length).toBe(0);
|
||||
createDiscussion();
|
||||
});
|
||||
it('deletes discussion when no more notes', () => {
|
||||
createDiscussion();
|
||||
createDiscussion(2);
|
||||
expect(Object.keys(CommentsStore.state).length).toBe(1);
|
||||
expect(Object.keys(CommentsStore.state['a'].notes).length).toBe(2);
|
||||
|
||||
it('deletes discussion by ID', () => {
|
||||
CommentsStore.delete('a', 1);
|
||||
expect(Object.keys(CommentsStore.state).length).toBe(0);
|
||||
});
|
||||
CommentsStore.delete('a', 1);
|
||||
CommentsStore.delete('a', 2);
|
||||
expect(Object.keys(CommentsStore.state).length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes discussion when no more notes', () => {
|
||||
createDiscussion();
|
||||
createDiscussion(2);
|
||||
expect(Object.keys(CommentsStore.state).length).toBe(1);
|
||||
expect(Object.keys(CommentsStore.state['a'].notes).length).toBe(2);
|
||||
|
||||
CommentsStore.delete('a', 1);
|
||||
CommentsStore.delete('a', 2);
|
||||
expect(Object.keys(CommentsStore.state).length).toBe(0);
|
||||
});
|
||||
describe('Update note', () => {
|
||||
beforeEach(() => {
|
||||
expect(Object.keys(CommentsStore.state).length).toBe(0);
|
||||
createDiscussion();
|
||||
});
|
||||
|
||||
describe('Update note', () => {
|
||||
beforeEach(() => {
|
||||
expect(Object.keys(CommentsStore.state).length).toBe(0);
|
||||
createDiscussion();
|
||||
});
|
||||
it('updates note to be unresolved', () => {
|
||||
CommentsStore.update('a', 1, false, 'test');
|
||||
|
||||
it('updates note to be unresolved', () => {
|
||||
CommentsStore.update('a', 1, false, 'test');
|
||||
const note = CommentsStore.get('a', 1);
|
||||
expect(note.resolved).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
const note = CommentsStore.get('a', 1);
|
||||
expect(note.resolved).toBe(false);
|
||||
});
|
||||
describe('Discussion resolved', () => {
|
||||
beforeEach(() => {
|
||||
expect(Object.keys(CommentsStore.state).length).toBe(0);
|
||||
createDiscussion();
|
||||
});
|
||||
|
||||
describe('Discussion resolved', () => {
|
||||
beforeEach(() => {
|
||||
expect(Object.keys(CommentsStore.state).length).toBe(0);
|
||||
createDiscussion();
|
||||
});
|
||||
|
||||
it('is resolved with single note', () => {
|
||||
const discussion = CommentsStore.state['a'];
|
||||
expect(discussion.isResolved()).toBe(true);
|
||||
});
|
||||
|
||||
it('is unresolved with 2 notes', () => {
|
||||
const discussion = CommentsStore.state['a'];
|
||||
createDiscussion(2, false);
|
||||
|
||||
expect(discussion.isResolved()).toBe(false);
|
||||
});
|
||||
|
||||
it('is resolved with 2 notes', () => {
|
||||
const discussion = CommentsStore.state['a'];
|
||||
createDiscussion(2);
|
||||
|
||||
expect(discussion.isResolved()).toBe(true);
|
||||
});
|
||||
|
||||
it('resolve all notes', () => {
|
||||
const discussion = CommentsStore.state['a'];
|
||||
createDiscussion(2, false);
|
||||
|
||||
discussion.resolveAllNotes();
|
||||
expect(discussion.isResolved()).toBe(true);
|
||||
});
|
||||
|
||||
it('unresolve all notes', () => {
|
||||
const discussion = CommentsStore.state['a'];
|
||||
createDiscussion(2);
|
||||
|
||||
discussion.unResolveAllNotes();
|
||||
expect(discussion.isResolved()).toBe(false);
|
||||
});
|
||||
it('is resolved with single note', () => {
|
||||
const discussion = CommentsStore.state['a'];
|
||||
expect(discussion.isResolved()).toBe(true);
|
||||
});
|
||||
})();
|
||||
|
||||
it('is unresolved with 2 notes', () => {
|
||||
const discussion = CommentsStore.state['a'];
|
||||
createDiscussion(2, false);
|
||||
|
||||
expect(discussion.isResolved()).toBe(false);
|
||||
});
|
||||
|
||||
it('is resolved with 2 notes', () => {
|
||||
const discussion = CommentsStore.state['a'];
|
||||
createDiscussion(2);
|
||||
|
||||
expect(discussion.isResolved()).toBe(true);
|
||||
});
|
||||
|
||||
it('resolve all notes', () => {
|
||||
const discussion = CommentsStore.state['a'];
|
||||
createDiscussion(2, false);
|
||||
|
||||
discussion.resolveAllNotes();
|
||||
expect(discussion.isResolved()).toBe(true);
|
||||
});
|
||||
|
||||
it('unresolve all notes', () => {
|
||||
const discussion = CommentsStore.state['a'];
|
||||
createDiscussion(2);
|
||||
|
||||
discussion.unResolveAllNotes();
|
||||
expect(discussion.isResolved()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue