1<script>
2import { mapGetters, mapActions } from 'vuex';
3import { __ } from '~/locale';
4import PlaceholderNote from '~/vue_shared/components/notes/placeholder_note.vue';
5import PlaceholderSystemNote from '~/vue_shared/components/notes/placeholder_system_note.vue';
6import SystemNote from '~/vue_shared/components/notes/system_note.vue';
7import { SYSTEM_NOTE } from '../constants';
8import DiscussionNotesRepliesWrapper from './discussion_notes_replies_wrapper.vue';
9import NoteEditedText from './note_edited_text.vue';
10import NoteableNote from './noteable_note.vue';
11import ToggleRepliesWidget from './toggle_replies_widget.vue';
12
13export default {
14  name: 'DiscussionNotes',
15  components: {
16    ToggleRepliesWidget,
17    NoteEditedText,
18    DiscussionNotesRepliesWrapper,
19  },
20  props: {
21    discussion: {
22      type: Object,
23      required: true,
24    },
25    isExpanded: {
26      type: Boolean,
27      required: false,
28      default: false,
29    },
30    diffLine: {
31      type: Object,
32      required: false,
33      default: null,
34    },
35    line: {
36      type: Object,
37      required: false,
38      default: null,
39    },
40    shouldGroupReplies: {
41      type: Boolean,
42      required: false,
43      default: false,
44    },
45    helpPagePath: {
46      type: String,
47      required: false,
48      default: '',
49    },
50    isOverviewTab: {
51      type: Boolean,
52      required: false,
53      default: false,
54    },
55  },
56  computed: {
57    ...mapGetters(['userCanReply']),
58    hasReplies() {
59      return Boolean(this.replies.length);
60    },
61    replies() {
62      return this.discussion.notes.slice(1);
63    },
64    firstNote() {
65      return this.discussion.notes.slice(0, 1)[0];
66    },
67    resolvedText() {
68      return this.discussion.resolved_by_push ? __('Automatically resolved') : __('Resolved');
69    },
70    commit() {
71      if (!this.discussion.for_commit) {
72        return null;
73      }
74
75      return {
76        id: this.discussion.commit_id,
77        url: this.discussion.discussion_path,
78      };
79    },
80  },
81  methods: {
82    ...mapActions(['toggleDiscussion', 'setSelectedCommentPositionHover']),
83    componentName(note) {
84      if (note.isPlaceholderNote) {
85        if (note.placeholderType === SYSTEM_NOTE) {
86          return PlaceholderSystemNote;
87        }
88
89        return PlaceholderNote;
90      }
91
92      if (note.system) {
93        return SystemNote;
94      }
95
96      return NoteableNote;
97    },
98    componentData(note) {
99      return note.isPlaceholderNote ? note.notes[0] : note;
100    },
101    handleMouseEnter(discussion) {
102      if (discussion.position) {
103        this.setSelectedCommentPositionHover(discussion.position.line_range);
104      }
105    },
106    handleMouseLeave(discussion) {
107      // Even though position isn't used here we still don't want to unnecessarily call a mutation
108      // The lack of position tells us that highlighting is irrelevant in this context
109      if (discussion.position) {
110        this.setSelectedCommentPositionHover();
111      }
112    },
113  },
114};
115</script>
116
117<template>
118  <div class="discussion-notes">
119    <ul
120      class="notes"
121      @mouseenter="handleMouseEnter(discussion)"
122      @mouseleave="handleMouseLeave(discussion)"
123    >
124      <template v-if="shouldGroupReplies">
125        <component
126          :is="componentName(firstNote)"
127          :note="componentData(firstNote)"
128          :line="line || diffLine"
129          :discussion-file="discussion.diff_file"
130          :commit="commit"
131          :help-page-path="helpPagePath"
132          :show-reply-button="userCanReply"
133          :discussion-root="true"
134          :discussion-resolve-path="discussion.resolve_path"
135          :is-overview-tab="isOverviewTab"
136          @handleDeleteNote="$emit('deleteNote')"
137          @startReplying="$emit('startReplying')"
138        >
139          <template #discussion-resolved-text>
140            <note-edited-text
141              v-if="discussion.resolved"
142              :edited-at="discussion.resolved_at"
143              :edited-by="discussion.resolved_by"
144              :action-text="resolvedText"
145              class-name="discussion-headline-light js-discussion-headline discussion-resolved-text"
146            />
147          </template>
148          <template #avatar-badge>
149            <slot name="avatar-badge"></slot>
150          </template>
151        </component>
152        <discussion-notes-replies-wrapper :is-diff-discussion="discussion.diff_discussion">
153          <toggle-replies-widget
154            v-if="hasReplies"
155            :collapsed="!isExpanded"
156            :replies="replies"
157            :class="{ 'discussion-toggle-replies': discussion.diff_discussion }"
158            @toggle="toggleDiscussion({ discussionId: discussion.id })"
159          />
160          <template v-if="isExpanded">
161            <component
162              :is="componentName(note)"
163              v-for="note in replies"
164              :key="note.id"
165              :note="componentData(note)"
166              :help-page-path="helpPagePath"
167              :line="line"
168              @handleDeleteNote="$emit('deleteNote')"
169            />
170          </template>
171          <slot :show-replies="isExpanded || !hasReplies" name="footer"></slot>
172        </discussion-notes-replies-wrapper>
173      </template>
174      <template v-else>
175        <component
176          :is="componentName(note)"
177          v-for="(note, index) in discussion.notes"
178          :key="note.id"
179          :note="componentData(note)"
180          :discussion-file="discussion.diff_file"
181          :help-page-path="helpPagePath"
182          :line="diffLine"
183          :discussion-root="index === 0"
184          :discussion-resolve-path="discussion.resolve_path"
185          :is-overview-tab="isOverviewTab"
186          @handleDeleteNote="$emit('deleteNote')"
187        >
188          <template #avatar-badge>
189            <slot v-if="index === 0" name="avatar-badge"></slot>
190          </template>
191        </component>
192        <slot :show-replies="isExpanded || !hasReplies" name="footer"></slot>
193      </template>
194    </ul>
195  </div>
196</template>
197