Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/notes/components')
-rw-r--r--app/assets/javascripts/notes/components/discussion_counter.vue4
-rw-r--r--app/assets/javascripts/notes/components/discussion_notes.vue90
-rw-r--r--app/assets/javascripts/notes/components/multiline_comment_form.vue6
-rw-r--r--app/assets/javascripts/notes/components/note_body.vue13
-rw-r--r--app/assets/javascripts/notes/components/note_form.vue32
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue18
6 files changed, 104 insertions, 59 deletions
diff --git a/app/assets/javascripts/notes/components/discussion_counter.vue b/app/assets/javascripts/notes/components/discussion_counter.vue
index 831e6dd8f92..33819c78c0f 100644
--- a/app/assets/javascripts/notes/components/discussion_counter.vue
+++ b/app/assets/javascripts/notes/components/discussion_counter.vue
@@ -78,8 +78,8 @@ export default {
v-if="resolveAllDiscussionsIssuePath && !allResolved"
v-gl-tooltip
:href="resolveAllDiscussionsIssuePath"
- :title="s__('Create issue to resolve all threads')"
- :aria-label="s__('Create issue to resolve all threads')"
+ :title="__('Create issue to resolve all threads')"
+ :aria-label="__('Create issue to resolve all threads')"
class="new-issue-for-discussion discussion-create-issue-btn"
icon="issue-new"
/>
diff --git a/app/assets/javascripts/notes/components/discussion_notes.vue b/app/assets/javascripts/notes/components/discussion_notes.vue
index 6fcfa66ea49..d1df4eb848b 100644
--- a/app/assets/javascripts/notes/components/discussion_notes.vue
+++ b/app/assets/javascripts/notes/components/discussion_notes.vue
@@ -1,5 +1,6 @@
<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';
@@ -16,7 +17,9 @@ export default {
ToggleRepliesWidget,
NoteEditedText,
DiscussionNotesRepliesWrapper,
+ GlIntersectionObserver,
},
+ inject: ['discussionObserverHandler'],
props: {
discussion: {
type: Object,
@@ -54,7 +57,11 @@ export default {
},
},
computed: {
- ...mapGetters(['userCanReply']),
+ ...mapGetters([
+ 'userCanReply',
+ 'previousUnresolvedDiscussionId',
+ 'firstUnresolvedDiscussionId',
+ ]),
hasReplies() {
return Boolean(this.replies.length);
},
@@ -77,9 +84,20 @@ 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']),
+ ...mapActions([
+ 'toggleDiscussion',
+ 'setSelectedCommentPositionHover',
+ 'setCurrentDiscussionId',
+ ]),
componentName(note) {
if (note.isPlaceholderNote) {
if (note.placeholderType === SYSTEM_NOTE) {
@@ -110,6 +128,18 @@ 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>
@@ -122,33 +152,35 @@ export default {
@mouseleave="handleMouseLeave(discussion)"
>
<template v-if="shouldGroupReplies">
- <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 :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>
<discussion-notes-replies-wrapper :is-diff-discussion="discussion.diff_discussion">
<toggle-replies-widget
v-if="hasReplies"
diff --git a/app/assets/javascripts/notes/components/multiline_comment_form.vue b/app/assets/javascripts/notes/components/multiline_comment_form.vue
index 6ad565567be..1633b79c3be 100644
--- a/app/assets/javascripts/notes/components/multiline_comment_form.vue
+++ b/app/assets/javascripts/notes/components/multiline_comment_form.vue
@@ -1,6 +1,6 @@
<script>
import { GlFormSelect, GlSprintf } from '@gitlab/ui';
-import { mapActions, mapState } from 'vuex';
+import { mapActions } from 'vuex';
import { getSymbol, getLineClasses } from './multiline_comment_utils';
export default {
@@ -27,13 +27,12 @@ export default {
};
},
computed: {
- ...mapState({ selectedCommentPosition: ({ notes }) => notes.selectedCommentPosition }),
lineNumber() {
return this.commentLineOptions[this.commentLineOptions.length - 1].text;
},
},
created() {
- const line = this.selectedCommentPosition?.start || this.lineRange?.start || this.line;
+ const line = this.lineRange?.start || this.line;
this.commentLineStart = {
line_code: line.line_code,
@@ -42,7 +41,6 @@ export default {
new_line: line.new_line,
};
- if (this.selectedCommentPosition) return;
this.highlightSelection();
},
destroyed() {
diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue
index 1ce1696e332..c09582d6287 100644
--- a/app/assets/javascripts/notes/components/note_body.vue
+++ b/app/assets/javascripts/notes/components/note_body.vue
@@ -1,5 +1,6 @@
<script>
import $ from 'jquery';
+import { GlSafeHtmlDirective } from '@gitlab/ui';
import { escape } from 'lodash';
import { mapActions, mapGetters, mapState } from 'vuex';
@@ -19,6 +20,9 @@ export default {
noteForm,
Suggestions,
},
+ directives: {
+ SafeHtml: GlSafeHtmlDirective,
+ },
mixins: [autosave],
props: {
note: {
@@ -144,6 +148,9 @@ export default {
this.removeSuggestionInfoFromBatch(suggestionId);
},
},
+ safeHtmlConfig: {
+ ADD_TAGS: ['use', 'gl-emoji'],
+ },
};
</script>
@@ -163,11 +170,7 @@ export default {
@addToBatch="addSuggestionToBatch"
@removeFromBatch="removeSuggestionFromBatch"
/>
- <div
- v-else
- class="note-text md"
- v-html="note.note_html /* eslint-disable-line vue/no-v-html */"
- ></div>
+ <div v-else v-safe-html:[$options.safeHtmlConfig]="note.note_html" class="note-text md"></div>
<note-form
v-if="isEditing"
ref="noteForm"
diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue
index b05643e5e13..d6b65ed0e8b 100644
--- a/app/assets/javascripts/notes/components/note_form.vue
+++ b/app/assets/javascripts/notes/components/note_form.vue
@@ -1,9 +1,9 @@
<script>
-import { GlButton } from '@gitlab/ui';
+import { GlButton, GlSprintf, GlLink } from '@gitlab/ui';
import { mapGetters, mapActions, mapState } from 'vuex';
import { getDraft, updateDraft } from '~/lib/utils/autosave';
import { mergeUrlParams } from '~/lib/utils/url_utility';
-import { __, sprintf } from '~/locale';
+import { __ } from '~/locale';
import markdownField from '~/vue_shared/components/markdown/field.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import eventHub from '../event_hub';
@@ -17,6 +17,8 @@ export default {
markdownField,
CommentFieldLayout,
GlButton,
+ GlSprintf,
+ GlLink,
},
mixins: [glFeatureFlagsMixin(), issuableStateMixin, resolvable],
props: {
@@ -203,16 +205,12 @@ export default {
);
},
changedCommentText() {
- return sprintf(
- __(
+ return {
+ text: __(
'This comment changed after you started editing it. Review the %{startTag}updated comment%{endTag} to ensure information is not lost.',
),
- {
- startTag: `<a href="${this.noteHash}" target="_blank" rel="noopener noreferrer">`,
- endTag: '</a>',
- },
- false,
- );
+ placeholder: { link: ['startTag', 'endTag'] },
+ };
},
},
watch: {
@@ -318,11 +316,13 @@ export default {
<template>
<div ref="editNoteForm" class="note-edit-form current-note-edit-form js-discussion-note-form">
- <div
- v-if="conflictWhileEditing"
- class="js-conflict-edit-warning alert alert-danger"
- v-html="changedCommentText /* eslint-disable-line vue/no-v-html */"
- ></div>
+ <div v-if="conflictWhileEditing" class="js-conflict-edit-warning alert alert-danger">
+ <gl-sprintf :message="changedCommentText.text" :placeholders="changedCommentText.placeholder">
+ <template #link="{ content }">
+ <gl-link :href="noteHash" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </div>
<div class="flash-container timeline-content"></div>
<form :data-line-code="lineCode" class="edit-note common-note-form js-quick-submit gfm-form">
<comment-field-layout
@@ -334,13 +334,13 @@ export default {
:markdown-docs-path="markdownDocsPath"
:quick-actions-docs-path="quickActionsDocsPath"
:line="line"
+ :lines="lines"
:note="discussionNote"
:can-suggest="canSuggest"
:add-spacing-classes="false"
:help-page-path="helpPagePath"
:show-suggest-popover="showSuggestPopover"
:textarea-value="updatedNoteBody"
- :lines="lines"
@handleSuggestDismissed="() => $emit('handleSuggestDismissed')"
>
<template #textarea>
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index 58570e76795..3ab3e7a20d4 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -8,6 +8,7 @@ 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';
@@ -38,6 +39,9 @@ export default {
TimelineEntryItem,
},
mixins: [glFeatureFlagsMixin()],
+ provide: {
+ discussionObserverHandler: discussionIntersectionObserverHandlerFactory(),
+ },
props: {
noteableData: {
type: Object,
@@ -94,15 +98,17 @@ export default {
return this.noteableData.noteableType;
},
allDiscussions() {
+ let skeletonNotes = [];
+
if (this.renderSkeleton || this.isLoading) {
const prerenderedNotesCount = parseInt(this.notesData.prerenderedNotesCount, 10) || 0;
- return new Array(prerenderedNotesCount).fill({
+ skeletonNotes = new Array(prerenderedNotesCount).fill({
isSkeletonNote: true,
});
}
- return this.discussions;
+ return this.discussions.concat(skeletonNotes);
},
canReply() {
return this.userCanReply && !this.commentsDisabled && !this.timelineEnabled;
@@ -258,7 +264,13 @@ export default {
getFetchDiscussionsConfig() {
const defaultConfig = { path: this.getNotesDataByProp('discussionsPath') };
- if (doesHashExistInUrl(constants.NOTE_UNDERSCORE)) {
+ const currentFilter =
+ this.getNotesDataByProp('notesFilter') || constants.DISCUSSION_FILTERS_DEFAULT_VALUE;
+
+ if (
+ doesHashExistInUrl(constants.NOTE_UNDERSCORE) &&
+ currentFilter !== constants.DISCUSSION_FILTERS_DEFAULT_VALUE
+ ) {
return {
...defaultConfig,
filter: constants.DISCUSSION_FILTERS_DEFAULT_VALUE,