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:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-08-29 09:09:31 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-08-29 09:09:31 +0300
commitf2ba923aa70596b5ca56cbf8b58ac33dc208c6a8 (patch)
treedc8838b0323bdc7a0763b7caa18776052aa65a3a /app/assets/javascripts/notes
parent5e5c529ef67c6902c69613dd0d490613fa9ef505 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/notes')
-rw-r--r--app/assets/javascripts/notes/components/note_actions.vue15
-rw-r--r--app/assets/javascripts/notes/components/note_actions/timeline_event_button.vue49
-rw-r--r--app/assets/javascripts/notes/constants.js2
-rw-r--r--app/assets/javascripts/notes/graphql/promote_timeline_event.mutation.graphql8
-rw-r--r--app/assets/javascripts/notes/index.js2
-rw-r--r--app/assets/javascripts/notes/stores/actions.js55
-rw-r--r--app/assets/javascripts/notes/stores/getters.js7
-rw-r--r--app/assets/javascripts/notes/stores/modules/index.js1
-rw-r--r--app/assets/javascripts/notes/stores/mutation_types.js3
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js3
10 files changed, 142 insertions, 3 deletions
diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue
index c7f293a219a..9806f8e5dc2 100644
--- a/app/assets/javascripts/notes/components/note_actions.vue
+++ b/app/assets/javascripts/notes/components/note_actions.vue
@@ -1,6 +1,6 @@
<script>
import { GlTooltipDirective, GlIcon, GlButton, GlDropdownItem } from '@gitlab/ui';
-import { mapActions, mapGetters } from 'vuex';
+import { mapActions, mapGetters, mapState } from 'vuex';
import Api from '~/api';
import resolvedStatusMixin from '~/batch_comments/mixins/resolved_status';
import createFlash from '~/flash';
@@ -11,6 +11,7 @@ import UserAccessRoleBadge from '~/vue_shared/components/user_access_role_badge.
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { splitCamelCase } from '~/lib/utils/text_utility';
import ReplyButton from './note_actions/reply_button.vue';
+import TimelineEventButton from './note_actions/timeline_event_button.vue';
export default {
i18n: {
@@ -23,6 +24,7 @@ export default {
components: {
GlIcon,
ReplyButton,
+ TimelineEventButton,
GlButton,
GlDropdownItem,
UserAccessRoleBadge,
@@ -133,7 +135,8 @@ export default {
},
},
computed: {
- ...mapGetters(['getUserDataByProp', 'getNoteableData']),
+ ...mapState(['isPromoteCommentToTimelineEventInProgress']),
+ ...mapGetters(['getUserDataByProp', 'getNoteableData', 'canUserAddIncidentTimelineEvents']),
shouldShowActionsDropdown() {
return this.currentUserId && (this.canEdit || this.canReportAsAbuse);
},
@@ -199,7 +202,7 @@ export default {
},
},
methods: {
- ...mapActions(['toggleAwardRequest']),
+ ...mapActions(['toggleAwardRequest', 'promoteCommentToTimelineEvent']),
onEdit() {
this.$emit('handleEdit');
},
@@ -292,6 +295,12 @@ export default {
class="line-resolve-btn note-action-button"
@click="onResolve"
/>
+ <timeline-event-button
+ v-if="canUserAddIncidentTimelineEvents"
+ :note-id="noteId"
+ :is-promotion-in-progress="isPromoteCommentToTimelineEventInProgress"
+ @click-promote-comment-to-event="promoteCommentToTimelineEvent"
+ />
<emoji-picker
v-if="canAwardEmoji"
toggle-class="note-action-button note-emoji-button btn-icon btn-default-tertiary"
diff --git a/app/assets/javascripts/notes/components/note_actions/timeline_event_button.vue b/app/assets/javascripts/notes/components/note_actions/timeline_event_button.vue
new file mode 100644
index 00000000000..4dd0c968282
--- /dev/null
+++ b/app/assets/javascripts/notes/components/note_actions/timeline_event_button.vue
@@ -0,0 +1,49 @@
+<script>
+import { GlTooltipDirective, GlButton } from '@gitlab/ui';
+import { __ } from '~/locale';
+
+export default {
+ i18n: {
+ buttonText: __('Add comment to incident timeline'),
+ addError: __('Error promoting the note to timeline event: %{error}'),
+ addGenericError: __('Something went wrong while promoting the note to timeline event.'),
+ },
+ components: {
+ GlButton,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ noteId: {
+ type: [String, Number],
+ required: true,
+ },
+ isPromotionInProgress: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ methods: {
+ handleButtonClick() {
+ this.$emit('click-promote-comment-to-event', {
+ noteId: this.noteId,
+ addError: this.$options.i18n.addError,
+ addGenericError: this.$options.i18n.addGenericError,
+ });
+ },
+ },
+};
+</script>
+<template>
+ <span v-gl-tooltip :title="$options.i18n.buttonText">
+ <gl-button
+ category="tertiary"
+ icon="clock"
+ :aria-label="$options.i18n.buttonText"
+ :disabled="isPromotionInProgress"
+ @click="handleButtonClick"
+ />
+ </span>
+</template>
diff --git a/app/assets/javascripts/notes/constants.js b/app/assets/javascripts/notes/constants.js
index a5f459c8910..88f438975f6 100644
--- a/app/assets/javascripts/notes/constants.js
+++ b/app/assets/javascripts/notes/constants.js
@@ -13,6 +13,7 @@ export const MERGED = 'merged';
export const ISSUE_NOTEABLE_TYPE = 'Issue';
export const EPIC_NOTEABLE_TYPE = 'Epic';
export const MERGE_REQUEST_NOTEABLE_TYPE = 'MergeRequest';
+export const INCIDENT_NOTEABLE_TYPE = 'INCIDENT'; // TODO: check if value can be converted to `Incident`
export const UNRESOLVE_NOTE_METHOD_NAME = 'delete';
export const RESOLVE_NOTE_METHOD_NAME = 'post';
export const DESCRIPTION_TYPE = 'changed the description';
@@ -31,6 +32,7 @@ export const NOTEABLE_TYPE_MAPPING = {
Issue: ISSUE_NOTEABLE_TYPE,
MergeRequest: MERGE_REQUEST_NOTEABLE_TYPE,
Epic: EPIC_NOTEABLE_TYPE,
+ Incident: INCIDENT_NOTEABLE_TYPE,
};
export const DISCUSSION_FILTER_TYPES = {
diff --git a/app/assets/javascripts/notes/graphql/promote_timeline_event.mutation.graphql b/app/assets/javascripts/notes/graphql/promote_timeline_event.mutation.graphql
new file mode 100644
index 00000000000..c9df9cfd6d3
--- /dev/null
+++ b/app/assets/javascripts/notes/graphql/promote_timeline_event.mutation.graphql
@@ -0,0 +1,8 @@
+mutation PromoteTimelineEvent($input: TimelineEventPromoteFromNoteInput!) {
+ timelineEventPromoteFromNote(input: $input) {
+ timelineEvent {
+ id
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js
index 27e54a1ea69..054a5bd36e2 100644
--- a/app/assets/javascripts/notes/index.js
+++ b/app/assets/javascripts/notes/index.js
@@ -1,4 +1,5 @@
import Vue from 'vue';
+import { parseBoolean } from '~/lib/utils/common_utils';
import NotesApp from './components/notes_app.vue';
import initDiscussionFilters from './discussion_filters';
import { store } from './stores';
@@ -39,6 +40,7 @@ export default () => {
username: parsedUserData.username,
avatar_url: parsedUserData.avatar_path || parsedUserData.avatar_url,
path: parsedUserData.path,
+ can_add_timeline_events: parseBoolean(notesDataset.canAddTimelineEvents),
};
}
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index 82417c9134b..fcef26d720c 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -6,6 +6,7 @@ import createFlash from '~/flash';
import { EVENT_ISSUABLE_VUE_APP_CHANGE } from '~/issuable/constants';
import axios from '~/lib/utils/axios_utils';
import { __, sprintf } from '~/locale';
+import toast from '~/vue_shared/plugins/global_toast';
import { confidentialWidget } from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
import updateIssueLockMutation from '~/sidebar/components/lock/mutations/update_issue_lock.mutation.graphql';
import updateMergeRequestLockMutation from '~/sidebar/components/lock/mutations/update_merge_request_lock.mutation.graphql';
@@ -18,6 +19,12 @@ import sidebarTimeTrackingEventHub from '~/sidebar/event_hub';
import TaskList from '~/task_list';
import mrWidgetEventHub from '~/vue_merge_request_widget/event_hub';
import SidebarStore from '~/sidebar/stores/sidebar_store';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
+import { TYPE_NOTE } from '~/graphql_shared/constants';
+import notesEventHub from '../event_hub';
+
+import promoteTimelineEvent from '../graphql/promote_timeline_event.mutation.graphql';
+
import * as constants from '../constants';
import * as types from './mutation_types';
import * as utils from './utils';
@@ -226,6 +233,54 @@ export const updateOrCreateNotes = ({ commit, state, getters, dispatch }, notes)
});
};
+export const promoteCommentToTimelineEvent = (
+ { commit },
+ { noteId, addError, addGenericError },
+) => {
+ commit(types.SET_PROMOTE_COMMENT_TO_TIMELINE_PROGRESS, true); // Set loading state
+ return utils.gqClient
+ .mutate({
+ mutation: promoteTimelineEvent,
+ variables: {
+ input: {
+ noteId: convertToGraphQLId(TYPE_NOTE, noteId),
+ },
+ },
+ })
+ .then(({ data = {} }) => {
+ const errors = data.timelineEventPromoteFromNote?.errors;
+ if (errors.length) {
+ const errorMessage = sprintf(addError, {
+ error: errors.join('. '),
+ });
+ throw new Error(errorMessage);
+ } else {
+ notesEventHub.$emit('comment-promoted-to-timeline-event');
+ toast(__('Comment added to the timeline.'));
+ }
+ })
+ .catch((error) => {
+ const message = error.message || addGenericError;
+
+ let captureError = false;
+ let errorObj = null;
+
+ if (message === addGenericError) {
+ captureError = true;
+ errorObj = error;
+ }
+
+ createFlash({
+ message,
+ captureError,
+ error: errorObj,
+ });
+ })
+ .finally(() => {
+ commit(types.SET_PROMOTE_COMMENT_TO_TIMELINE_PROGRESS, false); // Revert loading state
+ });
+};
+
export const replyToDiscussion = (
{ commit, state, getters, dispatch },
{ endpoint, data: reply },
diff --git a/app/assets/javascripts/notes/stores/getters.js b/app/assets/javascripts/notes/stores/getters.js
index 1fe82d96435..6876220f75c 100644
--- a/app/assets/javascripts/notes/stores/getters.js
+++ b/app/assets/javascripts/notes/stores/getters.js
@@ -93,6 +93,13 @@ export const getUserDataByProp = (state) => (prop) => state.userData && state.us
export const descriptionVersions = (state) => state.descriptionVersions;
+export const canUserAddIncidentTimelineEvents = (state) => {
+ return (
+ state.userData.can_add_timeline_events &&
+ state.noteableData.type === constants.NOTEABLE_TYPE_MAPPING.Incident
+ );
+};
+
export const notesById = (state) =>
state.discussions.reduce((acc, note) => {
note.notes.every((n) => Object.assign(acc, { [n.id]: n }));
diff --git a/app/assets/javascripts/notes/stores/modules/index.js b/app/assets/javascripts/notes/stores/modules/index.js
index f779aad5679..7ba1f470b05 100644
--- a/app/assets/javascripts/notes/stores/modules/index.js
+++ b/app/assets/javascripts/notes/stores/modules/index.js
@@ -30,6 +30,7 @@ export default () => ({
isNotesFetched: false,
isLoading: true,
isLoadingDescriptionVersion: false,
+ isPromoteCommentToTimelineEventInProgress: false,
// holds endpoints and permissions provided through haml
notesData: {
diff --git a/app/assets/javascripts/notes/stores/mutation_types.js b/app/assets/javascripts/notes/stores/mutation_types.js
index e28a7bc5cdd..42df6bc0980 100644
--- a/app/assets/javascripts/notes/stores/mutation_types.js
+++ b/app/assets/javascripts/notes/stores/mutation_types.js
@@ -57,3 +57,6 @@ export const RECEIVE_DESCRIPTION_VERSION_ERROR = 'RECEIVE_DESCRIPTION_VERSION_ER
export const REQUEST_DELETE_DESCRIPTION_VERSION = 'REQUEST_DELETE_DESCRIPTION_VERSION';
export const RECEIVE_DELETE_DESCRIPTION_VERSION = 'RECEIVE_DELETE_DESCRIPTION_VERSION';
export const RECEIVE_DELETE_DESCRIPTION_VERSION_ERROR = 'RECEIVE_DELETE_DESCRIPTION_VERSION_ERROR';
+
+// Incidents
+export const SET_PROMOTE_COMMENT_TO_TIMELINE_PROGRESS = 'SET_PROMOTE_COMMENT_TO_TIMELINE_PROGRESS';
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index 0823eacf1b7..83c15c12eac 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -425,4 +425,7 @@ export default {
[types.SET_DONE_FETCHING_BATCH_DISCUSSIONS](state, value) {
state.doneFetchingBatchDiscussions = value;
},
+ [types.SET_PROMOTE_COMMENT_TO_TIMELINE_PROGRESS](state, value) {
+ state.isPromoteCommentToTimelineEventInProgress = value;
+ },
};