summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/notes
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/notes')
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue18
-rw-r--r--app/assets/javascripts/notes/components/note_actions.vue13
-rw-r--r--app/assets/javascripts/notes/components/note_actions/reply_button.vue12
-rw-r--r--app/assets/javascripts/notes/components/note_body.vue1
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue73
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue19
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue18
-rw-r--r--app/assets/javascripts/notes/stores/actions.js59
-rw-r--r--app/assets/javascripts/notes/stores/getters.js2
-rw-r--r--app/assets/javascripts/notes/stores/modules/index.js1
-rw-r--r--app/assets/javascripts/notes/stores/mutation_types.js1
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js11
12 files changed, 142 insertions, 86 deletions
diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue
index 376d4114efd..d8947e8ca50 100644
--- a/app/assets/javascripts/notes/components/diff_with_note.vue
+++ b/app/assets/javascripts/notes/components/diff_with_note.vue
@@ -5,6 +5,7 @@ import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue';
import { GlSkeletonLoading } from '@gitlab/ui';
import { getDiffMode } from '~/diffs/store/utils';
+import { diffViewerModes } from '~/ide/constants';
export default {
components: {
@@ -31,6 +32,12 @@ export default {
diffMode() {
return getDiffMode(this.discussion.diff_file);
},
+ diffViewerMode() {
+ return this.discussion.diff_file.viewer.name;
+ },
+ isTextFile() {
+ return this.diffViewerMode === diffViewerModes.text;
+ },
hasTruncatedDiffLines() {
return (
this.discussion.truncated_diff_lines && this.discussion.truncated_diff_lines.length !== 0
@@ -58,18 +65,14 @@ export default {
</script>
<template>
- <div :class="{ 'text-file': discussion.diff_file.text }" class="diff-file file-holder">
+ <div :class="{ 'text-file': isTextFile }" class="diff-file file-holder">
<diff-file-header
:discussion-path="discussion.discussion_path"
:diff-file="discussion.diff_file"
:can-current-user-fork="false"
- :expanded="!discussion.diff_file.collapsed"
+ :expanded="!discussion.diff_file.viewer.collapsed"
/>
- <div
- v-if="discussion.diff_file.text"
- :class="$options.userColorSchemeClass"
- class="diff-content code"
- >
+ <div v-if="isTextFile" :class="$options.userColorSchemeClass" class="diff-content code">
<table>
<template v-if="hasTruncatedDiffLines">
<tr
@@ -109,6 +112,7 @@ export default {
<div v-else>
<diff-viewer
:diff-mode="diffMode"
+ :diff-viewer-mode="diffViewerMode"
:new-path="discussion.diff_file.new_path"
:new-sha="discussion.diff_file.diff_refs.head_sha"
:old-path="discussion.diff_file.old_path"
diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue
index 91b9e5de374..de1ea0f58d6 100644
--- a/app/assets/javascripts/notes/components/note_actions.vue
+++ b/app/assets/javascripts/notes/components/note_actions.vue
@@ -23,11 +23,6 @@ export default {
type: [String, Number],
required: true,
},
- discussionId: {
- type: String,
- required: false,
- default: '',
- },
noteUrl: {
type: String,
required: false,
@@ -126,6 +121,11 @@ export default {
onResolve() {
this.$emit('handleResolve');
},
+ closeTooltip() {
+ this.$nextTick(() => {
+ this.$root.$emit('bv::hide::tooltip');
+ });
+ },
},
};
</script>
@@ -171,7 +171,7 @@ export default {
v-if="showReplyButton"
ref="replyButton"
class="js-reply-button"
- :note-id="discussionId"
+ @startReplying="$emit('startReplying')"
/>
<div v-if="canEdit" class="note-actions-item">
<button
@@ -202,6 +202,7 @@ export default {
title="More actions"
class="note-action-button more-actions-toggle btn btn-transparent"
data-toggle="dropdown"
+ @click="closeTooltip"
>
<icon css-classes="icon" name="ellipsis_v" />
</button>
diff --git a/app/assets/javascripts/notes/components/note_actions/reply_button.vue b/app/assets/javascripts/notes/components/note_actions/reply_button.vue
index b2f9d7f128a..f50cab81efe 100644
--- a/app/assets/javascripts/notes/components/note_actions/reply_button.vue
+++ b/app/assets/javascripts/notes/components/note_actions/reply_button.vue
@@ -1,5 +1,4 @@
<script>
-import { mapActions } from 'vuex';
import { GlTooltipDirective, GlButton } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
@@ -12,15 +11,6 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
- props: {
- noteId: {
- type: String,
- required: true,
- },
- },
- methods: {
- ...mapActions(['convertToDiscussion']),
- },
};
</script>
@@ -32,7 +22,7 @@ export default {
class="note-action-button"
variant="transparent"
:title="__('Reply to comment')"
- @click="convertToDiscussion(noteId)"
+ @click="$emit('startReplying')"
>
<icon name="comment" css-classes="link-highlight" />
</gl-button>
diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue
index ff303d0f55a..fb1d98355b3 100644
--- a/app/assets/javascripts/notes/components/note_body.vue
+++ b/app/assets/javascripts/notes/components/note_body.vue
@@ -95,6 +95,7 @@ export default {
<div ref="note-body" :class="{ 'js-task-list-container': canEdit }" class="note-body">
<suggestions
v-if="hasSuggestion && !isEditing"
+ class="note-text md"
:suggestions="note.suggestions"
:note-html="note.note_html"
:line-type="lineType"
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index b7e9f7c2028..2d6fd8b116f 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -26,6 +26,7 @@ import resolvable from '../mixins/resolvable';
import discussionNavigation from '../mixins/discussion_navigation';
import ReplyPlaceholder from './discussion_reply_placeholder.vue';
import jumpToNextDiscussionButton from './discussion_jump_to_next_button.vue';
+import eventHub from '../event_hub';
export default {
name: 'NoteableDiscussion',
@@ -93,6 +94,7 @@ export default {
},
computed: {
...mapGetters([
+ 'convertedDisscussionIds',
'getNoteableData',
'nextUnresolvedDiscussionId',
'unresolvedDiscussionsCount',
@@ -245,6 +247,12 @@ export default {
}
},
},
+ created() {
+ eventHub.$on('startReplying', this.onStartReplying);
+ },
+ beforeDestroy() {
+ eventHub.$off('startReplying', this.onStartReplying);
+ },
methods: {
...mapActions([
'saveNote',
@@ -252,6 +260,7 @@ export default {
'removePlaceholderNotes',
'toggleResolveNote',
'expandDiscussion',
+ 'removeConvertedDiscussion',
]),
truncateSha,
componentName(note) {
@@ -291,6 +300,10 @@ export default {
}
}
+ if (this.convertedDisscussionIds.includes(this.discussion.id)) {
+ this.removeConvertedDiscussion(this.discussion.id);
+ }
+
this.isReplying = false;
this.resetAutoSave();
},
@@ -301,6 +314,10 @@ export default {
note: { note: noteText },
};
+ if (this.convertedDisscussionIds.includes(this.discussion.id)) {
+ postData.return_discussion = true;
+ }
+
if (this.discussion.for_commit) {
postData.note_project_id = this.discussion.project_id;
}
@@ -340,6 +357,11 @@ Please check your network connection and try again.`;
deleteNoteHandler(note) {
this.$emit('noteDeleted', this.discussion, note);
},
+ onStartReplying(discussionId) {
+ if (this.discussion.id === discussionId) {
+ this.showReplyForm();
+ }
+ },
},
};
</script>
@@ -358,30 +380,32 @@ Please check your network connection and try again.`;
:img-size="40"
/>
</div>
- <note-header
- :author="author"
- :created-at="initialDiscussion.created_at"
- :note-id="initialDiscussion.id"
- :include-toggle="true"
- :expanded="discussion.expanded"
- @toggleHandler="toggleDiscussionHandler"
- >
- <span v-html="actionText"></span>
- </note-header>
- <note-edited-text
- v-if="discussion.resolved"
- :edited-at="discussion.resolved_at"
- :edited-by="discussion.resolved_by"
- :action-text="resolvedText"
- class-name="discussion-headline-light js-discussion-headline"
- />
- <note-edited-text
- v-else-if="lastUpdatedAt"
- :edited-at="lastUpdatedAt"
- :edited-by="lastUpdatedBy"
- action-text="Last updated"
- class-name="discussion-headline-light js-discussion-headline"
- />
+ <div class="timeline-content">
+ <note-header
+ :author="author"
+ :created-at="initialDiscussion.created_at"
+ :note-id="initialDiscussion.id"
+ :include-toggle="true"
+ :expanded="discussion.expanded"
+ @toggleHandler="toggleDiscussionHandler"
+ >
+ <span v-html="actionText"></span>
+ </note-header>
+ <note-edited-text
+ v-if="discussion.resolved"
+ :edited-at="discussion.resolved_at"
+ :edited-by="discussion.resolved_by"
+ :action-text="resolvedText"
+ class-name="discussion-headline-light js-discussion-headline"
+ />
+ <note-edited-text
+ v-else-if="lastUpdatedAt"
+ :edited-at="lastUpdatedAt"
+ :edited-by="lastUpdatedBy"
+ action-text="Last updated"
+ class-name="discussion-headline-light js-discussion-headline"
+ />
+ </div>
</div>
<div v-if="shouldShowDiscussions" class="discussion-body">
<component
@@ -400,6 +424,7 @@ Please check your network connection and try again.`;
:help-page-path="helpPagePath"
:show-reply-button="canReply"
@handleDeleteNote="deleteNoteHandler"
+ @startReplying="showReplyForm"
>
<note-edited-text
v-if="discussion.resolved"
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index 56108a58010..04e74a43acc 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -29,11 +29,6 @@ export default {
type: Object,
required: true,
},
- discussion: {
- type: Object,
- required: false,
- default: null,
- },
line: {
type: Object,
required: false,
@@ -49,6 +44,11 @@ export default {
required: false,
default: () => null,
},
+ showReplyButton: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -91,13 +91,6 @@ export default {
}
return '';
},
- showReplyButton() {
- if (!this.discussion || !this.getNoteableData.current_user.can_create_note) {
- return false;
- }
-
- return this.discussion.individual_note && !this.commentsDisabled;
- },
actionText() {
if (!this.commit) {
return '';
@@ -260,10 +253,10 @@ export default {
:is-resolved="note.resolved"
:is-resolving="isResolving"
:resolved-by="note.resolved_by"
- :discussion-id="discussionId"
@handleEdit="editHandler"
@handleDelete="deleteHandler"
@handleResolve="resolveHandler"
+ @startReplying="$emit('startReplying')"
/>
</div>
<div class="timeline-discussion-body">
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index 6d72b72e628..a63571edcea 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -60,9 +60,11 @@ export default {
...mapGetters([
'isNotesFetched',
'discussions',
+ 'convertedDisscussionIds',
'getNotesDataByProp',
'isLoading',
'commentsDisabled',
+ 'getNoteableData',
]),
noteableType() {
return this.noteableData.noteableType;
@@ -78,6 +80,9 @@ export default {
return this.discussions;
},
+ canReply() {
+ return this.getNoteableData.current_user.can_create_note && !this.commentsDisabled;
+ },
},
watch: {
shouldShow() {
@@ -128,6 +133,7 @@ export default {
'setNotesFetchedState',
'expandDiscussion',
'startTaskList',
+ 'convertToDiscussion',
]),
fetchNotes() {
if (this.isFetching) return null;
@@ -175,6 +181,11 @@ export default {
}
}
},
+ startReplying(discussionId) {
+ return this.convertToDiscussion(discussionId)
+ .then(() => this.$nextTick())
+ .then(() => eventHub.$emit('startReplying', discussionId));
+ },
},
systemNote: constants.SYSTEM_NOTE,
};
@@ -193,7 +204,9 @@ export default {
/>
<placeholder-note v-else :key="discussion.id" :note="discussion.notes[0]" />
</template>
- <template v-else-if="discussion.individual_note">
+ <template
+ v-else-if="discussion.individual_note && !convertedDisscussionIds.includes(discussion.id)"
+ >
<system-note
v-if="discussion.notes[0].system"
:key="discussion.id"
@@ -203,7 +216,8 @@ export default {
v-else
:key="discussion.id"
:note="discussion.notes[0]"
- :discussion="discussion"
+ :show-reply-button="canReply"
+ @startReplying="startReplying(discussion.id)"
/>
</template>
<noteable-discussion
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index ff65f14d529..1a0dba69a7c 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -83,12 +83,44 @@ export const updateNote = ({ commit, dispatch }, { endpoint, note }) =>
dispatch('startTaskList');
});
-export const replyToDiscussion = ({ commit }, { endpoint, data }) =>
+export const updateOrCreateNotes = ({ commit, state, getters, dispatch }, notes) => {
+ const { notesById } = getters;
+
+ notes.forEach(note => {
+ if (notesById[note.id]) {
+ commit(types.UPDATE_NOTE, note);
+ } else if (note.type === constants.DISCUSSION_NOTE || note.type === constants.DIFF_NOTE) {
+ const discussion = utils.findNoteObjectById(state.discussions, note.discussion_id);
+
+ if (discussion) {
+ commit(types.ADD_NEW_REPLY_TO_DISCUSSION, note);
+ } else if (note.type === constants.DIFF_NOTE) {
+ dispatch('fetchDiscussions', { path: state.notesData.discussionsPath });
+ } else {
+ commit(types.ADD_NEW_NOTE, note);
+ }
+ } else {
+ commit(types.ADD_NEW_NOTE, note);
+ }
+ });
+};
+
+export const replyToDiscussion = ({ commit, state, getters, dispatch }, { endpoint, data }) =>
service
.replyToDiscussion(endpoint, data)
.then(res => res.json())
.then(res => {
- commit(types.ADD_NEW_REPLY_TO_DISCUSSION, res);
+ if (res.discussion) {
+ commit(types.UPDATE_DISCUSSION, res.discussion);
+
+ updateOrCreateNotes({ commit, state, getters, dispatch }, res.discussion.notes);
+
+ dispatch('updateMergeRequestWidget');
+ dispatch('startTaskList');
+ dispatch('updateResolvableDiscussonsCounts');
+ } else {
+ commit(types.ADD_NEW_REPLY_TO_DISCUSSION, res);
+ }
return res;
});
@@ -262,25 +294,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
const pollSuccessCallBack = (resp, commit, state, getters, dispatch) => {
if (resp.notes && resp.notes.length) {
- const { notesById } = getters;
-
- resp.notes.forEach(note => {
- if (notesById[note.id]) {
- commit(types.UPDATE_NOTE, note);
- } else if (note.type === constants.DISCUSSION_NOTE || note.type === constants.DIFF_NOTE) {
- const discussion = utils.findNoteObjectById(state.discussions, note.discussion_id);
-
- if (discussion) {
- commit(types.ADD_NEW_REPLY_TO_DISCUSSION, note);
- } else if (note.type === constants.DIFF_NOTE) {
- dispatch('fetchDiscussions', { path: state.notesData.discussionsPath });
- } else {
- commit(types.ADD_NEW_NOTE, note);
- }
- } else {
- commit(types.ADD_NEW_NOTE, note);
- }
- });
+ updateOrCreateNotes({ commit, state, getters, dispatch }, resp.notes);
dispatch('startTaskList');
}
@@ -429,5 +443,8 @@ export const submitSuggestion = (
export const convertToDiscussion = ({ commit }, noteId) =>
commit(types.CONVERT_TO_DISCUSSION, noteId);
+export const removeConvertedDiscussion = ({ commit }, noteId) =>
+ commit(types.REMOVE_CONVERTED_DISCUSSION, noteId);
+
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/notes/stores/getters.js b/app/assets/javascripts/notes/stores/getters.js
index 0ffc0cb2593..5026c13dab5 100644
--- a/app/assets/javascripts/notes/stores/getters.js
+++ b/app/assets/javascripts/notes/stores/getters.js
@@ -4,6 +4,8 @@ import { collapseSystemNotes } from './collapse_utils';
export const discussions = state => collapseSystemNotes(state.discussions);
+export const convertedDisscussionIds = state => state.convertedDisscussionIds;
+
export const targetNoteHash = state => state.targetNoteHash;
export const getNotesData = state => state.notesData;
diff --git a/app/assets/javascripts/notes/stores/modules/index.js b/app/assets/javascripts/notes/stores/modules/index.js
index 887e6d22b06..6168aeae35d 100644
--- a/app/assets/javascripts/notes/stores/modules/index.js
+++ b/app/assets/javascripts/notes/stores/modules/index.js
@@ -5,6 +5,7 @@ import mutations from '../mutations';
export default () => ({
state: {
discussions: [],
+ convertedDisscussionIds: [],
targetNoteHash: null,
lastFetchedAt: null,
diff --git a/app/assets/javascripts/notes/stores/mutation_types.js b/app/assets/javascripts/notes/stores/mutation_types.js
index 2bffedad336..796370920bb 100644
--- a/app/assets/javascripts/notes/stores/mutation_types.js
+++ b/app/assets/javascripts/notes/stores/mutation_types.js
@@ -18,6 +18,7 @@ export const SET_NOTES_LOADING_STATE = 'SET_NOTES_LOADING_STATE';
export const DISABLE_COMMENTS = 'DISABLE_COMMENTS';
export const APPLY_SUGGESTION = 'APPLY_SUGGESTION';
export const CONVERT_TO_DISCUSSION = 'CONVERT_TO_DISCUSSION';
+export const REMOVE_CONVERTED_DISCUSSION = 'REMOVE_CONVERTED_DISCUSSION';
// DISCUSSION
export const COLLAPSE_DISCUSSION = 'COLLAPSE_DISCUSSION';
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index d167f8ef421..ae6f8b7790a 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -266,7 +266,14 @@ export default {
},
[types.CONVERT_TO_DISCUSSION](state, discussionId) {
- const discussion = utils.findNoteObjectById(state.discussions, discussionId);
- Object.assign(discussion, { individual_note: false });
+ const convertedDisscussionIds = [...state.convertedDisscussionIds, discussionId];
+ Object.assign(state, { convertedDisscussionIds });
+ },
+
+ [types.REMOVE_CONVERTED_DISCUSSION](state, discussionId) {
+ const convertedDisscussionIds = [...state.convertedDisscussionIds];
+
+ convertedDisscussionIds.splice(convertedDisscussionIds.indexOf(discussionId), 1);
+ Object.assign(state, { convertedDisscussionIds });
},
};