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