diff options
Diffstat (limited to 'app/assets/javascripts/notes/components/noteable_discussion.vue')
-rw-r--r-- | app/assets/javascripts/notes/components/noteable_discussion.vue | 273 |
1 files changed, 91 insertions, 182 deletions
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue index 3894dc8c677..eb6a4a67fff 100644 --- a/app/assets/javascripts/notes/components/noteable_discussion.vue +++ b/app/assets/javascripts/notes/components/noteable_discussion.vue @@ -4,55 +4,42 @@ import { mapActions, mapGetters } from 'vuex'; import { GlTooltipDirective } from '@gitlab/ui'; import { truncateSha } from '~/lib/utils/text_utility'; import { s__, __, sprintf } from '~/locale'; -import systemNote from '~/vue_shared/components/notes/system_note.vue'; +import { clearDraft, getDiscussionReplyKey } from '~/lib/utils/autosave'; import icon from '~/vue_shared/components/icon.vue'; +import diffLineNoteFormMixin from 'ee_else_ce/notes/mixins/diff_line_note_form'; import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue'; import Flash from '../../flash'; -import { SYSTEM_NOTE } from '../constants'; import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; -import noteableNote from './noteable_note.vue'; import noteHeader from './note_header.vue'; -import resolveDiscussionButton from './discussion_resolve_button.vue'; -import toggleRepliesWidget from './toggle_replies_widget.vue'; import noteSignedOutWidget from './note_signed_out_widget.vue'; import noteEditedText from './note_edited_text.vue'; import noteForm from './note_form.vue'; import diffWithNote from './diff_with_note.vue'; -import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue'; -import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue'; -import autosave from '../mixins/autosave'; import noteable from '../mixins/noteable'; import resolvable from '../mixins/resolvable'; import discussionNavigation from '../mixins/discussion_navigation'; -import ReplyPlaceholder from './discussion_reply_placeholder.vue'; -import ResolveWithIssueButton from './discussion_resolve_with_issue_button.vue'; -import jumpToNextDiscussionButton from './discussion_jump_to_next_button.vue'; import eventHub from '../event_hub'; +import DiscussionNotes from './discussion_notes.vue'; +import DiscussionActions from './discussion_actions.vue'; export default { name: 'NoteableDiscussion', components: { icon, - noteableNote, userAvatarLink, noteHeader, noteSignedOutWidget, noteEditedText, noteForm, - resolveDiscussionButton, - jumpToNextDiscussionButton, - toggleRepliesWidget, - ReplyPlaceholder, - placeholderNote, - placeholderSystemNote, - ResolveWithIssueButton, - systemNote, + DraftNote: () => import('ee_component/batch_comments/components/draft_note.vue'), TimelineEntryItem, + DiscussionNotes, + DiscussionActions, }, directives: { GlTooltip: GlTooltipDirective, }, - mixins: [autosave, noteable, resolvable, discussionNavigation], + mixins: [noteable, resolvable, discussionNavigation, diffLineNoteFormMixin], props: { discussion: { type: Object, @@ -85,42 +72,38 @@ export default { }, }, data() { - const { diff_discussion: isDiffDiscussion, resolved } = this.discussion; - return { isReplying: false, isResolving: false, resolveAsThread: true, - isRepliesCollapsed: Boolean(!isDiffDiscussion && resolved), }; }, computed: { ...mapGetters([ 'convertedDisscussionIds', 'getNoteableData', + 'userCanReply', 'nextUnresolvedDiscussionId', 'unresolvedDiscussionsCount', 'hasUnresolvedDiscussions', 'showJumpToNextDiscussion', + 'getUserData', ]), + currentUser() { + return this.getUserData; + }, author() { - return this.initialDiscussion.author; + return this.firstNote.author; }, - canReply() { - return this.getNoteableData.current_user.can_create_note; + autosaveKey() { + return getDiscussionReplyKey(this.firstNote.noteable_type, this.discussion.id); }, newNotePath() { return this.getNoteableData.create_note_path; }, - hasReplies() { - return this.discussion.notes.length > 1; - }, - initialDiscussion() { + firstNote() { return this.discussion.notes.slice(0, 1)[0]; }, - replies() { - return this.discussion.notes.slice(1); - }, lastUpdatedBy() { const { notes } = this.discussion; @@ -173,11 +156,11 @@ export default { return ''; }, - shouldShowDiscussions() { - const { expanded, resolved } = this.discussion; - const isResolvedNonDiffDiscussion = !this.discussion.diff_discussion && resolved; - - return expanded || this.alwaysExpanded || isResolvedNonDiffDiscussion; + isExpanded() { + return this.discussion.expanded || this.alwaysExpanded; + }, + shouldHideDiscussionBody() { + return this.shouldRenderDiffs && !this.isExpanded; }, actionText() { const linkStart = `<a href="${_.escape(this.discussion.discussion_path)}">`; @@ -226,30 +209,8 @@ export default { return null; }, - commit() { - if (!this.discussion.for_commit) { - return null; - } - - return { - id: this.discussion.commit_id, - url: this.discussion.discussion_path, - }; - }, resolveWithIssuePath() { - return !this.discussionResolved && this.discussion.resolve_with_issue_path; - }, - }, - watch: { - isReplying() { - if (this.isReplying) { - this.$nextTick(() => { - // Pass an extra key to separate reply and note edit forms - this.initAutoSave({ ...this.initialDiscussion, ...this.discussion }, ['Reply']); - }); - } else { - this.disposeAutoSave(); - } + return !this.discussionResolved ? this.discussion.resolve_with_issue_path : ''; }, }, created() { @@ -268,30 +229,9 @@ export default { 'removeConvertedDiscussion', ]), truncateSha, - componentName(note) { - if (note.isPlaceholderNote) { - if (note.placeholderType === SYSTEM_NOTE) { - return placeholderSystemNote; - } - - return placeholderNote; - } - - if (note.system) { - return systemNote; - } - - return noteableNote; - }, - componentData(note) { - return note.isPlaceholderNote ? note.notes[0] : note; - }, toggleDiscussionHandler() { this.toggleDiscussion({ discussionId: this.discussion.id }); }, - toggleReplies() { - this.isRepliesCollapsed = !this.isRepliesCollapsed; - }, showReplyForm() { this.isReplying = true; }, @@ -310,7 +250,7 @@ export default { } this.isReplying = false; - this.resetAutoSave(); + clearDraft(this.autosaveKey); }, saveReply(noteText, form, callback) { const postData = { @@ -336,7 +276,7 @@ export default { this.isReplying = false; this.saveNote(replyData) .then(() => { - this.resetAutoSave(); + clearDraft(this.autosaveKey); callback(); }) .catch(err => { @@ -388,8 +328,8 @@ Please check your network connection and try again.`; <div class="timeline-content"> <note-header :author="author" - :created-at="initialDiscussion.created_at" - :note-id="initialDiscussion.id" + :created-at="firstNote.created_at" + :note-id="firstNote.id" :include-toggle="true" :expanded="discussion.expanded" @toggleHandler="toggleDiscussionHandler" @@ -412,110 +352,79 @@ Please check your network connection and try again.`; /> </div> </div> - <div v-if="shouldShowDiscussions" class="discussion-body"> + <div v-if="!shouldHideDiscussionBody" class="discussion-body"> <component :is="wrapperComponent" v-bind="wrapperComponentProps" class="card discussion-wrapper" > - <div class="discussion-notes"> - <ul class="notes"> - <template v-if="shouldGroupReplies"> - <component - :is="componentName(initialDiscussion)" - :note="componentData(initialDiscussion)" - :line="line" - :commit="commit" - :help-page-path="helpPagePath" - :show-reply-button="canReply" - @handleDeleteNote="deleteNoteHandler" - @startReplying="showReplyForm" - > - <note-edited-text - v-if="discussion.resolved" - slot="discussion-resolved-text" - :edited-at="discussion.resolved_at" - :edited-by="discussion.resolved_by" - :action-text="resolvedText" - class-name="discussion-headline-light js-discussion-headline discussion-resolved-text" - /> - <slot slot="avatar-badge" name="avatar-badge"></slot> - </component> - <toggle-replies-widget - v-if="hasReplies" - :collapsed="isRepliesCollapsed" - :replies="replies" - @toggle="toggleReplies" + <discussion-notes + :discussion="discussion" + :diff-line="diffLine" + :help-page-path="helpPagePath" + :is-expanded="isExpanded" + :line="line" + :should-group-replies="shouldGroupReplies" + @startReplying="showReplyForm" + @toggleDiscussion="toggleDiscussionHandler" + @deleteNote="deleteNoteHandler" + > + <slot slot="avatar-badge" name="avatar-badge"></slot> + <template #footer="{ showReplies }"> + <draft-note + v-if="showDraft(discussion.reply_id)" + :key="`draft_${discussion.id}`" + :draft="draftForDiscussion(discussion.reply_id)" + /> + <div + v-else-if="showReplies" + :class="{ 'is-replying': isReplying }" + class="discussion-reply-holder" + > + <user-avatar-link + v-if="!isReplying && currentUser" + :link-href="currentUser.path" + :img-src="currentUser.avatar_url" + :img-alt="currentUser.name" + :img-size="40" + class="d-none d-sm-block" + /> + <discussion-actions + v-if="!isReplying && userCanReply" + :discussion="discussion" + :is-resolving="isResolving" + :resolve-button-title="resolveButtonTitle" + :resolve-with-issue-path="resolveWithIssuePath" + :should-show-jump-to-next-discussion="shouldShowJumpToNextDiscussion" + @showReplyForm="showReplyForm" + @resolve="resolveHandler" + @jumpToNextDiscussion="jumpToNextDiscussion" /> - <template v-if="!isRepliesCollapsed"> - <component - :is="componentName(note)" - v-for="note in replies" - :key="note.id" - :note="componentData(note)" - :help-page-path="helpPagePath" - :line="line" - @handleDeleteNote="deleteNoteHandler" + <div v-if="isReplying" class="avatar-note-form-holder"> + <user-avatar-link + v-if="currentUser" + :link-href="currentUser.path" + :img-src="currentUser.avatar_url" + :img-alt="currentUser.name" + :img-size="40" + class="d-none d-sm-block" /> - </template> - </template> - <template v-else> - <component - :is="componentName(note)" - v-for="(note, index) in discussion.notes" - :key="note.id" - :note="componentData(note)" - :help-page-path="helpPagePath" - :line="diffLine" - @handleDeleteNote="deleteNoteHandler" - > - <slot v-if="index === 0" slot="avatar-badge" name="avatar-badge"></slot> - </component> - </template> - </ul> - <div - v-if="!isRepliesCollapsed || !hasReplies" - :class="{ 'is-replying': isReplying }" - class="discussion-reply-holder" - > - <template v-if="!isReplying && canReply"> - <div class="discussion-with-resolve-btn"> - <reply-placeholder class="qa-discussion-reply" @onClick="showReplyForm" /> - <resolve-discussion-button - v-if="discussion.resolvable" - :is-resolving="isResolving" - :button-title="resolveButtonTitle" - @onClick="resolveHandler" + <note-form + ref="noteForm" + :discussion="discussion" + :is-editing="false" + :line="diffLine" + save-button-title="Comment" + :autosave-key="autosaveKey" + @handleFormUpdateAddToReview="addReplyToReview" + @handleFormUpdate="saveReply" + @cancelForm="cancelReplyForm" /> - <div - v-if="discussion.resolvable" - class="btn-group discussion-actions ml-sm-2" - role="group" - > - <resolve-with-issue-button - v-if="resolveWithIssuePath" - :url="resolveWithIssuePath" - /> - <jump-to-next-discussion-button - v-if="shouldShowJumpToNextDiscussion" - @onClick="jumpToNextDiscussion" - /> - </div> </div> - </template> - <note-form - v-if="isReplying" - ref="noteForm" - :discussion="discussion" - :is-editing="false" - :line="diffLine" - save-button-title="Comment" - @handleFormUpdate="saveReply" - @cancelForm="cancelReplyForm" - /> - <note-signed-out-widget v-if="!canReply" /> - </div> - </div> + <note-signed-out-widget v-if="!userCanReply" /> + </div> + </template> + </discussion-notes> </component> </div> </div> |