diff options
Diffstat (limited to 'app/assets/javascripts/issues/show/components/header_actions.vue')
-rw-r--r-- | app/assets/javascripts/issues/show/components/header_actions.vue | 197 |
1 files changed, 182 insertions, 15 deletions
diff --git a/app/assets/javascripts/issues/show/components/header_actions.vue b/app/assets/javascripts/issues/show/components/header_actions.vue index 9d92b5cf954..4d9b69ddf99 100644 --- a/app/assets/javascripts/issues/show/components/header_actions.vue +++ b/app/assets/javascripts/issues/show/components/header_actions.vue @@ -9,17 +9,30 @@ import { GlModalDirective, GlTooltipDirective, } from '@gitlab/ui'; +import * as Sentry from '@sentry/browser'; import { mapActions, mapGetters, mapState } from 'vuex'; -import { createAlert, VARIANT_SUCCESS } from '~/flash'; +import { createAlert, VARIANT_SUCCESS } from '~/alert'; import { EVENT_ISSUABLE_VUE_APP_CHANGE } from '~/issuable/constants'; -import { IssueType, STATUS_CLOSED } from '~/issues/constants'; -import { ISSUE_STATE_EVENT_CLOSE, ISSUE_STATE_EVENT_REOPEN } from '~/issues/show/constants'; +import { STATUS_CLOSED, TYPE_INCIDENT, TYPE_ISSUE, IssuableTypeText } from '~/issues/constants'; +import { + ISSUE_STATE_EVENT_CLOSE, + ISSUE_STATE_EVENT_REOPEN, + NEW_ACTIONS_POPOVER_KEY, +} from '~/issues/show/constants'; import { capitalizeFirstCharacter } from '~/lib/utils/text_utility'; +import { getCookie, parseBoolean, setCookie } from '~/lib/utils/common_utils'; import { visitUrl } from '~/lib/utils/url_utility'; import { s__, __, sprintf } from '~/locale'; import eventHub from '~/notes/event_hub'; import Tracking from '~/tracking'; +import toast from '~/vue_shared/plugins/global_toast'; import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_selector.vue'; +import NewHeaderActionsPopover from '~/issues/show/components/new_header_actions_popover.vue'; +import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue'; +import IssuableLockForm from '~/sidebar/components/lock/issuable_lock_form.vue'; +import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; +import issueReferenceQuery from '~/sidebar/queries/issue_reference.query.graphql'; +import issuesEventHub from '../event_hub'; import promoteToEpicMutation from '../queries/promote_to_epic.mutation.graphql'; import updateIssueMutation from '../queries/update_issue.mutation.graphql'; import DeleteIssueModal from './delete_issue_modal.vue'; @@ -35,6 +48,8 @@ export default { }, deleteModalId: 'delete-modal-id', i18n: { + edit: __('Edit'), + editTitleAndDescription: __('Edit title and description'), promoteErrorMessage: __( 'Something went wrong while promoting the issue to an epic. Please try again.', ), @@ -42,6 +57,8 @@ export default { 'The issue was successfully promoted to an epic. Redirecting to epic...', ), reportAbuse: __('Report abuse to administrator'), + referenceFetchError: __('An error occurred while fetching reference'), + copyReferenceText: __('Copy reference'), }, components: { DeleteIssueModal, @@ -52,12 +69,15 @@ export default { GlLink, GlModal, AbuseCategorySelector, + NewHeaderActionsPopover, + SidebarSubscriptionsWidget, + IssuableLockForm, }, directives: { GlModal: GlModalDirective, GlTooltip: GlTooltipDirective, }, - mixins: [trackingMixin], + mixins: [trackingMixin, glFeatureFlagMixin()], inject: { canCreateIssue: { default: false, @@ -80,6 +100,9 @@ export default { iid: { default: '', }, + issuableId: { + default: '', + }, isIssueAuthor: { default: false, }, @@ -87,7 +110,7 @@ export default { default: '', }, issueType: { - default: IssueType.Issue, + default: TYPE_ISSUE, }, newIssuePath: { default: '', @@ -104,22 +127,53 @@ export default { reportedFromUrl: { default: '', }, + issuableEmailAddress: { + default: '', + }, + fullPath: { + default: '', + }, }, data() { return { isReportAbuseDrawerOpen: false, }; }, + apollo: { + issuableReference: { + query: issueReferenceQuery, + variables() { + return { + fullPath: this.fullPath, + iid: this.iid, + }; + }, + update(data) { + return data.workspace?.issuable?.reference || ''; + }, + skip() { + return !this.isMrSidebarMoved; + }, + error(error) { + createAlert({ message: this.$options.i18n.referenceFetchError }); + Sentry.captureException(error); + }, + }, + }, computed: { ...mapState(['isToggleStateButtonLoading']), ...mapGetters(['openState', 'getBlockedByIssues']), + ...mapGetters(['getNoteableData']), + isLocked() { + return this.getNoteableData.discussion_locked; + }, isClosed() { return this.openState === STATUS_CLOSED; }, issueTypeText() { const issueTypeTexts = { - [IssueType.Issue]: s__('HeaderAction|issue'), - [IssueType.Incident]: s__('HeaderAction|incident'), + [TYPE_ISSUE]: s__('HeaderAction|issue'), + [TYPE_INCIDENT]: s__('HeaderAction|incident'), }; return issueTypeTexts[this.issueType] ?? this.issueType; @@ -156,6 +210,17 @@ export default { hasMobileDropdown() { return this.hasDesktopDropdown || this.showToggleIssueStateButton; }, + copyMailAddressText() { + return sprintf(__('Copy %{issueType} email address'), { + issueType: IssuableTypeText[this.issueType], + }); + }, + isMrSidebarMoved() { + return this.glFeatures.movedMrSidebar; + }, + showLockIssueOption() { + return this.isMrSidebarMoved && this.issueType === TYPE_ISSUE; + }, }, created() { eventHub.$on('toggle.issuable.state', this.toggleIssueState); @@ -165,6 +230,7 @@ export default { }, methods: { ...mapActions(['toggleStateButtonLoading']), + ...mapActions(['updateLockedAttribute']), toggleIssueState() { if (!this.isClosed && this.getBlockedByIssues?.length) { this.$refs.blockedByIssuesModal.show(); @@ -240,12 +306,27 @@ export default { this.toggleStateButtonLoading(false); }); }, + edit() { + issuesEventHub.$emit('open.form'); + }, + dismissPopover() { + if (this.isMrSidebarMoved && !parseBoolean(getCookie(`${NEW_ACTIONS_POPOVER_KEY}`))) { + setCookie(NEW_ACTIONS_POPOVER_KEY, true); + } + }, + copyReference() { + toast(__('Reference copied')); + }, + copyEmailAddress() { + toast(__('Email address copied')); + }, }, + TYPE_ISSUE, }; </script> <template> - <div class="detail-page-header-actions gl-display-flex gl-align-self-start"> + <div class="detail-page-header-actions gl-display-flex gl-align-self-start gl-gap-3"> <gl-dropdown v-if="hasMobileDropdown" class="gl-sm-display-none! w-100" @@ -255,6 +336,24 @@ export default { data-testid="mobile-dropdown" :loading="isToggleStateButtonLoading" > + <template v-if="isMrSidebarMoved"> + <sidebar-subscriptions-widget + :iid="String(iid)" + :full-path="fullPath" + :issuable-type="$options.TYPE_ISSUE" + data-testid="notification-toggle" + /> + + <gl-dropdown-divider /> + </template> + + <template v-if="showLockIssueOption"> + <issuable-lock-form :is-editable="false" data-testid="lock-issue-toggle" /> + </template> + + <gl-dropdown-item v-if="canUpdateIssue" @click="edit"> + {{ $options.i18n.edit }} + </gl-dropdown-item> <gl-dropdown-item v-if="showToggleIssueStateButton" :data-qa-selector="`mobile_${qaSelector}`" @@ -268,9 +367,21 @@ export default { <gl-dropdown-item v-if="canPromoteToEpic" @click="promoteToEpic"> {{ __('Promote to epic') }} </gl-dropdown-item> - <gl-dropdown-item v-if="!isIssueAuthor" @click="toggleReportAbuseDrawer(true)"> - {{ $options.i18n.reportAbuse }} - </gl-dropdown-item> + <template v-if="isMrSidebarMoved"> + <gl-dropdown-item + :data-clipboard-text="issuableReference" + data-testid="copy-reference" + @click="copyReference" + >{{ $options.i18n.copyReferenceText }}</gl-dropdown-item + > + <gl-dropdown-item + v-if="issuableEmailAddress" + :data-clipboard-text="issuableEmailAddress" + data-testid="copy-email" + @click="copyEmailAddress" + >{{ copyMailAddressText }}</gl-dropdown-item + > + </template> <gl-dropdown-item v-if="canReportSpam" :href="submitAsSpamPath" @@ -289,13 +400,33 @@ export default { {{ deleteButtonText }} </gl-dropdown-item> </template> + <gl-dropdown-item + v-if="!isIssueAuthor" + data-testid="report-abuse-item" + @click="toggleReportAbuseDrawer(true)" + > + {{ $options.i18n.reportAbuse }} + </gl-dropdown-item> </gl-dropdown> <gl-button + v-if="canUpdateIssue" + v-gl-tooltip.bottom + :title="$options.i18n.editTitleAndDescription" + :aria-label="$options.i18n.editTitleAndDescription" + class="js-issuable-edit gl-display-none gl-sm-display-block" + data-testid="edit-button" + @click="edit" + > + {{ $options.i18n.edit }} + </gl-button> + + <gl-button v-if="showToggleIssueStateButton" class="gl-display-none gl-sm-display-inline-flex!" :data-qa-selector="qaSelector" :loading="isToggleStateButtonLoading" + data-testid="toggle-button" @click="toggleIssueState" > {{ buttonText }} @@ -303,8 +434,9 @@ export default { <gl-dropdown v-if="hasDesktopDropdown" + id="new-actions-header-dropdown" v-gl-tooltip.hover - class="gl-display-none gl-sm-display-inline-flex! gl-ml-3" + class="gl-display-none gl-sm-display-inline-flex!" icon="ellipsis_v" category="tertiary" data-qa-selector="issue_actions_ellipsis_dropdown" @@ -315,7 +447,19 @@ export default { data-testid="desktop-dropdown" no-caret right + @shown="dismissPopover" > + <template v-if="isMrSidebarMoved"> + <sidebar-subscriptions-widget + :iid="String(iid)" + :full-path="fullPath" + :issuable-type="$options.TYPE_ISSUE" + data-testid="notification-toggle" + /> + + <gl-dropdown-divider /> + </template> + <gl-dropdown-item v-if="canCreateIssue" :href="newIssuePath"> {{ newIssueTypeText }} </gl-dropdown-item> @@ -327,9 +471,24 @@ export default { > {{ __('Promote to epic') }} </gl-dropdown-item> - <gl-dropdown-item v-if="!isIssueAuthor" @click="toggleReportAbuseDrawer(true)"> - {{ $options.i18n.reportAbuse }} - </gl-dropdown-item> + <template v-if="showLockIssueOption"> + <issuable-lock-form :is-editable="false" data-testid="lock-issue-toggle" /> + </template> + <template v-if="isMrSidebarMoved"> + <gl-dropdown-item + :data-clipboard-text="issuableReference" + data-testid="copy-reference" + @click="copyReference" + >{{ $options.i18n.copyReferenceText }}</gl-dropdown-item + > + <gl-dropdown-item + v-if="issuableEmailAddress" + :data-clipboard-text="issuableEmailAddress" + data-testid="copy-email" + @click="copyEmailAddress" + >{{ copyMailAddressText }}</gl-dropdown-item + > + </template> <gl-dropdown-item v-if="canReportSpam" :href="submitAsSpamPath" @@ -349,8 +508,16 @@ export default { {{ deleteButtonText }} </gl-dropdown-item> </template> + <gl-dropdown-item + v-if="!isIssueAuthor" + data-testid="report-abuse-item" + @click="toggleReportAbuseDrawer(true)" + > + {{ $options.i18n.reportAbuse }} + </gl-dropdown-item> </gl-dropdown> + <new-header-actions-popover v-if="isMrSidebarMoved" :issue-type="issueType" /> <gl-modal ref="blockedByIssuesModal" modal-id="blocked-by-issues-modal" |