summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/notes
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-12-20 13:37:47 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-12-20 13:37:47 +0000
commitaee0a117a889461ce8ced6fcf73207fe017f1d99 (patch)
tree891d9ef189227a8445d83f35c1b0fc99573f4380 /app/assets/javascripts/notes
parent8d46af3258650d305f53b819eabf7ab18d22f59e (diff)
downloadgitlab-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')
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue26
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue14
-rw-r--r--app/assets/javascripts/notes/components/discussion_filter.vue3
-rw-r--r--app/assets/javascripts/notes/components/discussion_locked_widget.vue12
-rw-r--r--app/assets/javascripts/notes/components/discussion_notes.vue90
-rw-r--r--app/assets/javascripts/notes/components/note_body.vue2
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue18
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue15
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue8
-rw-r--r--app/assets/javascripts/notes/components/sidebar_subscription.vue2
-rw-r--r--app/assets/javascripts/notes/i18n.js16
-rw-r--r--app/assets/javascripts/notes/stores/actions.js3
-rw-r--r--app/assets/javascripts/notes/stores/mutation_types.js1
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js4
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 };