summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/notes/components/noteable_note.vue
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/notes/components/noteable_note.vue')
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue98
1 files changed, 96 insertions, 2 deletions
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index 37675e20b3d..0e4dd1b9c84 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -2,7 +2,8 @@
import $ from 'jquery';
import { mapGetters, mapActions } from 'vuex';
import { escape } from 'lodash';
-import draftMixin from 'ee_else_ce/notes/mixins/draft';
+import { GlSprintf } from '@gitlab/ui';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { truncateSha } from '~/lib/utils/text_utility';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import { __, s__, sprintf } from '../../locale';
@@ -15,17 +16,26 @@ import eventHub from '../event_hub';
import noteable from '../mixins/noteable';
import resolvable from '../mixins/resolvable';
import httpStatusCodes from '~/lib/utils/http_status';
+import {
+ getStartLineNumber,
+ getEndLineNumber,
+ getLineClasses,
+ commentLineOptions,
+} from './multiline_comment_utils';
+import MultilineCommentForm from './multiline_comment_form.vue';
export default {
name: 'NoteableNote',
components: {
+ GlSprintf,
userAvatarLink,
noteHeader,
noteActions,
NoteBody,
TimelineEntryItem,
+ MultilineCommentForm,
},
- mixins: [noteable, resolvable, draftMixin],
+ mixins: [noteable, resolvable, glFeatureFlagsMixin()],
props: {
note: {
type: Object,
@@ -51,6 +61,11 @@ export default {
required: false,
default: false,
},
+ diffLines: {
+ type: Object,
+ required: false,
+ default: null,
+ },
},
data() {
return {
@@ -58,9 +73,14 @@ export default {
isDeleting: false,
isRequesting: false,
isResolving: false,
+ commentLineStart: {
+ line_code: this.line?.line_code,
+ type: this.line?.type,
+ },
};
},
computed: {
+ ...mapGetters('diffs', ['getDiffFileByHash']),
...mapGetters(['targetNoteHash', 'getNoteableData', 'getUserData', 'commentsDisabled']),
author() {
return this.note.author;
@@ -105,6 +125,41 @@ export default {
)}</a>`;
return sprintf(s__('MergeRequests|commented on commit %{commitLink}'), { commitLink }, false);
},
+ isDraft() {
+ return this.note.isDraft;
+ },
+ canResolve() {
+ return (
+ this.note.current_user.can_resolve ||
+ (this.note.isDraft && this.note.discussion_id !== null)
+ );
+ },
+ lineRange() {
+ return this.note.position?.line_range;
+ },
+ startLineNumber() {
+ return getStartLineNumber(this.lineRange);
+ },
+ endLineNumber() {
+ return getEndLineNumber(this.lineRange);
+ },
+ showMultiLineComment() {
+ return (
+ this.glFeatures.multilineComments &&
+ this.startLineNumber &&
+ this.endLineNumber &&
+ (this.startLineNumber !== this.endLineNumber || this.isEditing)
+ );
+ },
+ commentLineOptions() {
+ if (this.diffLines) {
+ return commentLineOptions(this.diffLines, this.line.line_code);
+ }
+
+ const diffFile = this.diffFile || this.getDiffFileByHash(this.targetNoteHash);
+ if (!diffFile) return null;
+ return commentLineOptions(diffFile.highlighted_diff_lines, this.line.line_code);
+ },
},
created() {
@@ -129,6 +184,7 @@ export default {
'updateNote',
'toggleResolveNote',
'scrollToNoteIfNeeded',
+ 'updateAssignees',
]),
editHandler() {
this.isEditing = true;
@@ -166,10 +222,20 @@ export default {
this.$emit('updateSuccess');
},
formUpdateHandler(noteText, parentElement, callback, resolveDiscussion) {
+ const position = {
+ ...this.note.position,
+ line_range: {
+ start_line_code: this.commentLineStart?.lineCode,
+ start_line_type: this.commentLineStart?.type,
+ end_line_code: this.line?.line_code,
+ end_line_type: this.line?.type,
+ },
+ };
this.$emit('handleUpdateNote', {
note: this.note,
noteText,
resolveDiscussion,
+ position,
callback: () => this.updateSuccess(),
});
@@ -231,6 +297,12 @@ export default {
noteBody.note.note = noteText;
}
},
+ getLineClasses(lineNumber) {
+ return getLineClasses(lineNumber);
+ },
+ assigneesUpdate(assignees) {
+ this.updateAssignees(assignees);
+ },
},
};
</script>
@@ -243,6 +315,26 @@ export default {
:data-note-id="note.id"
class="note note-wrapper qa-noteable-note-item"
>
+ <div v-if="showMultiLineComment" data-testid="multiline-comment">
+ <multiline-comment-form
+ v-if="isEditing && commentLineOptions && line"
+ v-model="commentLineStart"
+ :line="line"
+ :comment-line-options="commentLineOptions"
+ :line-range="note.position.line_range"
+ class="gl-mb-3 gl-text-gray-700 gl-border-gray-200 gl-border-b-solid gl-border-b-1 gl-pb-3"
+ />
+ <div v-else class="gl-mb-3 gl-text-gray-700">
+ <gl-sprintf :message="__('Comment on lines %{startLine} to %{endLine}')">
+ <template #startLine>
+ <span :class="getLineClasses(startLineNumber)">{{ startLineNumber }}</span>
+ </template>
+ <template #endLine>
+ <span :class="getLineClasses(endLineNumber)">{{ endLineNumber }}</span>
+ </template>
+ </gl-sprintf>
+ </div>
+ </div>
<div v-once class="timeline-icon">
<user-avatar-link
:link-href="author.path"
@@ -267,6 +359,7 @@ export default {
<span v-else-if="note.created_at" class="d-none d-sm-inline">&middot;</span>
</note-header>
<note-actions
+ :author="author"
:author-id="author.id"
:note-id="note.id"
:note-url="note.noteable_note_url"
@@ -289,6 +382,7 @@ export default {
@handleDelete="deleteHandler"
@handleResolve="resolveHandler"
@startReplying="$emit('startReplying')"
+ @updateAssignees="assigneesUpdate"
/>
</div>
<div class="timeline-discussion-body">