summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/notes
diff options
context:
space:
mode:
authorRobert Speicher <rspeicher@gmail.com>2021-01-20 13:34:23 -0600
committerRobert Speicher <rspeicher@gmail.com>2021-01-20 13:34:23 -0600
commit6438df3a1e0fb944485cebf07976160184697d72 (patch)
tree00b09bfd170e77ae9391b1a2f5a93ef6839f2597 /app/assets/javascripts/notes
parent42bcd54d971da7ef2854b896a7b34f4ef8601067 (diff)
downloadgitlab-ce-6438df3a1e0fb944485cebf07976160184697d72.tar.gz
Add latest changes from gitlab-org/gitlab@13-8-stable-eev13.8.0-rc42
Diffstat (limited to 'app/assets/javascripts/notes')
-rw-r--r--app/assets/javascripts/notes/components/comment_field_layout.vue69
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue81
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue2
-rw-r--r--app/assets/javascripts/notes/components/discussion_actions.vue19
-rw-r--r--app/assets/javascripts/notes/components/discussion_counter.vue6
-rw-r--r--app/assets/javascripts/notes/components/discussion_filter.vue2
-rw-r--r--app/assets/javascripts/notes/components/discussion_jump_to_next_button.vue38
-rw-r--r--app/assets/javascripts/notes/components/email_participants_warning.vue70
-rw-r--r--app/assets/javascripts/notes/components/multiline_comment_form.vue7
-rw-r--r--app/assets/javascripts/notes/components/multiline_comment_utils.js10
-rw-r--r--app/assets/javascripts/notes/components/note_actions.vue4
-rw-r--r--app/assets/javascripts/notes/components/note_body.vue16
-rw-r--r--app/assets/javascripts/notes/components/note_form.vue96
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue4
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue2
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue4
-rw-r--r--app/assets/javascripts/notes/components/timeline_toggle.vue1
-rw-r--r--app/assets/javascripts/notes/components/toggle_replies_widget.vue4
-rw-r--r--app/assets/javascripts/notes/discussion_filters.js4
-rw-r--r--app/assets/javascripts/notes/mixins/diff_line_note_form.js6
-rw-r--r--app/assets/javascripts/notes/mixins/discussion_navigation.js2
-rw-r--r--app/assets/javascripts/notes/mixins/issuable_state.js11
-rw-r--r--app/assets/javascripts/notes/mixins/resolvable.js2
-rw-r--r--app/assets/javascripts/notes/sort_discussions.js2
-rw-r--r--app/assets/javascripts/notes/stores/actions.js41
-rw-r--r--app/assets/javascripts/notes/stores/collapse_utils.js4
-rw-r--r--app/assets/javascripts/notes/stores/getters.js96
-rw-r--r--app/assets/javascripts/notes/stores/modules/index.js2
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js24
-rw-r--r--app/assets/javascripts/notes/stores/utils.js14
-rw-r--r--app/assets/javascripts/notes/utils.js2
31 files changed, 359 insertions, 286 deletions
diff --git a/app/assets/javascripts/notes/components/comment_field_layout.vue b/app/assets/javascripts/notes/components/comment_field_layout.vue
new file mode 100644
index 00000000000..aaf64702ffd
--- /dev/null
+++ b/app/assets/javascripts/notes/components/comment_field_layout.vue
@@ -0,0 +1,69 @@
+<script>
+import EmailParticipantsWarning from './email_participants_warning.vue';
+import NoteableWarning from '~/vue_shared/components/notes/noteable_warning.vue';
+
+const DEFAULT_NOTEABLE_TYPE = 'Issue';
+
+export default {
+ components: {
+ EmailParticipantsWarning,
+ NoteableWarning,
+ },
+ props: {
+ noteableData: {
+ type: Object,
+ required: true,
+ },
+ noteableType: {
+ type: String,
+ required: false,
+ default: DEFAULT_NOTEABLE_TYPE,
+ },
+ withAlertContainer: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ isLocked() {
+ return Boolean(this.noteableData.discussion_locked);
+ },
+ isConfidential() {
+ return Boolean(this.noteableData.confidential);
+ },
+ hasWarning() {
+ return this.isConfidential || this.isLocked;
+ },
+ emailParticipants() {
+ return this.noteableData.issue_email_participants?.map(({ email }) => email) || [];
+ },
+ },
+};
+</script>
+<template>
+ <div
+ class="comment-warning-wrapper gl-border-solid gl-border-1 gl-rounded-base gl-border-gray-100"
+ >
+ <div
+ v-if="withAlertContainer"
+ class="error-alert"
+ data-testid="comment-field-alert-container"
+ ></div>
+ <noteable-warning
+ v-if="hasWarning"
+ class="gl-border-b-1 gl-border-b-solid gl-border-b-gray-100 gl-rounded-base gl-rounded-bottom-left-none gl-rounded-bottom-right-none"
+ :is-locked="isLocked"
+ :is-confidential="isConfidential"
+ :noteable-type="noteableType"
+ :locked-noteable-docs-path="noteableData.locked_discussion_docs_path"
+ :confidential-noteable-docs-path="noteableData.confidential_issues_docs_path"
+ />
+ <slot></slot>
+ <email-participants-warning
+ v-if="emailParticipants.length"
+ class="gl-border-t-1 gl-border-t-solid gl-border-t-gray-100 gl-rounded-base gl-rounded-top-left-none! gl-rounded-top-right-none!"
+ :emails="emailParticipants"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
index 0363173f912..111af977ec5 100644
--- a/app/assets/javascripts/notes/components/comment_form.vue
+++ b/app/assets/javascripts/notes/components/comment_form.vue
@@ -17,17 +17,17 @@ import {
import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
import * as constants from '../constants';
import eventHub from '../event_hub';
-import NoteableWarning from '~/vue_shared/components/notes/noteable_warning.vue';
import markdownField from '~/vue_shared/components/markdown/field.vue';
import userAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import noteSignedOutWidget from './note_signed_out_widget.vue';
import discussionLockedWidget from './discussion_locked_widget.vue';
import issuableStateMixin from '../mixins/issuable_state';
+import CommentFieldLayout from './comment_field_layout.vue';
export default {
name: 'CommentForm',
components: {
- NoteableWarning,
noteSignedOutWidget,
discussionLockedWidget,
markdownField,
@@ -35,8 +35,9 @@ export default {
GlButton,
TimelineEntryItem,
GlIcon,
+ CommentFieldLayout,
},
- mixins: [issuableStateMixin],
+ mixins: [glFeatureFlagsMixin(), issuableStateMixin],
props: {
noteableType: {
type: String,
@@ -286,6 +287,9 @@ export default {
Autosize.update(this.$refs.textarea);
});
},
+ hasEmailParticipants() {
+ return this.getNoteableData.issue_email_participants?.length;
+ },
},
};
</script>
@@ -308,46 +312,41 @@ export default {
</div>
<div class="timeline-content timeline-content-form">
<form ref="commentForm" class="new-note common-note-form gfm-form js-main-target-form">
- <div class="error-alert"></div>
-
- <noteable-warning
- v-if="hasWarning(getNoteableData)"
- :is-locked="isLocked(getNoteableData)"
- :is-confidential="isConfidential(getNoteableData)"
+ <comment-field-layout
+ :with-alert-container="true"
+ :noteable-data="getNoteableData"
:noteable-type="noteableType"
- :locked-noteable-docs-path="lockedIssueDocsPath"
- :confidential-noteable-docs-path="confidentialIssueDocsPath"
- />
-
- <markdown-field
- ref="markdownField"
- :is-submitting="isSubmitting"
- :markdown-preview-path="markdownPreviewPath"
- :markdown-docs-path="markdownDocsPath"
- :quick-actions-docs-path="quickActionsDocsPath"
- :add-spacing-classes="false"
- :textarea-value="note"
>
- <textarea
- id="note-body"
- ref="textarea"
- slot="textarea"
- v-model="note"
- dir="auto"
- :disabled="isSubmitting"
- name="note[note]"
- class="note-textarea js-vue-comment-form js-note-text js-gfm-input js-autosize markdown-area"
- data-qa-selector="comment_field"
- data-testid="comment-field"
- data-supports-quick-actions="true"
- :aria-label="__('Description')"
- :placeholder="__('Write a comment or drag your files here…')"
- @keydown.up="editCurrentUserLastNote()"
- @keydown.meta.enter="handleSave()"
- @keydown.ctrl.enter="handleSave()"
- ></textarea>
- </markdown-field>
-
+ <markdown-field
+ ref="markdownField"
+ :is-submitting="isSubmitting"
+ :markdown-preview-path="markdownPreviewPath"
+ :markdown-docs-path="markdownDocsPath"
+ :quick-actions-docs-path="quickActionsDocsPath"
+ :add-spacing-classes="false"
+ :textarea-value="note"
+ >
+ <template #textarea>
+ <textarea
+ id="note-body"
+ ref="textarea"
+ v-model="note"
+ dir="auto"
+ :disabled="isSubmitting"
+ name="note[note]"
+ class="note-textarea js-vue-comment-form js-note-text js-gfm-input js-autosize markdown-area"
+ data-qa-selector="comment_field"
+ data-testid="comment-field"
+ :data-supports-quick-actions="!glFeatures.tributeAutocomplete"
+ :aria-label="__('Description')"
+ :placeholder="__('Write a comment or drag your files here…')"
+ @keydown.up="editCurrentUserLastNote()"
+ @keydown.meta.enter="handleSave()"
+ @keydown.ctrl.enter="handleSave()"
+ ></textarea>
+ </template>
+ </markdown-field>
+ </comment-field-layout>
<div class="note-form-actions">
<div
class="btn-group gl-mr-3 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue
index 1580c94658a..b7355d4d927 100644
--- a/app/assets/javascripts/notes/components/diff_with_note.vue
+++ b/app/assets/javascripts/notes/components/diff_with_note.vue
@@ -31,7 +31,7 @@ export default {
},
computed: {
...mapState({
- projectPath: state => state.diffs.projectPath,
+ projectPath: (state) => state.diffs.projectPath,
}),
diffMode() {
return getDiffMode(this.discussion.diff_file);
diff --git a/app/assets/javascripts/notes/components/discussion_actions.vue b/app/assets/javascripts/notes/components/discussion_actions.vue
index 0272790a75d..da4134ab2c4 100644
--- a/app/assets/javascripts/notes/components/discussion_actions.vue
+++ b/app/assets/javascripts/notes/components/discussion_actions.vue
@@ -2,7 +2,6 @@
import ReplyPlaceholder from './discussion_reply_placeholder.vue';
import ResolveDiscussionButton from './discussion_resolve_button.vue';
import ResolveWithIssueButton from './discussion_resolve_with_issue_button.vue';
-import JumpToNextDiscussionButton from './discussion_jump_to_next_button.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
@@ -11,7 +10,6 @@ export default {
ReplyPlaceholder,
ResolveDiscussionButton,
ResolveWithIssueButton,
- JumpToNextDiscussionButton,
},
mixins: [glFeatureFlagsMixin()],
props: {
@@ -38,14 +36,11 @@ export default {
},
},
computed: {
- hideJumpToNextUnresolvedInThreads() {
- return this.glFeatures.hideJumpToNextUnresolvedInThreads;
- },
resolvableNotes() {
- return this.discussion.notes.filter(x => x.resolvable);
+ return this.discussion.notes.filter((x) => x.resolvable);
},
userCanResolveDiscussion() {
- return this.resolvableNotes.every(note => note.current_user?.can_resolve_discussion);
+ return this.resolvableNotes.every((note) => note.current_user?.can_resolve_discussion);
},
},
};
@@ -74,15 +69,5 @@ export default {
:url="resolveWithIssuePath"
/>
</div>
- <div
- v-if="
- !hideJumpToNextUnresolvedInThreads &&
- discussion.resolvable &&
- shouldShowJumpToNextDiscussion
- "
- class="btn-group discussion-actions ml-sm-2"
- >
- <jump-to-next-discussion-button :from-discussion-id="discussion.id" />
- </div>
</div>
</template>
diff --git a/app/assets/javascripts/notes/components/discussion_counter.vue b/app/assets/javascripts/notes/components/discussion_counter.vue
index 2427a3f98ad..0a72627834d 100644
--- a/app/assets/javascripts/notes/components/discussion_counter.vue
+++ b/app/assets/javascripts/notes/components/discussion_counter.vue
@@ -32,10 +32,10 @@ export default {
return this.getNoteableData.create_issue_to_resolve_discussions_path;
},
toggeableDiscussions() {
- return this.discussions.filter(discussion => !discussion.individual_note);
+ return this.discussions.filter((discussion) => !discussion.individual_note);
},
allExpanded() {
- return this.toggeableDiscussions.every(discussion => discussion.expanded);
+ return this.toggeableDiscussions.every((discussion) => discussion.expanded);
},
lineResolveClass() {
return this.allResolved ? 'line-resolve-btn is-active' : 'line-resolve-text';
@@ -48,7 +48,7 @@ export default {
...mapActions(['setExpandDiscussions']),
handleExpandDiscussions() {
this.setExpandDiscussions({
- discussionIds: this.toggeableDiscussions.map(discussion => discussion.id),
+ discussionIds: this.toggeableDiscussions.map((discussion) => discussion.id),
expanded: !this.allExpanded,
});
},
diff --git a/app/assets/javascripts/notes/components/discussion_filter.vue b/app/assets/javascripts/notes/components/discussion_filter.vue
index 08c22f0b4c6..aa61aa9b3cb 100644
--- a/app/assets/javascripts/notes/components/discussion_filter.vue
+++ b/app/assets/javascripts/notes/components/discussion_filter.vue
@@ -42,7 +42,7 @@ export default {
...mapGetters(['getNotesDataByProp', 'timelineEnabled']),
currentFilter() {
if (!this.currentValue) return this.filters[0];
- return this.filters.find(filter => filter.value === this.currentValue);
+ return this.filters.find((filter) => filter.value === this.currentValue);
},
},
created() {
diff --git a/app/assets/javascripts/notes/components/discussion_jump_to_next_button.vue b/app/assets/javascripts/notes/components/discussion_jump_to_next_button.vue
deleted file mode 100644
index f94d0060b41..00000000000
--- a/app/assets/javascripts/notes/components/discussion_jump_to_next_button.vue
+++ /dev/null
@@ -1,38 +0,0 @@
-<script>
-import { GlTooltipDirective, GlIcon } from '@gitlab/ui';
-import discussionNavigation from '../mixins/discussion_navigation';
-
-export default {
- name: 'JumpToNextDiscussionButton',
- components: {
- GlIcon,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- mixins: [discussionNavigation],
- props: {
- fromDiscussionId: {
- type: String,
- required: true,
- },
- },
-};
-</script>
-
-<template>
- <div class="btn-group" role="group">
- <button
- ref="button"
- v-gl-tooltip
- class="btn btn-default discussion-next-btn"
- :title="s__('MergeRequests|Jump to next unresolved thread')"
- data-track-event="click_button"
- data-track-label="mr_next_unresolved_thread"
- data-track-property="click_next_unresolved_thread"
- @click="jumpToNextRelativeDiscussion(fromDiscussionId)"
- >
- <gl-icon name="comment-next" />
- </button>
- </div>
-</template>
diff --git a/app/assets/javascripts/notes/components/email_participants_warning.vue b/app/assets/javascripts/notes/components/email_participants_warning.vue
new file mode 100644
index 00000000000..bb1ff58120a
--- /dev/null
+++ b/app/assets/javascripts/notes/components/email_participants_warning.vue
@@ -0,0 +1,70 @@
+<script>
+import { GlSprintf } from '@gitlab/ui';
+import { s__, sprintf } from '~/locale';
+import { toNounSeriesText } from '~/lib/utils/grammar';
+
+export default {
+ components: {
+ GlSprintf,
+ },
+ props: {
+ emails: {
+ type: Array,
+ required: true,
+ },
+ numberOfLessParticipants: {
+ type: Number,
+ required: false,
+ default: 3,
+ },
+ },
+ data() {
+ return {
+ isShowingMoreParticipants: false,
+ };
+ },
+ computed: {
+ title() {
+ return this.moreParticipantsAvailable
+ ? toNounSeriesText(this.lessParticipants, { onlyCommas: true })
+ : toNounSeriesText(this.emails);
+ },
+ lessParticipants() {
+ return this.emails.slice(0, this.numberOfLessParticipants);
+ },
+ moreLabel() {
+ return sprintf(s__('EmailParticipantsWarning|and %{moreCount} more'), {
+ moreCount: this.emails.length - this.numberOfLessParticipants,
+ });
+ },
+ moreParticipantsAvailable() {
+ return !this.isShowingMoreParticipants && this.emails.length > this.numberOfLessParticipants;
+ },
+ message() {
+ return this.moreParticipantsAvailable
+ ? s__('EmailParticipantsWarning|%{emails}, %{andMore} will be notified of your comment.')
+ : s__('EmailParticipantsWarning|%{emails} will be notified of your comment.');
+ },
+ },
+ methods: {
+ showMoreParticipants() {
+ this.isShowingMoreParticipants = true;
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="issuable-note-warning" data-testid="email-participants-warning">
+ <gl-sprintf :message="message">
+ <template #andMore>
+ <button type="button" class="btn-transparent btn-link" @click="showMoreParticipants">
+ {{ moreLabel }}
+ </button>
+ </template>
+ <template #emails>
+ <span>{{ title }}</span>
+ </template>
+ </gl-sprintf>
+ </div>
+</template>
diff --git a/app/assets/javascripts/notes/components/multiline_comment_form.vue b/app/assets/javascripts/notes/components/multiline_comment_form.vue
index bb13eb87af7..9fbf2c9265c 100644
--- a/app/assets/javascripts/notes/components/multiline_comment_form.vue
+++ b/app/assets/javascripts/notes/components/multiline_comment_form.vue
@@ -1,5 +1,5 @@
<script>
-import { mapActions } from 'vuex';
+import { mapActions, mapState } from 'vuex';
import { GlFormSelect, GlSprintf } from '@gitlab/ui';
import { getSymbol, getLineClasses } from './multiline_comment_utils';
@@ -27,12 +27,13 @@ export default {
};
},
computed: {
+ ...mapState({ selectedCommentPosition: ({ notes }) => notes.selectedCommentPosition }),
lineNumber() {
return this.commentLineOptions[this.commentLineOptions.length - 1].text;
},
},
created() {
- const line = this.lineRange?.start || this.line;
+ const line = this.selectedCommentPosition?.start || this.lineRange?.start || this.line;
this.commentLineStart = {
line_code: line.line_code,
@@ -40,6 +41,8 @@ export default {
old_line: line.old_line,
new_line: line.new_line,
};
+
+ if (this.selectedCommentPosition) return;
this.highlightSelection();
},
destroyed() {
diff --git a/app/assets/javascripts/notes/components/multiline_comment_utils.js b/app/assets/javascripts/notes/components/multiline_comment_utils.js
index 2451400e980..4991695b97e 100644
--- a/app/assets/javascripts/notes/components/multiline_comment_utils.js
+++ b/app/assets/javascripts/notes/components/multiline_comment_utils.js
@@ -48,11 +48,11 @@ export function getLineClasses(line) {
export function commentLineOptions(diffLines, startingLine, lineCode, side = 'left') {
const preferredSide = side === 'left' ? 'old_line' : 'new_line';
const fallbackSide = preferredSide === 'new_line' ? 'old_line' : 'new_line';
- const notMatchType = l => l.type !== 'match';
+ const notMatchType = (l) => l.type !== 'match';
const linesCopy = [...diffLines]; // don't mutate the argument
const startingLineCode = startingLine.line_code;
- const currentIndex = linesCopy.findIndex(line => line.line_code === lineCode);
+ const currentIndex = linesCopy.findIndex((line) => line.line_code === lineCode);
// We're limiting adding comments to only lines above the current line
// to make rendering simpler. Future interations will use a more
@@ -66,10 +66,10 @@ export function commentLineOptions(diffLines, startingLine, lineCode, side = 'le
// If the selected line is "hidden" in an unchanged line block
// or "above" the current group of lines add it to the array so
// that the drop down is not defaulted to empty
- const selectedIndex = lines.findIndex(line => line.line_code === startingLineCode);
+ const selectedIndex = lines.findIndex((line) => line.line_code === startingLineCode);
if (selectedIndex < 0) lines.unshift(startingLine);
- return lines.map(l => {
+ return lines.map((l) => {
const { line_code, type, old_line, new_line } = l;
return {
value: { line_code, type, old_line, new_line },
@@ -103,7 +103,7 @@ export function getCommentedLines(selectedCommentPosition, diffLines) {
};
}
- const findLineCodeIndex = line => position => {
+ const findLineCodeIndex = (line) => (position) => {
return [position.line_code, position.left?.line_code, position.right?.line_code].includes(
line.line_code,
);
diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue
index fc131f548b4..b85cfa83e09 100644
--- a/app/assets/javascripts/notes/components/note_actions.vue
+++ b/app/assets/javascripts/notes/components/note_actions.vue
@@ -206,14 +206,14 @@ export default {
const { project_id, iid } = this.getNoteableData;
if (this.isUserAssigned) {
- assignees = assignees.filter(assignee => assignee.id !== this.author.id);
+ assignees = assignees.filter((assignee) => assignee.id !== this.author.id);
} else {
assignees.push({ id: this.author.id });
}
if (this.targetType === 'issue') {
Api.updateIssue(project_id, iid, {
- assignee_ids: assignees.map(assignee => assignee.id),
+ assignee_ids: assignees.map((assignee) => assignee.id),
})
.then(() => this.handleAssigneeUpdate(assignees))
.catch(() => flash(__('Something went wrong while updating assignees')));
diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue
index 65b89b94eaa..8855ceac3d5 100644
--- a/app/assets/javascripts/notes/components/note_body.vue
+++ b/app/assets/javascripts/notes/components/note_body.vue
@@ -52,8 +52,9 @@ export default {
return this.getDiscussion(this.note.discussion_id);
},
...mapState({
- batchSuggestionsInfo: state => state.notes.batchSuggestionsInfo,
+ batchSuggestionsInfo: (state) => state.notes.batchSuggestionsInfo,
}),
+ ...mapState('diffs', ['defaultSuggestionCommitMessage']),
noteBody() {
return this.note.note;
},
@@ -98,12 +99,16 @@ export default {
formCancelHandler(shouldConfirm, isDirty) {
this.$emit('cancelForm', shouldConfirm, isDirty);
},
- applySuggestion({ suggestionId, flashContainer, callback = () => {} }) {
+ applySuggestion({ suggestionId, flashContainer, callback = () => {}, message }) {
const { discussion_id: discussionId, id: noteId } = this.note;
- return this.submitSuggestion({ discussionId, noteId, suggestionId, flashContainer }).then(
- callback,
- );
+ return this.submitSuggestion({
+ discussionId,
+ noteId,
+ suggestionId,
+ flashContainer,
+ message,
+ }).then(callback);
},
applySuggestionBatch({ flashContainer }) {
return this.submitSuggestionBatch({ flashContainer });
@@ -130,6 +135,7 @@ export default {
:note-html="note.note_html"
:line-type="lineType"
:help-page-path="helpPagePath"
+ :default-commit-message="defaultSuggestionCommitMessage"
@apply="applySuggestion"
@applyBatch="applySuggestionBatch"
@addToBatch="addSuggestionToBatch"
diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue
index 84769bfc7c8..9acb837c27f 100644
--- a/app/assets/javascripts/notes/components/note_form.vue
+++ b/app/assets/javascripts/notes/components/note_form.vue
@@ -3,20 +3,21 @@
import { mapGetters, mapActions, mapState } from 'vuex';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import eventHub from '../event_hub';
-import NoteableWarning from '../../vue_shared/components/notes/noteable_warning.vue';
-import markdownField from '../../vue_shared/components/markdown/field.vue';
+import markdownField from '~/vue_shared/components/markdown/field.vue';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import issuableStateMixin from '../mixins/issuable_state';
import resolvable from '../mixins/resolvable';
import { __, sprintf } from '~/locale';
import { getDraft, updateDraft } from '~/lib/utils/autosave';
+import CommentFieldLayout from './comment_field_layout.vue';
export default {
name: 'NoteForm',
components: {
- NoteableWarning,
markdownField,
+ CommentFieldLayout,
},
- mixins: [issuableStateMixin, resolvable],
+ mixins: [glFeatureFlagsMixin(), issuableStateMixin, resolvable],
props: {
noteBody: {
type: String,
@@ -114,7 +115,7 @@ export default {
'getUserDataByProp',
]),
...mapState({
- withBatchComments: state => state.batchComments?.withBatchComments,
+ withBatchComments: (state) => state.batchComments?.withBatchComments,
}),
...mapGetters('batchComments', ['hasDrafts']),
showBatchCommentsActions() {
@@ -125,8 +126,8 @@ export default {
return (
this.discussion?.notes
- .filter(n => n.resolvable)
- .some(n => n.current_user?.can_resolve_discussion) || this.isDraft
+ .filter((n) => n.resolvable)
+ .some((n) => n.current_user?.can_resolve_discussion) || this.isDraft
);
},
noteHash() {
@@ -192,8 +193,7 @@ export default {
},
canSuggest() {
return (
- this.getNoteableData.can_receive_suggestion &&
- (this.line && this.line.can_receive_suggestion)
+ this.getNoteableData.can_receive_suggestion && this.line && this.line.can_receive_suggestion
);
},
changedCommentText() {
@@ -303,6 +303,9 @@ export default {
this.$emit('handleFormUpdateAddToReview', this.updatedNoteBody, shouldResolve);
},
+ hasEmailParticipants() {
+ return this.getNoteableData.issue_email_participants?.length;
+ },
},
};
</script>
@@ -316,46 +319,41 @@ export default {
></div>
<div class="flash-container timeline-content"></div>
<form :data-line-code="lineCode" class="edit-note common-note-form js-quick-submit gfm-form">
- <noteable-warning
- v-if="hasWarning(getNoteableData)"
- :is-locked="isLocked(getNoteableData)"
- :is-confidential="isConfidential(getNoteableData)"
- :locked-noteable-docs-path="lockedIssueDocsPath"
- :confidential-noteable-docs-path="confidentialIssueDocsPath"
- />
-
- <markdown-field
- :markdown-preview-path="markdownPreviewPath"
- :markdown-docs-path="markdownDocsPath"
- :quick-actions-docs-path="quickActionsDocsPath"
- :line="line"
- :note="discussionNote"
- :can-suggest="canSuggest"
- :add-spacing-classes="false"
- :help-page-path="helpPagePath"
- :show-suggest-popover="showSuggestPopover"
- :textarea-value="updatedNoteBody"
- @handleSuggestDismissed="() => $emit('handleSuggestDismissed')"
- >
- <textarea
- id="note_note"
- ref="textarea"
- slot="textarea"
- v-model="updatedNoteBody"
- :data-supports-quick-actions="!isEditing"
- name="note[note]"
- class="note-textarea js-gfm-input js-note-text js-autosize markdown-area js-vue-issue-note-form"
- data-qa-selector="reply_field"
- dir="auto"
- :aria-label="__('Description')"
- :placeholder="__('Write a comment or drag your files here…')"
- @keydown.meta.enter="handleKeySubmit()"
- @keydown.ctrl.enter="handleKeySubmit()"
- @keydown.exact.up="editMyLastNote()"
- @keydown.exact.esc="cancelHandler(true)"
- @input="onInput"
- ></textarea>
- </markdown-field>
+ <comment-field-layout :noteable-data="getNoteableData">
+ <markdown-field
+ :markdown-preview-path="markdownPreviewPath"
+ :markdown-docs-path="markdownDocsPath"
+ :quick-actions-docs-path="quickActionsDocsPath"
+ :line="line"
+ :note="discussionNote"
+ :can-suggest="canSuggest"
+ :add-spacing-classes="false"
+ :help-page-path="helpPagePath"
+ :show-suggest-popover="showSuggestPopover"
+ :textarea-value="updatedNoteBody"
+ @handleSuggestDismissed="() => $emit('handleSuggestDismissed')"
+ >
+ <template #textarea>
+ <textarea
+ id="note_note"
+ ref="textarea"
+ v-model="updatedNoteBody"
+ :data-supports-quick-actions="!isEditing && !glFeatures.tributeAutocomplete"
+ name="note[note]"
+ class="note-textarea js-gfm-input js-note-text js-autosize markdown-area js-vue-issue-note-form"
+ data-qa-selector="reply_field"
+ dir="auto"
+ :aria-label="__('Description')"
+ :placeholder="__('Write a comment or drag your files here…')"
+ @keydown.meta.enter="handleKeySubmit()"
+ @keydown.ctrl.enter="handleKeySubmit()"
+ @keydown.exact.up="editMyLastNote()"
+ @keydown.exact.esc="cancelHandler(true)"
+ @input="onInput"
+ ></textarea>
+ </template>
+ </markdown-field>
+ </comment-field-layout>
<div class="note-form-actions clearfix">
<template v-if="showBatchCommentsActions">
<p v-if="showResolveDiscussionToggle">
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index 62ee7f30c57..0a9a3da6069 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -201,14 +201,14 @@ export default {
};
this.saveNote(replyData)
- .then(res => {
+ .then((res) => {
if (res.hasFlash !== true) {
this.isReplying = false;
clearDraft(this.autosaveKey);
}
callback();
})
- .catch(err => {
+ .catch((err) => {
this.removePlaceholderNotes();
const msg = __(
'Your comment could not be submitted! Please check your network connection and try again.',
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index 5073922e4a4..eaa64cf7c01 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -296,7 +296,7 @@ export default {
this.updateSuccess();
callback();
})
- .catch(response => {
+ .catch((response) => {
if (response.status === httpStatusCodes.GONE) {
this.removeNote(this.note);
this.updateSuccess();
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index 9eaa4e422d5..e9e687a8743 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -130,7 +130,7 @@ export default {
const { parentElement } = this.$el;
if (parentElement && parentElement.classList.contains('js-vue-notes-event')) {
- parentElement.addEventListener('toggleAward', event => {
+ parentElement.addEventListener('toggleAward', (event) => {
const { awardName, noteId } = event.detail;
this.toggleAward({ awardName, noteId });
});
@@ -217,7 +217,7 @@ export default {
const noteId = hash && hash.replace(/^note_/, '');
if (noteId) {
- const discussion = this.discussions.find(d => d.notes.some(({ id }) => id === noteId));
+ const discussion = this.discussions.find((d) => d.notes.some(({ id }) => id === noteId));
if (discussion) {
this.expandDiscussion({ discussionId: discussion.id });
diff --git a/app/assets/javascripts/notes/components/timeline_toggle.vue b/app/assets/javascripts/notes/components/timeline_toggle.vue
index d1ffe0a3601..8162878f80d 100644
--- a/app/assets/javascripts/notes/components/timeline_toggle.vue
+++ b/app/assets/javascripts/notes/components/timeline_toggle.vue
@@ -50,7 +50,6 @@ export default {
v-gl-tooltip
v-track-event="trackToggleTimelineView(timelineEnabled)"
icon="comments"
- size="small"
:selected="timelineEnabled"
:title="tooltip"
:aria-label="tooltip"
diff --git a/app/assets/javascripts/notes/components/toggle_replies_widget.vue b/app/assets/javascripts/notes/components/toggle_replies_widget.vue
index 0628e1d8647..ab7fa793bdc 100644
--- a/app/assets/javascripts/notes/components/toggle_replies_widget.vue
+++ b/app/assets/javascripts/notes/components/toggle_replies_widget.vue
@@ -26,9 +26,9 @@ export default {
return this.replies[this.replies.length - 1];
},
uniqueAuthors() {
- const authors = this.replies.map(reply => reply.author || {});
+ const authors = this.replies.map((reply) => reply.author || {});
- return uniqBy(authors, author => author.username);
+ return uniqBy(authors, (author) => author.username);
},
className() {
return this.collapsed ? 'collapsed' : 'expanded';
diff --git a/app/assets/javascripts/notes/discussion_filters.js b/app/assets/javascripts/notes/discussion_filters.js
index cdf9a46c5aa..7c9e7703d59 100644
--- a/app/assets/javascripts/notes/discussion_filters.js
+++ b/app/assets/javascripts/notes/discussion_filters.js
@@ -1,13 +1,13 @@
import Vue from 'vue';
import DiscussionFilter from './components/discussion_filter.vue';
-export default store => {
+export default (store) => {
const discussionFilterEl = document.getElementById('js-vue-discussion-filter');
if (discussionFilterEl) {
const { defaultFilter, notesFilters } = discussionFilterEl.dataset;
const filterValues = notesFilters ? JSON.parse(notesFilters) : {};
- const filters = Object.keys(filterValues).map(entry => ({
+ const filters = Object.keys(filterValues).map((entry) => ({
title: entry,
value: filterValues[entry],
}));
diff --git a/app/assets/javascripts/notes/mixins/diff_line_note_form.js b/app/assets/javascripts/notes/mixins/diff_line_note_form.js
index c4a42eb1a98..5ce541781d4 100644
--- a/app/assets/javascripts/notes/mixins/diff_line_note_form.js
+++ b/app/assets/javascripts/notes/mixins/diff_line_note_form.js
@@ -9,9 +9,9 @@ import { formatLineRange } from '~/notes/components/multiline_comment_utils';
export default {
computed: {
...mapState({
- noteableData: state => state.notes.noteableData,
- notesData: state => state.notes.notesData,
- withBatchComments: state => state.batchComments?.withBatchComments,
+ noteableData: (state) => state.notes.noteableData,
+ notesData: (state) => state.notes.notesData,
+ withBatchComments: (state) => state.batchComments?.withBatchComments,
}),
...mapGetters('diffs', ['getDiffFileByHash']),
...mapGetters('batchComments', ['shouldRenderDraftRowInDiscussion', 'draftForDiscussion']),
diff --git a/app/assets/javascripts/notes/mixins/discussion_navigation.js b/app/assets/javascripts/notes/mixins/discussion_navigation.js
index c6932bfacae..96974c4fa2d 100644
--- a/app/assets/javascripts/notes/mixins/discussion_navigation.js
+++ b/app/assets/javascripts/notes/mixins/discussion_navigation.js
@@ -99,7 +99,7 @@ export default {
'getDiscussion',
]),
...mapState({
- currentDiscussionId: state => state.notes.currentDiscussionId,
+ currentDiscussionId: (state) => state.notes.currentDiscussionId,
}),
},
methods: {
diff --git a/app/assets/javascripts/notes/mixins/issuable_state.js b/app/assets/javascripts/notes/mixins/issuable_state.js
index 0ca8c8c98a3..52b67764b70 100644
--- a/app/assets/javascripts/notes/mixins/issuable_state.js
+++ b/app/assets/javascripts/notes/mixins/issuable_state.js
@@ -12,21 +12,10 @@ export default {
lockedIssueDocsPath() {
return this.getNoteableDataByProp('locked_discussion_docs_path');
},
- confidentialIssueDocsPath() {
- return this.getNoteableDataByProp('confidential_issues_docs_path');
- },
},
methods: {
- isConfidential(issue) {
- return Boolean(issue.confidential);
- },
-
isLocked(issue) {
return Boolean(issue.discussion_locked);
},
-
- hasWarning(issue) {
- return this.isConfidential(issue) || this.isLocked(issue);
- },
},
};
diff --git a/app/assets/javascripts/notes/mixins/resolvable.js b/app/assets/javascripts/notes/mixins/resolvable.js
index cef4475ed1d..baada4c5ce8 100644
--- a/app/assets/javascripts/notes/mixins/resolvable.js
+++ b/app/assets/javascripts/notes/mixins/resolvable.js
@@ -15,7 +15,7 @@ export default {
if (notes) {
// Decide resolved state using store. Only valid for discussions.
- return notes.filter(note => !note.system).every(note => note.resolved);
+ return notes.filter((note) => !note.system).every((note) => note.resolved);
}
return resolved;
diff --git a/app/assets/javascripts/notes/sort_discussions.js b/app/assets/javascripts/notes/sort_discussions.js
index a06c23f5f76..ecfa3223039 100644
--- a/app/assets/javascripts/notes/sort_discussions.js
+++ b/app/assets/javascripts/notes/sort_discussions.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import SortDiscussion from './components/sort_discussion.vue';
-export default store => {
+export default (store) => {
const el = document.getElementById('js-vue-sort-issue-discussions');
if (!el) return null;
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index 1fe5d6c2955..c6684efed4d 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -141,7 +141,7 @@ export const updateNote = ({ commit, dispatch }, { endpoint, note }) =>
export const updateOrCreateNotes = ({ commit, state, getters, dispatch }, notes) => {
const { notesById } = getters;
- const debouncedFetchDiscussions = isFetching => {
+ const debouncedFetchDiscussions = (isFetching) => {
if (!isFetching) {
commit(types.SET_FETCHING_DISCUSSIONS, true);
dispatch('fetchDiscussions', { path: state.notesData.discussionsPath });
@@ -159,7 +159,7 @@ export const updateOrCreateNotes = ({ commit, state, getters, dispatch }, notes)
}
};
- notes.forEach(note => {
+ notes.forEach((note) => {
if (notesById[note.id]) {
commit(types.UPDATE_NOTE, note);
} else if (note.type === constants.DISCUSSION_NOTE || note.type === constants.DIFF_NOTE) {
@@ -329,7 +329,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
}
}
- const processQuickActions = res => {
+ const processQuickActions = (res) => {
const { errors: { commands_only: message } = { commands_only: null } } = res;
/*
The following reply means that quick actions have been successfully applied:
@@ -347,7 +347,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
return res;
};
- const processEmojiAward = res => {
+ const processEmojiAward = (res) => {
const { commands_changes: commandsChanges } = res;
const { emoji_award: emojiAward } = commandsChanges || {};
if (!emojiAward) {
@@ -357,7 +357,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
const votesBlock = $('.js-awards-block').eq(0);
return loadAwardsHandler()
- .then(awardsHandler => {
+ .then((awardsHandler) => {
awardsHandler.addAwardToEmojiBar(votesBlock, emojiAward);
awardsHandler.scrollToAwards();
})
@@ -371,7 +371,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
.then(() => res);
};
- const processTimeTracking = res => {
+ const processTimeTracking = (res) => {
const { commands_changes: commandsChanges } = res;
const { spend_time: spendTime, time_estimate: timeEstimate } = commandsChanges || {};
if (spendTime != null || timeEstimate != null) {
@@ -383,7 +383,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
return res;
};
- const removePlaceholder = res => {
+ const removePlaceholder = (res) => {
if (replyId) {
commit(types.REMOVE_PLACEHOLDER_NOTES);
}
@@ -391,7 +391,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
return res;
};
- const processErrors = error => {
+ const processErrors = (error) => {
if (error.response) {
const {
response: { data = {} },
@@ -435,7 +435,7 @@ const pollSuccessCallBack = (resp, commit, state, getters, dispatch) => {
return resp;
};
-const getFetchDataParams = state => {
+const getFetchDataParams = (state) => {
const endpoint = state.notesData.notesPath;
const options = {
headers: {
@@ -559,7 +559,7 @@ export const updateResolvableDiscussionsCounts = ({ commit }) =>
export const submitSuggestion = (
{ commit, dispatch },
- { discussionId, noteId, suggestionId, flashContainer },
+ { discussionId, suggestionId, flashContainer, message },
) => {
const dispatchResolveDiscussion = () =>
dispatch('resolveDiscussion', { discussionId }).catch(() => {});
@@ -567,10 +567,9 @@ export const submitSuggestion = (
commit(types.SET_RESOLVING_DISCUSSION, true);
dispatch('stopPolling');
- return Api.applySuggestion(suggestionId)
- .then(() => commit(types.APPLY_SUGGESTION, { discussionId, noteId, suggestionId }))
+ return Api.applySuggestion(suggestionId, message)
.then(dispatchResolveDiscussion)
- .catch(err => {
+ .catch((err) => {
const defaultMessage = __(
'Something went wrong while applying the suggestion. Please try again.',
);
@@ -590,13 +589,8 @@ export const submitSuggestion = (
export const submitSuggestionBatch = ({ commit, dispatch, state }, { flashContainer }) => {
const suggestionIds = state.batchSuggestionsInfo.map(({ suggestionId }) => suggestionId);
- const applyAllSuggestions = () =>
- state.batchSuggestionsInfo.map(suggestionInfo =>
- commit(types.APPLY_SUGGESTION, suggestionInfo),
- );
-
const resolveAllDiscussions = () =>
- state.batchSuggestionsInfo.map(suggestionInfo => {
+ state.batchSuggestionsInfo.map((suggestionInfo) => {
const { discussionId } = suggestionInfo;
return dispatch('resolveDiscussion', { discussionId }).catch(() => {});
});
@@ -606,10 +600,9 @@ export const submitSuggestionBatch = ({ commit, dispatch, state }, { flashContai
dispatch('stopPolling');
return Api.applySuggestionBatch(suggestionIds)
- .then(() => Promise.all(applyAllSuggestions()))
.then(() => Promise.all(resolveAllDiscussions()))
.then(() => commit(types.CLEAR_SUGGESTION_BATCH))
- .catch(err => {
+ .catch((err) => {
const defaultMessage = __(
'Something went wrong while applying the batch of suggestions. Please try again.',
);
@@ -652,10 +645,10 @@ export const fetchDescriptionVersion = ({ dispatch }, { endpoint, startingVersio
return axios
.get(requestUrl)
- .then(res => {
+ .then((res) => {
dispatch('receiveDescriptionVersion', { descriptionVersion: res.data, versionId });
})
- .catch(error => {
+ .catch((error) => {
dispatch('receiveDescriptionVersionError', error);
Flash(__('Something went wrong while fetching description changes. Please try again.'));
});
@@ -687,7 +680,7 @@ export const softDeleteDescriptionVersion = (
.then(() => {
dispatch('receiveDeleteDescriptionVersion', versionId);
})
- .catch(error => {
+ .catch((error) => {
dispatch('receiveDeleteDescriptionVersionError', error);
Flash(__('Something went wrong while deleting description changes. Please try again.'));
diff --git a/app/assets/javascripts/notes/stores/collapse_utils.js b/app/assets/javascripts/notes/stores/collapse_utils.js
index f34247d4eb0..b2e2f6e2c31 100644
--- a/app/assets/javascripts/notes/stores/collapse_utils.js
+++ b/app/assets/javascripts/notes/stores/collapse_utils.js
@@ -18,7 +18,7 @@ export const getTimeDifferenceMinutes = (noteBeggining, noteEnd) => {
* @param {Object} note
* @returns {Boolean}
*/
-export const isDescriptionSystemNote = note => note.system && note.note === DESCRIPTION_TYPE;
+export const isDescriptionSystemNote = (note) => note.system && note.note === DESCRIPTION_TYPE;
/**
* Collapses the system notes of a description type, e.g. Changed the description, n minutes ago
@@ -29,7 +29,7 @@ export const isDescriptionSystemNote = note => note.system && note.note === DESC
* @param {Array} notes
* @returns {Array}
*/
-export const collapseSystemNotes = notes => {
+export const collapseSystemNotes = (notes) => {
let lastDescriptionSystemNote = null;
let lastDescriptionSystemNoteIndex = -1;
diff --git a/app/assets/javascripts/notes/stores/getters.js b/app/assets/javascripts/notes/stores/getters.js
index 5b3ffa425a0..5891a2e63e3 100644
--- a/app/assets/javascripts/notes/stores/getters.js
+++ b/app/assets/javascripts/notes/stores/getters.js
@@ -2,14 +2,14 @@ import { flattenDeep, clone } from 'lodash';
import * as constants from '../constants';
import { collapseSystemNotes } from './collapse_utils';
-export const discussions = state => {
+export const discussions = (state) => {
let discussionsInState = clone(state.discussions);
// NOTE: not testing bc will be removed when backend is finished.
if (state.isTimelineEnabled) {
discussionsInState = discussionsInState
.reduce((acc, discussion) => {
- const transformedToIndividualNotes = discussion.notes.map(note => ({
+ const transformedToIndividualNotes = discussion.notes.map((note) => ({
...discussion,
id: note.id,
created_at: note.created_at,
@@ -29,52 +29,52 @@ export const discussions = state => {
return collapseSystemNotes(discussionsInState);
};
-export const convertedDisscussionIds = state => state.convertedDisscussionIds;
+export const convertedDisscussionIds = (state) => state.convertedDisscussionIds;
-export const targetNoteHash = state => state.targetNoteHash;
+export const targetNoteHash = (state) => state.targetNoteHash;
-export const getNotesData = state => state.notesData;
+export const getNotesData = (state) => state.notesData;
-export const isNotesFetched = state => state.isNotesFetched;
+export const isNotesFetched = (state) => state.isNotesFetched;
/*
* WARNING: This is an example of an "unnecessary" getter
* more info found here: https://gitlab.com/groups/gitlab-org/-/epics/2913.
*/
-export const sortDirection = state => state.discussionSortOrder;
+export const sortDirection = (state) => state.discussionSortOrder;
-export const persistSortOrder = state => state.persistSortOrder;
+export const persistSortOrder = (state) => state.persistSortOrder;
-export const timelineEnabled = state => state.isTimelineEnabled;
+export const timelineEnabled = (state) => state.isTimelineEnabled;
-export const isLoading = state => state.isLoading;
+export const isLoading = (state) => state.isLoading;
-export const getNotesDataByProp = state => prop => state.notesData[prop];
+export const getNotesDataByProp = (state) => (prop) => state.notesData[prop];
-export const getNoteableData = state => state.noteableData;
+export const getNoteableData = (state) => state.noteableData;
-export const getNoteableDataByProp = state => prop => state.noteableData[prop];
+export const getNoteableDataByProp = (state) => (prop) => state.noteableData[prop];
-export const getBlockedByIssues = state => state.noteableData.blocked_by_issues;
+export const getBlockedByIssues = (state) => state.noteableData.blocked_by_issues;
-export const userCanReply = state => Boolean(state.noteableData.current_user.can_create_note);
+export const userCanReply = (state) => Boolean(state.noteableData.current_user.can_create_note);
-export const openState = state => state.noteableData.state;
+export const openState = (state) => state.noteableData.state;
-export const getUserData = state => state.userData || {};
+export const getUserData = (state) => state.userData || {};
-export const getUserDataByProp = state => prop => state.userData && state.userData[prop];
+export const getUserDataByProp = (state) => (prop) => state.userData && state.userData[prop];
-export const descriptionVersions = state => state.descriptionVersions;
+export const descriptionVersions = (state) => state.descriptionVersions;
-export const notesById = state =>
+export const notesById = (state) =>
state.discussions.reduce((acc, note) => {
- note.notes.every(n => Object.assign(acc, { [n.id]: n }));
+ note.notes.every((n) => Object.assign(acc, { [n.id]: n }));
return acc;
}, {});
-export const noteableType = state => {
+export const noteableType = (state) => {
const { ISSUE_NOTEABLE_TYPE, MERGE_REQUEST_NOTEABLE_TYPE, EPIC_NOTEABLE_TYPE } = constants;
if (state.noteableData.noteableType === EPIC_NOTEABLE_TYPE) {
@@ -84,21 +84,21 @@ export const noteableType = state => {
return state.noteableData.merge_params ? MERGE_REQUEST_NOTEABLE_TYPE : ISSUE_NOTEABLE_TYPE;
};
-const reverseNotes = array => array.slice(0).reverse();
+const reverseNotes = (array) => array.slice(0).reverse();
const isLastNote = (note, state) =>
!note.system && state.userData && note.author && note.author.id === state.userData.id;
-export const getCurrentUserLastNote = state =>
- flattenDeep(reverseNotes(state.discussions).map(note => reverseNotes(note.notes))).find(el =>
+export const getCurrentUserLastNote = (state) =>
+ flattenDeep(reverseNotes(state.discussions).map((note) => reverseNotes(note.notes))).find((el) =>
isLastNote(el, state),
);
-export const getDiscussionLastNote = state => discussion =>
- reverseNotes(discussion.notes).find(el => isLastNote(el, state));
+export const getDiscussionLastNote = (state) => (discussion) =>
+ reverseNotes(discussion.notes).find((el) => isLastNote(el, state));
-export const unresolvedDiscussionsCount = state => state.unresolvedDiscussionsCount;
-export const resolvableDiscussionsCount = state => state.resolvableDiscussionsCount;
+export const unresolvedDiscussionsCount = (state) => state.unresolvedDiscussionsCount;
+export const resolvableDiscussionsCount = (state) => state.resolvableDiscussionsCount;
export const showJumpToNextDiscussion = (state, getters) => (mode = 'discussion') => {
const orderedDiffs =
@@ -109,20 +109,20 @@ export const showJumpToNextDiscussion = (state, getters) => (mode = 'discussion'
return orderedDiffs.length > 1;
};
-export const isDiscussionResolved = (state, getters) => discussionId =>
+export const isDiscussionResolved = (state, getters) => (discussionId) =>
getters.resolvedDiscussionsById[discussionId] !== undefined;
-export const allResolvableDiscussions = state =>
- state.discussions.filter(d => !d.individual_note && d.resolvable);
+export const allResolvableDiscussions = (state) =>
+ state.discussions.filter((d) => !d.individual_note && d.resolvable);
-export const resolvedDiscussionsById = state => {
+export const resolvedDiscussionsById = (state) => {
const map = {};
state.discussions
- .filter(d => d.resolvable)
- .forEach(n => {
+ .filter((d) => d.resolvable)
+ .forEach((n) => {
if (n.notes) {
- const resolved = n.notes.filter(note => note.resolvable).every(note => note.resolved);
+ const resolved = n.notes.filter((note) => note.resolvable).every((note) => note.resolved);
if (resolved) {
map[n.id] = n;
@@ -136,7 +136,7 @@ export const resolvedDiscussionsById = state => {
// Gets Discussions IDs ordered by the date of their initial note
export const unresolvedDiscussionsIdsByDate = (state, getters) =>
getters.allResolvableDiscussions
- .filter(d => !d.resolved)
+ .filter((d) => !d.resolved)
.sort((a, b) => {
const aDate = new Date(a.notes[0].created_at);
const bDate = new Date(b.notes[0].created_at);
@@ -147,7 +147,7 @@ export const unresolvedDiscussionsIdsByDate = (state, getters) =>
return aDate === bDate ? 0 : 1;
})
- .map(d => d.id);
+ .map((d) => d.id);
// Gets Discussions IDs ordered by their position in the diff
//
@@ -156,7 +156,7 @@ export const unresolvedDiscussionsIdsByDate = (state, getters) =>
// line numbers.
export const unresolvedDiscussionsIdsByDiff = (state, getters) =>
getters.allResolvableDiscussions
- .filter(d => !d.resolved && d.active)
+ .filter((d) => !d.resolved && d.active)
.sort((a, b) => {
if (!a.diff_file || !b.diff_file) {
return 0;
@@ -176,7 +176,7 @@ export const unresolvedDiscussionsIdsByDiff = (state, getters) =>
? -1
: 1;
})
- .map(d => d.id);
+ .map((d) => d.id);
export const resolvedDiscussionCount = (state, getters) => {
const resolvedMap = getters.resolvedDiscussionsById;
@@ -184,16 +184,16 @@ export const resolvedDiscussionCount = (state, getters) => {
return Object.keys(resolvedMap).length;
};
-export const discussionTabCounter = state =>
+export const discussionTabCounter = (state) =>
state.discussions.reduce(
(acc, discussion) =>
- acc + discussion.notes.filter(note => !note.system && !note.placeholder).length,
+ acc + discussion.notes.filter((note) => !note.system && !note.placeholder).length,
0,
);
// Returns the list of discussion IDs ordered according to given parameter
// @param {Boolean} diffOrder - is ordered by diff?
-export const unresolvedDiscussionsIdsOrdered = (state, getters) => diffOrder => {
+export const unresolvedDiscussionsIdsOrdered = (state, getters) => (diffOrder) => {
if (diffOrder) {
return getters.unresolvedDiscussionsIdsByDiff;
}
@@ -241,17 +241,17 @@ export const previousUnresolvedDiscussionId = (state, getters) => (discussionId,
getters.findUnresolvedDiscussionIdNeighbor({ discussionId, diffOrder, step: -1 });
// @param {Boolean} diffOrder - is ordered by diff?
-export const firstUnresolvedDiscussionId = (state, getters) => diffOrder => {
+export const firstUnresolvedDiscussionId = (state, getters) => (diffOrder) => {
if (diffOrder) {
return getters.unresolvedDiscussionsIdsByDiff[0];
}
return getters.unresolvedDiscussionsIdsByDate[0];
};
-export const getDiscussion = state => discussionId =>
- state.discussions.find(discussion => discussion.id === discussionId);
+export const getDiscussion = (state) => (discussionId) =>
+ state.discussions.find((discussion) => discussion.id === discussionId);
-export const commentsDisabled = state => state.commentsDisabled;
+export const commentsDisabled = (state) => state.commentsDisabled;
export const suggestionsCount = (state, getters) =>
- Object.values(getters.notesById).filter(n => n.suggestions.length).length;
+ Object.values(getters.notesById).filter((n) => n.suggestions.length).length;
diff --git a/app/assets/javascripts/notes/stores/modules/index.js b/app/assets/javascripts/notes/stores/modules/index.js
index 4421a84a6b1..144a3d7ba90 100644
--- a/app/assets/javascripts/notes/stores/modules/index.js
+++ b/app/assets/javascripts/notes/stores/modules/index.js
@@ -15,7 +15,7 @@ export default () => ({
batchSuggestionsInfo: [],
currentlyFetchingDiscussions: false,
/**
- * selectedCommentPosition & selectedCommentPosition structures are the same as `position.line_range`:
+ * selectedCommentPosition & selectedCommentPositionHover structures are the same as `position.line_range`:
* {
* start: { line_code: string, new_line: number, old_line:number, type: string },
* end: { line_code: string, new_line: number, old_line:number, type: string },
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index 53387b2eaff..2c51ce0d970 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -7,7 +7,7 @@ export default {
[types.ADD_NEW_NOTE](state, data) {
const note = data.discussion ? data.discussion.notes[0] : data;
const { discussion_id, type } = note;
- const [exists] = state.discussions.filter(n => n.id === note.discussion_id);
+ const [exists] = state.discussions.filter((n) => n.id === note.discussion_id);
const isDiscussion = type === constants.DISCUSSION_NOTE || type === constants.DIFF_NOTE;
if (!exists) {
@@ -128,7 +128,7 @@ export default {
// To support legacy notes, should be very rare case.
if (discussion.individual_note && discussion.notes.length > 1) {
- discussion.notes.forEach(n => {
+ discussion.notes.forEach((n) => {
acc.push({
...discussion,
...diffData,
@@ -183,7 +183,7 @@ export default {
const { id, name, username } = state.userData;
const hasEmojiAwardedByCurrentUser = note.award_emoji.filter(
- emoji => `${emoji.name}` === `${data.awardName}` && emoji.user.id === id,
+ (emoji) => `${emoji.name}` === `${data.awardName}` && emoji.user.id === id,
);
if (hasEmojiAwardedByCurrentUser.length) {
@@ -206,7 +206,7 @@ export default {
[types.SET_EXPAND_DISCUSSIONS](state, { discussionIds, expanded }) {
if (discussionIds?.length) {
- discussionIds.forEach(discussionId => {
+ discussionIds.forEach((discussionId) => {
const discussion = utils.findNoteObjectById(state.discussions, discussionId);
Object.assign(discussion, { expanded });
});
@@ -236,7 +236,7 @@ export default {
const noteObj = utils.findNoteObjectById(state.discussions, discussionId);
const comment = utils.findNoteObjectById(noteObj.notes, noteId);
- comment.suggestions = comment.suggestions.map(suggestion => ({
+ comment.suggestions = comment.suggestions.map((suggestion) => ({
...suggestion,
applied: suggestion.applied || suggestion.id === suggestionId,
appliable: false,
@@ -244,13 +244,13 @@ export default {
},
[types.SET_APPLYING_BATCH_STATE](state, isApplyingBatch) {
- state.batchSuggestionsInfo.forEach(suggestionInfo => {
+ state.batchSuggestionsInfo.forEach((suggestionInfo) => {
const { discussionId, noteId, suggestionId } = suggestionInfo;
const noteObj = utils.findNoteObjectById(state.discussions, discussionId);
const comment = utils.findNoteObjectById(noteObj.notes, noteId);
- comment.suggestions = comment.suggestions.map(suggestion => ({
+ comment.suggestions = comment.suggestions.map((suggestion) => ({
...suggestion,
is_applying_batch: suggestion.id === suggestionId && isApplyingBatch,
}));
@@ -278,7 +278,7 @@ export default {
[types.UPDATE_DISCUSSION](state, noteData) {
const note = noteData;
- const selectedDiscussion = state.discussions.find(disc => disc.id === note.id);
+ const selectedDiscussion = state.discussions.find((disc) => disc.id === note.id);
note.expanded = true; // override expand flag to prevent collapse
if (note.diff_file) {
Object.assign(note, {
@@ -289,7 +289,7 @@ export default {
},
[types.UPDATE_DISCUSSION_POSITION](state, { discussionId, position }) {
- const selectedDiscussion = state.discussions.find(disc => disc.id === discussionId);
+ const selectedDiscussion = state.discussions.find((disc) => disc.id === discussionId);
if (selectedDiscussion) Object.assign(selectedDiscussion.position, { ...position });
},
@@ -341,13 +341,13 @@ export default {
},
[types.UPDATE_RESOLVABLE_DISCUSSIONS_COUNTS](state) {
state.resolvableDiscussionsCount = state.discussions.filter(
- discussion => !discussion.individual_note && discussion.resolvable,
+ (discussion) => !discussion.individual_note && discussion.resolvable,
).length;
state.unresolvedDiscussionsCount = state.discussions.filter(
- discussion =>
+ (discussion) =>
!discussion.individual_note &&
discussion.resolvable &&
- discussion.notes.some(note => note.resolvable && !note.resolved),
+ discussion.notes.some((note) => note.resolvable && !note.resolved),
).length;
},
diff --git a/app/assets/javascripts/notes/stores/utils.js b/app/assets/javascripts/notes/stores/utils.js
index 10faac0c32b..6df926e1249 100644
--- a/app/assets/javascripts/notes/stores/utils.js
+++ b/app/assets/javascripts/notes/stores/utils.js
@@ -6,13 +6,13 @@ import createGqClient, { fetchPolicies } from '~/lib/graphql';
// factory function because global flag makes RegExp stateful
const createQuickActionsRegex = () => /^\/\w+.*$/gm;
-export const findNoteObjectById = (notes, id) => notes.filter(n => n.id === id)[0];
+export const findNoteObjectById = (notes, id) => notes.filter((n) => n.id === id)[0];
-export const getQuickActionText = note => {
+export const getQuickActionText = (note) => {
let text = __('Applying command');
const quickActions = AjaxCache.get(gl.GfmAutoComplete.dataSources.commands) || [];
- const executedCommands = quickActions.filter(command => {
+ const executedCommands = quickActions.filter((command) => {
const commandRegex = new RegExp(`/${command.name}`);
return commandRegex.test(note);
});
@@ -29,12 +29,12 @@ export const getQuickActionText = note => {
return text;
};
-export const hasQuickActions = note => createQuickActionsRegex().test(note);
+export const hasQuickActions = (note) => createQuickActionsRegex().test(note);
-export const stripQuickActions = note => note.replace(createQuickActionsRegex(), '').trim();
+export const stripQuickActions = (note) => note.replace(createQuickActionsRegex(), '').trim();
-export const prepareDiffLines = diffLines =>
- diffLines.map(line => ({ ...trimFirstCharOfLineContent(line) }));
+export const prepareDiffLines = (diffLines) =>
+ diffLines.map((line) => ({ ...trimFirstCharOfLineContent(line) }));
export const gqClient = createGqClient(
{},
diff --git a/app/assets/javascripts/notes/utils.js b/app/assets/javascripts/notes/utils.js
index e6c2eb06a51..7966a884eab 100644
--- a/app/assets/javascripts/notes/utils.js
+++ b/app/assets/javascripts/notes/utils.js
@@ -4,7 +4,7 @@
* Tracks snowplow event when User toggles timeline view
* @param {Boolean} enabled that will be send as a property for the event
*/
-export const trackToggleTimelineView = enabled => ({
+export const trackToggleTimelineView = (enabled) => ({
category: 'Incident Management',
action: 'toggle_incident_comments_into_timeline_view',
label: 'Status',