diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-20 13:37:47 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-20 13:37:47 +0000 |
commit | aee0a117a889461ce8ced6fcf73207fe017f1d99 (patch) | |
tree | 891d9ef189227a8445d83f35c1b0fc99573f4380 /app/assets/javascripts/notes | |
parent | 8d46af3258650d305f53b819eabf7ab18d22f59e (diff) | |
download | gitlab-ce-aee0a117a889461ce8ced6fcf73207fe017f1d99.tar.gz |
Add latest changes from gitlab-org/gitlab@14-6-stable-eev14.6.0-rc42
Diffstat (limited to 'app/assets/javascripts/notes')
14 files changed, 109 insertions, 105 deletions
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue index 4e31fdcd4f0..996c008b881 100644 --- a/app/assets/javascripts/notes/components/comment_form.vue +++ b/app/assets/javascripts/notes/components/comment_form.vue @@ -11,7 +11,6 @@ import httpStatusCodes from '~/lib/utils/http_status'; import { capitalizeFirstCharacter, convertToCamelCase, - splitCamelCase, slugifyWithUnderscore, } from '~/lib/utils/text_utility'; import { sprintf } from '~/locale'; @@ -77,7 +76,15 @@ export default { ]), ...mapState(['isToggleStateButtonLoading']), noteableDisplayName() { - return splitCamelCase(this.noteableType).toLowerCase(); + const displayNameMap = { + [constants.ISSUE_NOTEABLE_TYPE]: this.$options.i18n.issue, + [constants.EPIC_NOTEABLE_TYPE]: this.$options.i18n.epic, + [constants.MERGE_REQUEST_NOTEABLE_TYPE]: this.$options.i18n.mergeRequest, + }; + + const noteableTypeKey = + constants.NOTEABLE_TYPE_MAPPING[this.noteableType] || constants.ISSUE_NOTEABLE_TYPE; + return displayNameMap[noteableTypeKey]; }, isLoggedIn() { return this.getUserData.id; @@ -103,15 +110,13 @@ export default { const openOrClose = this.isOpen ? 'close' : 'reopen'; if (this.note.length) { - return sprintf(this.$options.i18n.actionButtonWithNote, { + return sprintf(this.$options.i18n.actionButton.withNote[openOrClose], { actionText: this.commentButtonTitle, - openOrClose, noteable: this.noteableDisplayName, }); } - return sprintf(this.$options.i18n.actionButton, { - openOrClose: capitalizeFirstCharacter(openOrClose), + return sprintf(this.$options.i18n.actionButton.withoutNote[openOrClose], { noteable: this.noteableDisplayName, }); }, @@ -151,13 +156,8 @@ export default { draftEndpoint() { return this.getNotesData.draftsPath; }, - issuableTypeTitle() { - return this.noteableType === constants.MERGE_REQUEST_NOTEABLE_TYPE - ? this.$options.i18n.mergeRequest - : this.$options.i18n.issue; - }, isIssue() { - return this.noteableDisplayName === constants.ISSUE_NOTEABLE_TYPE; + return constants.NOTEABLE_TYPE_MAPPING[this.noteableType] === constants.ISSUE_NOTEABLE_TYPE; }, trackingLabel() { return slugifyWithUnderscore(`${this.commentButtonTitle} button`); @@ -329,7 +329,7 @@ export default { <template> <div> <note-signed-out-widget v-if="!isLoggedIn" /> - <discussion-locked-widget v-else-if="!canCreateNote" :issuable-type="issuableTypeTitle" /> + <discussion-locked-widget v-else-if="!canCreateNote" :issuable-type="noteableDisplayName" /> <ul v-else-if="canCreateNote" class="notes notes-form timeline"> <timeline-entry-item class="note-form"> <gl-alert diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue index b04aa74d46e..b2d5910fd3f 100644 --- a/app/assets/javascripts/notes/components/diff_with_note.vue +++ b/app/assets/javascripts/notes/components/diff_with_note.vue @@ -1,5 +1,8 @@ <script> -import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui'; +import { + GlDeprecatedSkeletonLoading as GlSkeletonLoading, + GlSafeHtmlDirective as SafeHtml, +} from '@gitlab/ui'; import { mapState, mapActions } from 'vuex'; import DiffFileHeader from '~/diffs/components/diff_file_header.vue'; import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue'; @@ -17,6 +20,9 @@ export default { DiffViewer, ImageDiffOverlay, }, + directives: { + SafeHtml, + }, props: { discussion: { type: Object, @@ -92,11 +98,7 @@ export default { > <td :class="line.type" class="diff-line-num old_line">{{ line.old_line }}</td> <td :class="line.type" class="diff-line-num new_line">{{ line.new_line }}</td> - <td - :class="line.type" - class="line_content" - v-html="trimChar(line.rich_text) /* eslint-disable-line vue/no-v-html */" - ></td> + <td v-safe-html="trimChar(line.rich_text)" :class="line.type" class="line_content"></td> </tr> </template> <tr v-if="!hasTruncatedDiffLines" class="line_holder line-holder-placeholder"> diff --git a/app/assets/javascripts/notes/components/discussion_filter.vue b/app/assets/javascripts/notes/components/discussion_filter.vue index 88f053aed67..102afaf308f 100644 --- a/app/assets/javascripts/notes/components/discussion_filter.vue +++ b/app/assets/javascripts/notes/components/discussion_filter.vue @@ -39,7 +39,7 @@ export default { }; }, computed: { - ...mapGetters(['getNotesDataByProp', 'timelineEnabled']), + ...mapGetters(['getNotesDataByProp', 'timelineEnabled', 'isLoading']), currentFilter() { if (!this.currentValue) return this.filters[0]; return this.filters.find((filter) => filter.value === this.currentValue); @@ -119,6 +119,7 @@ export default { class="gl-mr-3 full-width-mobile discussion-filter-container js-discussion-filter-container" data-qa-selector="discussion_filter_dropdown" :text="currentFilter.title" + :disabled="isLoading" > <div v-for="filter in filters" :key="filter.value" class="dropdown-item-wrapper"> <gl-dropdown-item diff --git a/app/assets/javascripts/notes/components/discussion_locked_widget.vue b/app/assets/javascripts/notes/components/discussion_locked_widget.vue index 2f215e36d5b..8ac3f6bea68 100644 --- a/app/assets/javascripts/notes/components/discussion_locked_widget.vue +++ b/app/assets/javascripts/notes/components/discussion_locked_widget.vue @@ -1,7 +1,6 @@ <script> import { GlLink, GlIcon } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; -import Issuable from '~/vue_shared/mixins/issuable'; import issuableStateMixin from '../mixins/issuable_state'; export default { @@ -9,8 +8,17 @@ export default { GlIcon, GlLink, }, - mixins: [Issuable, issuableStateMixin], + mixins: [issuableStateMixin], + props: { + issuableType: { + required: true, + type: String, + }, + }, computed: { + issuableDisplayName() { + return this.issuableType.replace(/_/g, ' '); + }, projectArchivedWarning() { return __('This project is archived and cannot be commented on.'); }, diff --git a/app/assets/javascripts/notes/components/discussion_notes.vue b/app/assets/javascripts/notes/components/discussion_notes.vue index d1df4eb848b..6fcfa66ea49 100644 --- a/app/assets/javascripts/notes/components/discussion_notes.vue +++ b/app/assets/javascripts/notes/components/discussion_notes.vue @@ -1,6 +1,5 @@ <script> import { mapGetters, mapActions } from 'vuex'; -import { GlIntersectionObserver } from '@gitlab/ui'; import { __ } from '~/locale'; import PlaceholderNote from '~/vue_shared/components/notes/placeholder_note.vue'; import PlaceholderSystemNote from '~/vue_shared/components/notes/placeholder_system_note.vue'; @@ -17,9 +16,7 @@ export default { ToggleRepliesWidget, NoteEditedText, DiscussionNotesRepliesWrapper, - GlIntersectionObserver, }, - inject: ['discussionObserverHandler'], props: { discussion: { type: Object, @@ -57,11 +54,7 @@ export default { }, }, computed: { - ...mapGetters([ - 'userCanReply', - 'previousUnresolvedDiscussionId', - 'firstUnresolvedDiscussionId', - ]), + ...mapGetters(['userCanReply']), hasReplies() { return Boolean(this.replies.length); }, @@ -84,20 +77,9 @@ export default { url: this.discussion.discussion_path, }; }, - isFirstUnresolved() { - return this.firstUnresolvedDiscussionId === this.discussion.id; - }, - }, - observerOptions: { - threshold: 0, - rootMargin: '0px 0px -50% 0px', }, methods: { - ...mapActions([ - 'toggleDiscussion', - 'setSelectedCommentPositionHover', - 'setCurrentDiscussionId', - ]), + ...mapActions(['toggleDiscussion', 'setSelectedCommentPositionHover']), componentName(note) { if (note.isPlaceholderNote) { if (note.placeholderType === SYSTEM_NOTE) { @@ -128,18 +110,6 @@ export default { this.setSelectedCommentPositionHover(); } }, - observerTriggered(entry) { - this.discussionObserverHandler({ - entry, - isFirstUnresolved: this.isFirstUnresolved, - currentDiscussion: { ...this.discussion }, - isDiffsPage: !this.isOverviewTab, - functions: { - setCurrentDiscussionId: this.setCurrentDiscussionId, - getPreviousUnresolvedDiscussionId: this.previousUnresolvedDiscussionId, - }, - }); - }, }, }; </script> @@ -152,35 +122,33 @@ export default { @mouseleave="handleMouseLeave(discussion)" > <template v-if="shouldGroupReplies"> - <gl-intersection-observer :options="$options.observerOptions" @update="observerTriggered"> - <component - :is="componentName(firstNote)" - :note="componentData(firstNote)" - :line="line || diffLine" - :discussion-file="discussion.diff_file" - :commit="commit" - :help-page-path="helpPagePath" - :show-reply-button="userCanReply" - :discussion-root="true" - :discussion-resolve-path="discussion.resolve_path" - :is-overview-tab="isOverviewTab" - @handleDeleteNote="$emit('deleteNote')" - @startReplying="$emit('startReplying')" - > - <template #discussion-resolved-text> - <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 discussion-resolved-text" - /> - </template> - <template #avatar-badge> - <slot name="avatar-badge"></slot> - </template> - </component> - </gl-intersection-observer> + <component + :is="componentName(firstNote)" + :note="componentData(firstNote)" + :line="line || diffLine" + :discussion-file="discussion.diff_file" + :commit="commit" + :help-page-path="helpPagePath" + :show-reply-button="userCanReply" + :discussion-root="true" + :discussion-resolve-path="discussion.resolve_path" + :is-overview-tab="isOverviewTab" + @handleDeleteNote="$emit('deleteNote')" + @startReplying="$emit('startReplying')" + > + <template #discussion-resolved-text> + <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 discussion-resolved-text" + /> + </template> + <template #avatar-badge> + <slot name="avatar-badge"></slot> + </template> + </component> <discussion-notes-replies-wrapper :is-diff-discussion="discussion.diff_discussion"> <toggle-replies-widget v-if="hasReplies" diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue index c09582d6287..f465ad23a06 100644 --- a/app/assets/javascripts/notes/components/note_body.vue +++ b/app/assets/javascripts/notes/components/note_body.vue @@ -149,7 +149,7 @@ export default { }, }, safeHtmlConfig: { - ADD_TAGS: ['use', 'gl-emoji'], + ADD_TAGS: ['use', 'gl-emoji', 'copy-code'], }, }; </script> diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue index 77f796fe8b0..8e32c3b3073 100644 --- a/app/assets/javascripts/notes/components/noteable_discussion.vue +++ b/app/assets/javascripts/notes/components/noteable_discussion.vue @@ -223,17 +223,20 @@ export default { }) .catch((err) => { this.removePlaceholderNotes(); - const msg = __( - 'Your comment could not be submitted! Please check your network connection and try again.', - ); - createFlash({ - message: msg, - parent: this.$el, - }); + this.handleSaveError(err); // The 'err' parameter is being used in JH, don't remove it this.$refs.noteForm.note = noteText; callback(err); }); }, + handleSaveError() { + const msg = __( + 'Your comment could not be submitted! Please check your network connection and try again.', + ); + createFlash({ + message: msg, + parent: this.$el, + }); + }, deleteNoteHandler(note) { this.$emit('noteDeleted', this.discussion, note); }, @@ -280,6 +283,7 @@ export default { v-if="showDraft(discussion.reply_id)" :key="`draft_${discussion.id}`" :draft="draftForDiscussion(discussion.reply_id)" + :line="line" /> <div v-else-if="canShowReplyActions && showReplies" diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue index e35d8d94289..3250a4818c7 100644 --- a/app/assets/javascripts/notes/components/noteable_note.vue +++ b/app/assets/javascripts/notes/components/noteable_note.vue @@ -331,17 +331,20 @@ export default { this.isEditing = true; this.setSelectedCommentPositionHover(); this.$nextTick(() => { - const msg = __('Something went wrong while editing your comment. Please try again.'); - createFlash({ - message: msg, - parent: this.$el, - }); + this.handleUpdateError(response); // The 'response' parameter is being used in JH, don't remove it this.recoverNoteContent(noteText); callback(); }); } }); }, + handleUpdateError() { + const msg = __('Something went wrong while editing your comment. Please try again.'); + createFlash({ + message: msg, + parent: this.$el, + }); + }, formCancelHandler({ shouldConfirm, isDirty }) { if (shouldConfirm && isDirty) { // eslint-disable-next-line no-alert @@ -388,7 +391,7 @@ export default { <div v-if="showMultiLineComment" data-testid="multiline-comment" - class="gl-mb-3 gl-text-gray-500 gl-border-gray-200 gl-border-b-solid gl-border-b-1 gl-pb-3" + class="gl-mb-5 gl-text-gray-500 gl-border-gray-100 gl-border-b-solid gl-border-b-1 gl-pb-4" > <gl-sprintf :message="__('Comment on lines %{startLine} to %{endLine}')"> <template #startLine> diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue index 3ab3e7a20d4..c4924cd41f5 100644 --- a/app/assets/javascripts/notes/components/notes_app.vue +++ b/app/assets/javascripts/notes/components/notes_app.vue @@ -8,7 +8,6 @@ import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item import OrderedLayout from '~/vue_shared/components/ordered_layout.vue'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import draftNote from '../../batch_comments/components/draft_note.vue'; -import { discussionIntersectionObserverHandlerFactory } from '../../diffs/utils/discussions'; import { getLocationHash, doesHashExistInUrl } from '../../lib/utils/url_utility'; import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue'; import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue'; @@ -39,9 +38,6 @@ export default { TimelineEntryItem, }, mixins: [glFeatureFlagsMixin()], - provide: { - discussionObserverHandler: discussionIntersectionObserverHandlerFactory(), - }, props: { noteableData: { type: Object, @@ -108,6 +104,10 @@ export default { }); } + if (this.sortDirDesc) { + return skeletonNotes.concat(this.discussions); + } + return this.discussions.concat(skeletonNotes); }, canReply() { diff --git a/app/assets/javascripts/notes/components/sidebar_subscription.vue b/app/assets/javascripts/notes/components/sidebar_subscription.vue index 047c04c8482..52dadc7b4c3 100644 --- a/app/assets/javascripts/notes/components/sidebar_subscription.vue +++ b/app/assets/javascripts/notes/components/sidebar_subscription.vue @@ -1,6 +1,6 @@ <script> import { mapActions } from 'vuex'; -import { IssuableType } from '~/issue_show/constants'; +import { IssuableType } from '~/issues/constants'; import { fetchPolicies } from '~/lib/graphql'; import { confidentialityQueries } from '~/sidebar/constants'; import { defaultClient as gqlClient } from '~/sidebar/graphql'; diff --git a/app/assets/javascripts/notes/i18n.js b/app/assets/javascripts/notes/i18n.js index 1ffb94d11ad..951fa9733d4 100644 --- a/app/assets/javascripts/notes/i18n.js +++ b/app/assets/javascripts/notes/i18n.js @@ -9,15 +9,27 @@ export const COMMENT_FORM = { issue: __('issue'), startThread: __('Start thread'), mergeRequest: __('merge request'), + epic: __('epic'), bodyPlaceholder: __('Write a comment or drag your files hereā¦'), confidential: s__('Notes|Make this comment confidential'), - confidentialVisibility: s__('Notes|Confidential comments are only visible to project members'), + confidentialVisibility: s__( + 'Notes|Confidential comments are only visible to members with the role of Reporter or higher', + ), discussionThatNeedsResolution: __( 'Discuss a specific suggestion or question that needs to be resolved.', ), discussion: __('Discuss a specific suggestion or question.'), actionButtonWithNote: __('%{actionText} & %{openOrClose} %{noteable}'), - actionButton: __('%{openOrClose} %{noteable}'), + actionButton: { + withNote: { + reopen: __('%{actionText} & reopen %{noteable}'), + close: __('%{actionText} & close %{noteable}'), + }, + withoutNote: { + reopen: __('Reopen %{noteable}'), + close: __('Close %{noteable}'), + }, + }, submitButton: { startThread: __('Start thread'), comment: __('Comment'), diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js index c862a29ad9c..50b05ea9d69 100644 --- a/app/assets/javascripts/notes/stores/actions.js +++ b/app/assets/javascripts/notes/stores/actions.js @@ -601,7 +601,8 @@ export const setLoadingState = ({ commit }, data) => { commit(types.SET_NOTES_LOADING_STATE, data); }; -export const filterDiscussion = ({ dispatch }, { path, filter, persistFilter }) => { +export const filterDiscussion = ({ commit, dispatch }, { path, filter, persistFilter }) => { + commit(types.CLEAR_DISCUSSIONS); dispatch('setLoadingState', true); dispatch('fetchDiscussions', { path, filter, persistFilter }) .then(() => { diff --git a/app/assets/javascripts/notes/stores/mutation_types.js b/app/assets/javascripts/notes/stores/mutation_types.js index fcd2846ff0d..ebda08a3d62 100644 --- a/app/assets/javascripts/notes/stores/mutation_types.js +++ b/app/assets/javascripts/notes/stores/mutation_types.js @@ -1,6 +1,7 @@ export const ADD_NEW_NOTE = 'ADD_NEW_NOTE'; export const ADD_NEW_REPLY_TO_DISCUSSION = 'ADD_NEW_REPLY_TO_DISCUSSION'; export const ADD_OR_UPDATE_DISCUSSIONS = 'ADD_OR_UPDATE_DISCUSSIONS'; +export const CLEAR_DISCUSSIONS = 'CLEAR_DISCUSSIONS'; export const DELETE_NOTE = 'DELETE_NOTE'; export const REMOVE_PLACEHOLDER_NOTES = 'REMOVE_PLACEHOLDER_NOTES'; export const SET_NOTES_DATA = 'SET_NOTES_DATA'; diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js index 1a99750ddb3..ba19ecd0c04 100644 --- a/app/assets/javascripts/notes/stores/mutations.js +++ b/app/assets/javascripts/notes/stores/mutations.js @@ -129,6 +129,10 @@ export default { Object.assign(state, { userData: data }); }, + [types.CLEAR_DISCUSSIONS](state) { + state.discussions = []; + }, + [types.ADD_OR_UPDATE_DISCUSSIONS](state, discussionsData) { discussionsData.forEach((d) => { const discussion = { ...d }; |