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-05-19 10:33:21 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-05-19 10:33:21 +0300
commit36a59d088eca61b834191dacea009677a96c052f (patch)
treee4f33972dab5d8ef79e3944a9f403035fceea43f /app/assets/javascripts/sidebar
parenta1761f15ec2cae7c7f7bbda39a75494add0dfd6f (diff)
Add latest changes from gitlab-org/gitlab@15-0-stable-eev15.0.0-rc42
Diffstat (limited to 'app/assets/javascripts/sidebar')
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue7
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignee_title.vue5
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/sidebar_participant.vue20
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue32
-rw-r--r--app/assets/javascripts/sidebar/components/attention_requested_toggle.vue33
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_content.vue31
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/crm_contacts/crm_contacts.vue16
-rw-r--r--app/assets/javascripts/sidebar/components/date/sidebar_formatted_date.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue55
-rw-r--r--app/assets/javascripts/sidebar/components/participants/participants.vue5
-rw-r--r--app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer_list.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/reviewers/reviewer_avatar_link.vue9
-rw-r--r--app/assets/javascripts/sidebar/components/reviewers/reviewer_title.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/reviewers/uncollapsed_reviewer_list.vue7
-rw-r--r--app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue15
-rw-r--r--app/assets/javascripts/sidebar/components/sidebar_editable_item.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue18
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/report.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue8
-rw-r--r--app/assets/javascripts/sidebar/mount_sidebar.js10
-rw-r--r--app/assets/javascripts/sidebar/queries/remove_attention_request.mutation.graphql7
-rw-r--r--app/assets/javascripts/sidebar/queries/request_attention.mutation.graphql5
-rw-r--r--app/assets/javascripts/sidebar/queries/reviewer_rereview.mutation.graphql2
-rw-r--r--app/assets/javascripts/sidebar/queries/toggle_attention_requested.mutation.graphql7
-rw-r--r--app/assets/javascripts/sidebar/queries/update_merge_request_milestone.mutation.graphql2
-rw-r--r--app/assets/javascripts/sidebar/services/sidebar_service.js17
-rw-r--r--app/assets/javascripts/sidebar/sidebar_mediator.js33
-rw-r--r--app/assets/javascripts/sidebar/stores/sidebar_store.js35
31 files changed, 291 insertions, 122 deletions
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue b/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue
index 578c344da02..ef40de82d01 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue
@@ -101,14 +101,15 @@ export default {
<template>
<!-- must be `d-inline-block` or parent flex-basis causes width issues -->
<gl-link
- v-gl-tooltip="tooltipOption"
:href="assigneeUrl"
:title="tooltipTitle"
- class="gl-display-inline-block"
+ :data-user-id="user.id"
+ data-placement="left"
+ class="gl-display-inline-block js-user-link"
>
<!-- use d-flex so that slot can be appropriately styled -->
<span class="gl-display-flex">
- <assignee-avatar :user="user" :img-size="32" :issuable-type="issuableType" />
+ <assignee-avatar :user="user" :img-size="24" :issuable-type="issuableType" />
<slot></slot>
</span>
</gl-link>
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue b/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
index f98aa0dc77d..6e18cf36690 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
@@ -1,6 +1,7 @@
<script>
import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
import { n__, __ } from '~/locale';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
name: 'AssigneeTitle',
@@ -8,6 +9,7 @@ export default {
GlLoadingIcon,
GlIcon,
},
+ mixins: [glFeatureFlagMixin()],
props: {
loading: {
type: Boolean,
@@ -45,7 +47,7 @@ export default {
};
</script>
<template>
- <div class="hide-collapsed gl-line-height-20 gl-mb-2 gl-text-gray-900">
+ <div class="hide-collapsed gl-line-height-20 gl-mb-2 gl-text-gray-900 gl-font-weight-bold">
{{ assigneeTitle }}
<gl-loading-icon v-if="loading" size="sm" inline class="align-bottom" />
<a
@@ -63,6 +65,7 @@ export default {
v-if="showToggle"
:aria-label="__('Toggle sidebar')"
class="gutter-toggle float-right js-sidebar-toggle"
+ :class="{ 'gl-display-block gl-md-display-none!': glFeatures.movedMrSidebar }"
href="#"
role="button"
>
diff --git a/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue b/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue
index 856687c00ae..50b1955abcc 100644
--- a/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue
@@ -124,7 +124,11 @@ export default {
:issuable-type="issuableType"
/>
<button v-if="hasMoreThanTwoAssignees" class="btn-link" type="button">
- <span class="avatar-counter sidebar-avatar-counter"> {{ sidebarAvatarCounter }} </span>
+ <span
+ class="avatar-counter sidebar-avatar-counter gl-display-flex gl-align-items-center gl-pl-3"
+ >
+ {{ sidebarAvatarCounter }}
+ </span>
<gl-icon
v-if="isMergeRequest && !allAssigneesCanMerge"
name="warning-solid"
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_participant.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_participant.vue
index 19f588b28be..e9c68008143 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_participant.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_participant.vue
@@ -3,6 +3,11 @@ import { GlAvatarLabeled, GlAvatarLink, GlIcon } from '@gitlab/ui';
import { IssuableType } from '~/issues/constants';
import { s__, sprintf } from '~/locale';
+const AVAILABILITY_STATUS = {
+ NOT_SET: 'NOT_SET',
+ BUSY: 'BUSY',
+};
+
export default {
components: {
GlAvatarLabeled,
@@ -22,12 +27,17 @@ export default {
},
computed: {
userLabel() {
- if (!this.user.status) {
- return this.user.name;
+ const { name, status } = this.user;
+ if (!status || status?.availability !== AVAILABILITY_STATUS.BUSY) {
+ return name;
}
- return sprintf(s__('UserAvailability|%{author} (Busy)'), {
- author: this.user.name,
- });
+ return sprintf(
+ s__('UserAvailability|%{author} (Busy)'),
+ {
+ author: name,
+ },
+ false,
+ );
},
hasCannotMergeIcon() {
return this.issuableType === IssuableType.MergeRequest && !this.user.canMerge;
diff --git a/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue b/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue
index 8717d205dcb..01d29da5486 100644
--- a/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue
@@ -35,13 +35,6 @@ export default {
firstUser() {
return this.users[0];
},
- hasOneUser() {
- if (this.showVerticalList) {
- return false;
- }
-
- return this.users.length === 1;
- },
hiddenAssigneesLabel() {
const { numberOfHiddenAssignees } = this;
return sprintf(__('+ %{numberOfHiddenAssignees} more'), { numberOfHiddenAssignees });
@@ -90,30 +83,15 @@ export default {
</script>
<template>
- <assignee-avatar-link
- v-if="hasOneUser"
- tooltip-placement="left"
- :tooltip-has-name="false"
- :user="firstUser"
- :issuable-type="issuableType"
- >
- <div class="ml-2 gl-line-height-normal">
- <user-name-with-status :name="firstUser.name" :availability="userAvailability(firstUser)" />
- <div>{{ username }}</div>
- </div>
- </assignee-avatar-link>
- <div v-else>
+ <div>
<div class="gl-display-flex gl-flex-wrap">
<div
v-for="(user, index) in uncollapsedUsers"
:key="user.id"
:class="{
- 'user-item': !showVerticalList,
- 'gl-display-inline-block': !showVerticalList,
- 'gl-display-grid gl-align-items-center': showVerticalList,
- 'gl-mb-3': index !== users.length - 1 && showVerticalList,
+ 'gl-mb-3': index !== users.length - 1,
}"
- class="assignee-grid"
+ class="assignee-grid gl-display-grid gl-align-items-center gl-w-full"
>
<assignee-avatar-link
:user="user"
@@ -123,12 +101,10 @@ export default {
data-css-area="user"
>
<div
- v-if="showVerticalList"
- class="gl-ml-3 gl-line-height-normal gl-display-grid"
+ class="gl-ml-3 gl-line-height-normal gl-display-grid gl-align-items-center"
data-testid="username"
>
<user-name-with-status :name="user.name" :availability="userAvailability(user)" />
- <span>@{{ user.username }}</span>
</div>
</assignee-avatar-link>
<attention-requested-toggle
diff --git a/app/assets/javascripts/sidebar/components/attention_requested_toggle.vue b/app/assets/javascripts/sidebar/components/attention_requested_toggle.vue
index cdc1c65a516..031de669489 100644
--- a/app/assets/javascripts/sidebar/components/attention_requested_toggle.vue
+++ b/app/assets/javascripts/sidebar/components/attention_requested_toggle.vue
@@ -5,9 +5,8 @@ import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
export default {
i18n: {
- attentionRequestedReviewer: __('Request attention to review'),
- attentionRequestedAssignee: __('Request attention'),
- removeAttentionRequested: __('Remove attention request'),
+ addAttentionRequest: __('Add attention request'),
+ removeAttentionRequest: __('Remove attention request'),
attentionRequestedNoPermission: __('Attention requested'),
noAttentionRequestedNoPermission: __('No attention request'),
},
@@ -36,20 +35,35 @@ export default {
tooltipTitle() {
if (this.user.attention_requested) {
if (this.user.can_update_merge_request) {
- return this.$options.i18n.removeAttentionRequested;
+ return this.$options.i18n.removeAttentionRequest;
}
return this.$options.i18n.attentionRequestedNoPermission;
}
if (this.user.can_update_merge_request) {
- return this.type === 'reviewer'
- ? this.$options.i18n.attentionRequestedReviewer
- : this.$options.i18n.attentionRequestedAssignee;
+ return this.$options.i18n.addAttentionRequest;
}
return this.$options.i18n.noAttentionRequestedNoPermission;
},
+ request() {
+ const state = {
+ variant: 'default',
+ icon: 'attention',
+ direction: 'add',
+ };
+
+ if (this.user.attention_requested) {
+ Object.assign(state, {
+ variant: 'warning',
+ icon: 'attention-solid',
+ direction: 'remove',
+ });
+ }
+
+ return state;
+ },
},
methods: {
toggleAttentionRequired() {
@@ -60,6 +74,7 @@ export default {
this.$emit('toggle-attention-requested', {
user: this.user,
callback: this.toggleAttentionRequiredComplete,
+ direction: this.request.direction,
});
},
toggleAttentionRequiredComplete() {
@@ -77,8 +92,8 @@ export default {
>
<gl-button
:loading="loading"
- :variant="user.attention_requested ? 'warning' : 'default'"
- :icon="user.attention_requested ? 'attention-solid' : 'attention'"
+ :variant="request.variant"
+ :icon="request.icon"
:aria-label="tooltipTitle"
:class="{ 'gl-pointer-events-none': !user.can_update_merge_request }"
size="small"
diff --git a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_content.vue b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_content.vue
index 37a44eb8f01..6afaee91d7a 100644
--- a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_content.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_content.vue
@@ -1,10 +1,13 @@
<script>
-import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
-import { __, sprintf } from '~/locale';
+import { GlIcon, GlAlert, GlTooltipDirective } from '@gitlab/ui';
+import { __ } from '~/locale';
+import { IssuableType, WorkspaceType } from '~/issues/constants';
+import { confidentialityInfoText } from '~/vue_shared/constants';
export default {
components: {
GlIcon,
+ GlAlert,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -20,12 +23,11 @@ export default {
},
},
computed: {
- confidentialText() {
- return this.confidential
- ? sprintf(__('This %{issuableType} is confidential'), {
- issuableType: this.issuableType,
- })
- : __('Not confidential');
+ confidentialBodyText() {
+ return confidentialityInfoText(
+ this.issuableType === IssuableType.Epic ? WorkspaceType.group : WorkspaceType.project,
+ this.issuableType,
+ );
},
confidentialIcon() {
return this.confidential ? 'eye-slash' : 'eye';
@@ -59,6 +61,17 @@ export default {
class="sidebar-item-icon inline hide-collapsed"
:class="{ 'is-active': confidential }"
/>
- <span class="hide-collapsed" data-testid="confidential-text">{{ confidentialText }}</span>
+ <span class="hide-collapsed" data-testid="confidential-text">
+ {{ tooltipLabel }}
+ <gl-alert
+ v-if="confidential"
+ :show-icon="false"
+ :dismissible="false"
+ variant="warning"
+ class="gl-mt-3"
+ >
+ {{ confidentialBodyText }}
+ </gl-alert>
+ </span>
</div>
</template>
diff --git a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue
index 209d1cca360..71e40fde77d 100644
--- a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue
@@ -8,7 +8,7 @@ import { confidentialityQueries } from '~/sidebar/constants';
export default {
i18n: {
confidentialityOnWarning: __(
- 'You are going to turn on confidentiality. Only team members with %{strongStart}at least Reporter access%{strongEnd} will be able to see and leave comments on the %{issuableType}.',
+ 'You are going to turn on confidentiality. Only %{context} members with %{strongStart}at least Reporter role%{strongEnd} can view or be notified about this %{issuableType}.',
),
confidentialityOffWarning: __(
'You are going to turn off the confidentiality. This means %{strongStart}everyone%{strongEnd} will be able to see and leave a comment on this %{issuableType}.',
@@ -53,6 +53,9 @@ export default {
? this.$options.i18n.confidentialityOffWarning
: this.$options.i18n.confidentialityOnWarning;
},
+ context() {
+ return this.issuableType === IssuableType.Issue ? __('project') : __('group');
+ },
workspacePath() {
return this.issuableType === IssuableType.Issue
? {
@@ -119,6 +122,7 @@ export default {
<template #strong="{ content }">
<strong>{{ content }}</strong>
</template>
+ <template #context>{{ context }}</template>
<template #issuableType>{{ issuableType }}</template>
</gl-sprintf>
</p>
diff --git a/app/assets/javascripts/sidebar/components/crm_contacts/crm_contacts.vue b/app/assets/javascripts/sidebar/components/crm_contacts/crm_contacts.vue
index 950647f1cb2..67f36f65b5d 100644
--- a/app/assets/javascripts/sidebar/components/crm_contacts/crm_contacts.vue
+++ b/app/assets/javascripts/sidebar/components/crm_contacts/crm_contacts.vue
@@ -2,7 +2,7 @@
import { GlIcon, GlLink, GlPopover, GlTooltipDirective } from '@gitlab/ui';
import { __, n__, sprintf } from '~/locale';
import createFlash from '~/flash';
-import { convertToGraphQLId } from '~/graphql_shared/utils';
+import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
import { TYPE_ISSUE } from '~/graphql_shared/constants';
import getIssueCrmContactsQuery from './queries/get_issue_crm_contacts.query.graphql';
import issueCrmContactsSubscription from './queries/issue_crm_contacts.subscription.graphql';
@@ -21,6 +21,10 @@ export default {
type: String,
required: true,
},
+ groupIssuesPath: {
+ type: String,
+ required: true,
+ },
},
data() {
return {
@@ -85,6 +89,10 @@ export default {
Boolean,
);
},
+ getIssuesPath(contactId) {
+ const id = getIdFromGraphQLId(contactId);
+ return `${this.groupIssuesPath}?crm_contact_id=${id}`;
+ },
},
};
</script>
@@ -100,7 +108,7 @@ export default {
><gl-icon name="question-o"
/></gl-link>
</div>
- <div class="title hide-collapsed gl-mb-2 gl-line-height-20">
+ <div class="title hide-collapsed gl-mb-2 gl-line-height-20 gl-font-weight-bold">
{{ contactsLabel }}
</div>
<div class="hide-collapsed gl-display-flex gl-flex-wrap">
@@ -110,8 +118,8 @@ export default {
:key="index"
class="gl-pr-2"
>
- <span :id="`contact_${index}`" class="gl-font-weight-bold"
- >{{ contact.firstName }} {{ contact.lastName }}{{ divider(index) }}</span
+ <gl-link :id="`contact_${index}`" :href="getIssuesPath(contact.id)"
+ >{{ contact.firstName }} {{ contact.lastName }}{{ divider(index) }}</gl-link
>
<gl-popover
v-if="shouldShowPopover(contact)"
diff --git a/app/assets/javascripts/sidebar/components/date/sidebar_formatted_date.vue b/app/assets/javascripts/sidebar/components/date/sidebar_formatted_date.vue
index 87cf1c29fb0..627b8452508 100644
--- a/app/assets/javascripts/sidebar/components/date/sidebar_formatted_date.vue
+++ b/app/assets/javascripts/sidebar/components/date/sidebar_formatted_date.vue
@@ -35,7 +35,7 @@ export default {
<template>
<div class="gl-display-flex gl-align-items-center hide-collapsed">
<span
- :class="hasDate ? 'gl-text-gray-900 gl-font-weight-bold' : 'gl-text-gray-500'"
+ :class="hasDate ? 'gl-text-gray-900' : 'gl-text-gray-500'"
data-testid="sidebar-date-value"
>
{{ formattedDate }}
diff --git a/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue b/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
index cb49f329f7e..699d1bebea1 100644
--- a/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
+++ b/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
@@ -1,7 +1,9 @@
<script>
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
-import { mapGetters } from 'vuex';
-import { __ } from '~/locale';
+import { mapGetters, mapActions } from 'vuex';
+import { __, sprintf } from '~/locale';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import createFlash from '~/flash';
import eventHub from '~/sidebar/event_hub';
import editForm from './edit_form.vue';
@@ -23,11 +25,11 @@ export default {
editForm,
GlIcon,
},
-
directives: {
GlTooltip: GlTooltipDirective,
},
-
+ mixins: [glFeatureFlagMixin()],
+ inject: ['fullPath'],
props: {
isEditable: {
required: true,
@@ -41,6 +43,9 @@ export default {
},
computed: {
...mapGetters(['getNoteableData']),
+ isMergeRequest() {
+ return this.getNoteableData.targetType === 'merge_request' && this.glFeatures.movedMrSidebar;
+ },
issuableDisplayName() {
const isInIssuePage = this.getNoteableData.targetType === this.$options.issue;
return isInIssuePage ? __('issue') : __('merge request');
@@ -66,17 +71,49 @@ export default {
},
methods: {
+ ...mapActions(['updateLockedAttribute']),
toggleForm() {
if (this.isEditable) {
this.isLockDialogOpen = !this.isLockDialogOpen;
}
},
+ toggleLocked() {
+ this.isLoading = true;
+
+ this.updateLockedAttribute({
+ locked: !this.isLocked,
+ fullPath: this.fullPath,
+ })
+ .catch(() => {
+ const flashMessage = __(
+ 'Something went wrong trying to change the locked state of this %{issuableDisplayName}',
+ );
+ createFlash({
+ message: sprintf(flashMessage, { issuableDisplayName: this.issuableDisplayName }),
+ });
+ })
+ .finally(() => {
+ this.isLoading = false;
+ });
+ },
},
};
</script>
<template>
- <div class="block issuable-sidebar-item lock">
+ <li v-if="isMergeRequest" class="gl-new-dropdown-item">
+ <button type="button" class="dropdown-item" @click="toggleLocked">
+ <span class="gl-new-dropdown-item-text-wrapper">
+ <template v-if="isLocked">
+ {{ __('Unlock merge request') }}
+ </template>
+ <template v-else>
+ {{ __('Lock merge request') }}
+ </template>
+ </span>
+ </button>
+ </li>
+ <div v-else class="block issuable-sidebar-item lock">
<div
v-gl-tooltip.left.viewport="{ title: tooltipLabel }"
class="sidebar-collapsed-icon"
@@ -86,7 +123,7 @@ export default {
<gl-icon :name="lockStatus.icon" class="sidebar-item-icon is-active" />
</div>
- <div class="hide-collapsed gl-line-height-20 gl-mb-2 gl-text-gray-900">
+ <div class="hide-collapsed gl-line-height-20 gl-mb-2 gl-text-gray-900 gl-font-weight-bold">
{{ sprintf(__('Lock %{issuableDisplayName}'), { issuableDisplayName: issuableDisplayName }) }}
<a
v-if="isEditable"
@@ -111,12 +148,6 @@ export default {
/>
<div data-testid="lock-status" class="sidebar-item-value" :class="lockStatus.class">
- <gl-icon
- :size="16"
- :name="lockStatus.icon"
- class="sidebar-item-icon"
- :class="lockStatus.iconClass"
- />
{{ lockStatus.displayText }}
</div>
</div>
diff --git a/app/assets/javascripts/sidebar/components/participants/participants.vue b/app/assets/javascripts/sidebar/components/participants/participants.vue
index 3fd35de2132..77e41648e9b 100644
--- a/app/assets/javascripts/sidebar/components/participants/participants.vue
+++ b/app/assets/javascripts/sidebar/components/participants/participants.vue
@@ -101,7 +101,10 @@ export default {
<gl-loading-icon v-if="loading" size="sm" />
<span v-else data-testid="collapsed-count"> {{ participantCount }} </span>
</div>
- <div v-if="showParticipantLabel" class="title hide-collapsed gl-mb-2 gl-line-height-20">
+ <div
+ v-if="showParticipantLabel"
+ class="title hide-collapsed gl-mb-2! gl-line-height-20 gl-font-weight-bold"
+ >
<gl-loading-icon v-if="loading" size="sm" :inline="true" />
{{ participantLabel }}
</div>
diff --git a/app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer_list.vue b/app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer_list.vue
index 60d8fb4d408..e09b5d913f7 100644
--- a/app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer_list.vue
+++ b/app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer_list.vue
@@ -96,7 +96,11 @@ export default {
<gl-icon v-if="hasNoUsers" name="user" :aria-label="__('None')" />
<collapsed-reviewer v-for="user in collapsedUsers" :key="user.id" :user="user" />
<button v-if="hasMoreThanTwoReviewers" class="btn-link" type="button">
- <span class="avatar-counter sidebar-avatar-counter"> {{ sidebarAvatarCounter }} </span>
+ <span
+ class="avatar-counter sidebar-avatar-counter gl-display-flex gl-align-items-center gl-pl-3"
+ >
+ {{ sidebarAvatarCounter }}
+ </span>
<gl-icon
v-if="!allReviewersCanMerge"
name="warning-solid"
diff --git a/app/assets/javascripts/sidebar/components/reviewers/reviewer_avatar_link.vue b/app/assets/javascripts/sidebar/components/reviewers/reviewer_avatar_link.vue
index a11468c8761..36a08482e69 100644
--- a/app/assets/javascripts/sidebar/components/reviewers/reviewer_avatar_link.vue
+++ b/app/assets/javascripts/sidebar/components/reviewers/reviewer_avatar_link.vue
@@ -47,8 +47,6 @@ export default {
return sprintf(__('%{userName} (cannot merge)'), { userName: this.user.name });
} else if (this.cannotMerge) {
return __('Cannot merge');
- } else if (this.tooltipHasName) {
- return this.user.name;
}
return '';
@@ -70,14 +68,15 @@ export default {
<template>
<!-- must be `d-inline-block` or parent flex-basis causes width issues -->
<gl-link
- v-gl-tooltip="tooltipOption"
:href="reviewerUrl"
:title="tooltipTitle"
- class="gl-display-inline-block"
+ :data-user-id="user.id"
+ data-placement="left"
+ class="gl-display-inline-block js-user-link"
>
<!-- use d-flex so that slot can be appropriately styled -->
<span class="gl-display-flex">
- <reviewer-avatar :user="user" :img-size="32" :issuable-type="issuableType" />
+ <reviewer-avatar :user="user" :img-size="24" :issuable-type="issuableType" />
<slot :user="user"></slot>
</span>
</gl-link>
diff --git a/app/assets/javascripts/sidebar/components/reviewers/reviewer_title.vue b/app/assets/javascripts/sidebar/components/reviewers/reviewer_title.vue
index 367dcdb961b..933b9b11b40 100644
--- a/app/assets/javascripts/sidebar/components/reviewers/reviewer_title.vue
+++ b/app/assets/javascripts/sidebar/components/reviewers/reviewer_title.vue
@@ -33,7 +33,7 @@ export default {
};
</script>
<template>
- <div class="hide-collapsed gl-line-height-20 gl-mb-2 gl-text-gray-900">
+ <div class="hide-collapsed gl-line-height-20 gl-mb-2 gl-text-gray-900 gl-font-weight-bold">
{{ reviewerTitle }}
<gl-loading-icon v-if="loading" size="sm" inline class="align-bottom" />
<a
diff --git a/app/assets/javascripts/sidebar/components/reviewers/uncollapsed_reviewer_list.vue b/app/assets/javascripts/sidebar/components/reviewers/uncollapsed_reviewer_list.vue
index 3e6be3487b1..2f58e11c00f 100644
--- a/app/assets/javascripts/sidebar/components/reviewers/uncollapsed_reviewer_list.vue
+++ b/app/assets/javascripts/sidebar/components/reviewers/uncollapsed_reviewer_list.vue
@@ -98,7 +98,7 @@ export default {
'gl-mb-3': index !== users.length - 1,
'attention-requests': glFeatures.mrAttentionRequests,
}"
- class="gl-display-grid gl-align-items-center reviewer-grid"
+ class="gl-display-grid gl-align-items-center reviewer-grid gl-mr-2"
data-testid="reviewer"
>
<reviewer-avatar-link
@@ -108,9 +108,8 @@ export default {
class="gl-word-break-word gl-mr-2"
data-css-area="user"
>
- <div class="gl-ml-3 gl-line-height-normal gl-display-grid">
- <span>{{ user.name }}</span>
- <span>@{{ user.username }}</span>
+ <div class="gl-ml-3 gl-line-height-normal gl-display-grid gl-align-items-center">
+ {{ user.name }}
</div>
</reviewer-avatar-link>
<attention-requested-toggle
diff --git a/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue b/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
index ec23e817127..897cab45fe4 100644
--- a/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
+++ b/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
@@ -331,20 +331,19 @@ export default {
:data-testid="`select-${formatIssuableAttribute.kebab}`"
:class="isClassicSidebar ? 'hide-collapsed' : 'gl-mt-3'"
>
- <span v-if="updating" class="gl-font-weight-bold">{{ selectedTitle }}</span>
+ <span v-if="updating">{{ selectedTitle }}</span>
<span v-else-if="!currentAttribute" class="gl-text-gray-500">
{{ $options.i18n.none }}
</span>
<slot
v-else
name="value"
- :attributeTitle="attributeTitle"
- :attributeUrl="attributeUrl"
- :currentAttribute="currentAttribute"
+ :attribute-title="attributeTitle"
+ :attribute-url="attributeUrl"
+ :current-attribute="currentAttribute"
>
<gl-link
v-gl-tooltip="tooltipText"
- class="gl-text-gray-900! gl-font-weight-bold"
:href="attributeUrl"
:data-qa-selector="`${formatIssuableAttribute.snake}_link`"
>
@@ -389,9 +388,9 @@ export default {
<slot
v-else
name="list"
- :attributesList="attributesList"
- :isAttributeChecked="isAttributeChecked"
- :updateAttribute="updateAttribute"
+ :attributes-list="attributesList"
+ :is-attribute-checked="isAttributeChecked"
+ :update-attribute="updateAttribute"
>
<gl-dropdown-item
v-for="attrItem in attributesList"
diff --git a/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue b/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue
index 056b3e98a1c..7551b181a58 100644
--- a/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue
+++ b/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue
@@ -110,7 +110,7 @@ export default {
<template>
<div>
<div
- class="gl-display-flex gl-align-items-center gl-line-height-20 gl-mb-2 gl-text-gray-900"
+ class="gl-display-flex gl-align-items-center gl-line-height-20 gl-mb-2 gl-text-gray-900 gl-font-weight-bold"
@click.self="collapse"
>
<span class="hide-collapsed" data-testid="title" @click="collapse">
diff --git a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
index 7a10a9f3a4c..1bafa845665 100644
--- a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
+++ b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
@@ -5,6 +5,7 @@ import { IssuableType } from '~/issues/constants';
import { isLoggedIn } from '~/lib/utils/common_utils';
import { __, sprintf } from '~/locale';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { subscribedQueries, Tracking } from '~/sidebar/constants';
const ICON_ON = 'notifications';
@@ -25,6 +26,7 @@ export default {
GlToggle,
SidebarEditableItem,
},
+ mixins: [glFeatureFlagMixin()],
props: {
iid: {
type: String,
@@ -82,6 +84,9 @@ export default {
},
},
computed: {
+ isMergeRequest() {
+ return this.issuableType === IssuableType.MergeRequest && this.glFeatures.movedMrSidebar;
+ },
isLoading() {
return this.$apollo.queries?.subscribed?.loading || this.loading;
},
@@ -171,7 +176,20 @@ export default {
</script>
<template>
+ <li v-if="isMergeRequest" class="gl-new-dropdown-item">
+ <button type="button" class="dropdown-item" @click="toggleSubscribed">
+ <span class="gl-new-dropdown-item-text-wrapper">
+ <template v-if="subscribed">
+ {{ __('Turn off notifications') }}
+ </template>
+ <template v-else>
+ {{ __('Turn on notifications') }}
+ </template>
+ </span>
+ </button>
+ </li>
<sidebar-editable-item
+ v-else
ref="editable"
:title="$options.i18n.notifications"
:tracking="$options.tracking"
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/report.vue b/app/assets/javascripts/sidebar/components/time_tracking/report.vue
index 5d4031ac68b..d9797961d40 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/report.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/report.vue
@@ -78,9 +78,9 @@ export default {
},
},
fields: [
- { key: 'spentAt', label: __('Spent At'), sortable: true },
+ { key: 'spentAt', label: __('Spent At'), sortable: true, tdClass: 'gl-w-quarter' },
{ key: 'user', label: __('User'), sortable: true },
- { key: 'timeSpent', label: __('Time Spent'), sortable: true },
+ { key: 'timeSpent', label: __('Time Spent'), sortable: true, tdClass: 'gl-w-15' },
{ key: 'summary', label: __('Summary / Note'), sortable: true },
],
};
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
index fdbcef22bba..057bb9f0100 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
@@ -204,7 +204,7 @@ export default {
:time-estimate-human-readable="humanTimeEstimate"
/>
<div
- class="hide-collapsed gl-line-height-20 gl-text-gray-900 gl-display-flex gl-align-items-center"
+ class="hide-collapsed gl-line-height-20 gl-text-gray-900 gl-display-flex gl-align-items-center gl-font-weight-bold gl-mr-3"
>
{{ __('Time tracking') }}
<gl-loading-icon v-if="isTimeTrackingInfoLoading" size="sm" class="gl-ml-2" inline />
@@ -221,8 +221,7 @@ export default {
</div>
<div v-if="!isTimeTrackingInfoLoading" class="hide-collapsed">
<div v-if="showEstimateOnlyState" data-testid="estimateOnlyPane">
- <span class="gl-font-weight-bold">{{ $options.i18n.estimatedOnlyText }} </span
- >{{ humanTimeEstimate }}
+ <span>{{ $options.i18n.estimatedOnlyText }} </span>{{ humanTimeEstimate }}
</div>
<time-tracking-spent-only-pane
v-if="showSpentOnlyState"
@@ -250,6 +249,7 @@ export default {
</gl-link>
<gl-modal
modal-id="time-tracking-report"
+ size="lg"
:title="__('Time tracking report')"
:hide-footer="true"
>
diff --git a/app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue b/app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue
index eabba619af5..482b9343e70 100644
--- a/app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue
+++ b/app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue
@@ -6,6 +6,9 @@ import { __, sprintf } from '~/locale';
import { todoQueries, TodoMutationTypes, todoMutations } from '~/sidebar/constants';
import { todoLabel } from '~/vue_shared/components/sidebar/todo_toggle//utils';
import TodoButton from '~/vue_shared/components/sidebar/todo_toggle/todo_button.vue';
+import Tracking from '~/tracking';
+
+const trackingMixin = Tracking.mixin();
export default {
components: {
@@ -16,6 +19,7 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
+ mixins: [trackingMixin],
inject: {
isClassicSidebar: {
default: false,
@@ -151,6 +155,10 @@ export default {
message: errors[0],
});
}
+ this.track('click_todo', {
+ label: 'right_sidebar',
+ property: this.hasTodo,
+ });
},
)
.catch(() => {
diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js
index 2a7d967cb61..351bb50d941 100644
--- a/app/assets/javascripts/sidebar/mount_sidebar.js
+++ b/app/assets/javascripts/sidebar/mount_sidebar.js
@@ -218,7 +218,7 @@ function mountCrmContactsComponent() {
if (!el) return;
- const { issueId } = el.dataset;
+ const { issueId, groupIssuesPath } = el.dataset;
// eslint-disable-next-line no-new
new Vue({
el,
@@ -231,6 +231,7 @@ function mountCrmContactsComponent() {
createElement('crm-contacts', {
props: {
issueId,
+ groupIssuesPath,
},
}),
});
@@ -430,10 +431,7 @@ function mountLockComponent(store) {
return;
}
- const { fullPath } = getSidebarOptions();
-
- const dataNode = document.getElementById('js-lock-issue-data');
- const initialData = JSON.parse(dataNode.innerHTML);
+ const { fullPath, editable } = getSidebarOptions();
// eslint-disable-next-line no-new
new Vue({
@@ -446,7 +444,7 @@ function mountLockComponent(store) {
render: (createElement) =>
createElement(IssuableLockForm, {
props: {
- isEditable: initialData.is_editable,
+ isEditable: editable,
},
}),
});
diff --git a/app/assets/javascripts/sidebar/queries/remove_attention_request.mutation.graphql b/app/assets/javascripts/sidebar/queries/remove_attention_request.mutation.graphql
new file mode 100644
index 00000000000..d9b9c04fd63
--- /dev/null
+++ b/app/assets/javascripts/sidebar/queries/remove_attention_request.mutation.graphql
@@ -0,0 +1,7 @@
+mutation mergeRequestRemoveAttentionRequest($projectPath: ID!, $iid: String!, $userId: UserID!) {
+ mergeRequestRemoveAttentionRequest(
+ input: { projectPath: $projectPath, iid: $iid, userId: $userId }
+ ) {
+ errors
+ }
+}
diff --git a/app/assets/javascripts/sidebar/queries/request_attention.mutation.graphql b/app/assets/javascripts/sidebar/queries/request_attention.mutation.graphql
new file mode 100644
index 00000000000..99a86e4fe5c
--- /dev/null
+++ b/app/assets/javascripts/sidebar/queries/request_attention.mutation.graphql
@@ -0,0 +1,5 @@
+mutation mergeRequestRequestAttention($projectPath: ID!, $iid: String!, $userId: UserID!) {
+ mergeRequestRequestAttention(input: { projectPath: $projectPath, iid: $iid, userId: $userId }) {
+ errors
+ }
+}
diff --git a/app/assets/javascripts/sidebar/queries/reviewer_rereview.mutation.graphql b/app/assets/javascripts/sidebar/queries/reviewer_rereview.mutation.graphql
index 73765e7d77b..0d66ee0d6e5 100644
--- a/app/assets/javascripts/sidebar/queries/reviewer_rereview.mutation.graphql
+++ b/app/assets/javascripts/sidebar/queries/reviewer_rereview.mutation.graphql
@@ -1,4 +1,4 @@
-mutation mergeRequestRequestRereview($projectPath: ID!, $iid: String!, $userId: ID!) {
+mutation mergeRequestRequestRereview($projectPath: ID!, $iid: String!, $userId: UserID!) {
mergeRequestReviewerRereview(input: { projectPath: $projectPath, iid: $iid, userId: $userId }) {
errors
}
diff --git a/app/assets/javascripts/sidebar/queries/toggle_attention_requested.mutation.graphql b/app/assets/javascripts/sidebar/queries/toggle_attention_requested.mutation.graphql
deleted file mode 100644
index a9f4af6e1b9..00000000000
--- a/app/assets/javascripts/sidebar/queries/toggle_attention_requested.mutation.graphql
+++ /dev/null
@@ -1,7 +0,0 @@
-mutation mergeRequestToggleAttentionRequested($projectPath: ID!, $iid: String!, $userId: ID!) {
- mergeRequestToggleAttentionRequested(
- input: { projectPath: $projectPath, iid: $iid, userId: $userId }
- ) {
- errors
- }
-}
diff --git a/app/assets/javascripts/sidebar/queries/update_merge_request_milestone.mutation.graphql b/app/assets/javascripts/sidebar/queries/update_merge_request_milestone.mutation.graphql
index 368f06fac7f..938953ccfb2 100644
--- a/app/assets/javascripts/sidebar/queries/update_merge_request_milestone.mutation.graphql
+++ b/app/assets/javascripts/sidebar/queries/update_merge_request_milestone.mutation.graphql
@@ -1,4 +1,4 @@
-mutation mergeRequestSetMilestone($fullPath: ID!, $iid: String!, $attributeId: ID) {
+mutation mergeRequestSetMilestone($fullPath: ID!, $iid: String!, $attributeId: MilestoneID) {
issuableSetAttribute: mergeRequestSetMilestone(
input: { projectPath: $fullPath, iid: $iid, milestoneId: $attributeId }
) {
diff --git a/app/assets/javascripts/sidebar/services/sidebar_service.js b/app/assets/javascripts/sidebar/services/sidebar_service.js
index 90d8f2098bb..ea170203576 100644
--- a/app/assets/javascripts/sidebar/services/sidebar_service.js
+++ b/app/assets/javascripts/sidebar/services/sidebar_service.js
@@ -5,7 +5,8 @@ import createGqClient, { fetchPolicies } from '~/lib/graphql';
import axios from '~/lib/utils/axios_utils';
import reviewerRereviewMutation from '../queries/reviewer_rereview.mutation.graphql';
import sidebarDetailsMRQuery from '../queries/sidebar_details_mr.query.graphql';
-import toggleAttentionRequestedMutation from '../queries/toggle_attention_requested.mutation.graphql';
+import requestAttentionMutation from '../queries/request_attention.mutation.graphql';
+import removeAttentionRequestMutation from '../queries/remove_attention_request.mutation.graphql';
const queries = {
merge_request: sidebarDetailsMRQuery,
@@ -92,9 +93,19 @@ export default class SidebarService {
});
}
- toggleAttentionRequested(userId) {
+ requestAttention(userId) {
return gqClient.mutate({
- mutation: toggleAttentionRequestedMutation,
+ mutation: requestAttentionMutation,
+ variables: {
+ userId: convertToGraphQLId(TYPE_USER, `${userId}`),
+ projectPath: this.fullPath,
+ iid: this.iid.toString(),
+ },
+ });
+ }
+ removeAttentionRequest(userId) {
+ return gqClient.mutate({
+ mutation: removeAttentionRequestMutation,
variables: {
userId: convertToGraphQLId(TYPE_USER, `${userId}`),
projectPath: this.fullPath,
diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js
index 83fb8f31dfb..7df901577b8 100644
--- a/app/assets/javascripts/sidebar/sidebar_mediator.js
+++ b/app/assets/javascripts/sidebar/sidebar_mediator.js
@@ -40,6 +40,7 @@ export default class SidebarMediator {
const data = { assignee_ids: assignees };
try {
+ const { currentUserHasAttention } = this.store;
const res = await this.service.update(field, data);
this.store.overwrite('assignees', res.data.assignees);
@@ -48,6 +49,10 @@ export default class SidebarMediator {
this.store.overwrite('reviewers', res.data.reviewers);
}
+ if (currentUserHasAttention && this.store.isAddingAssignee) {
+ toast(__('Assigned user(s). Your attention request was removed.'));
+ }
+
return Promise.resolve(res);
} catch (e) {
return Promise.reject(e);
@@ -63,11 +68,16 @@ export default class SidebarMediator {
const data = { reviewer_ids: reviewers };
try {
+ const { currentUserHasAttention } = this.store;
const res = await this.service.update(field, data);
this.store.overwrite('reviewers', res.data.reviewers);
this.store.overwrite('assignees', res.data.assignees);
+ if (currentUserHasAttention && this.store.isAddingAssignee) {
+ toast(__('Requested review. Your attention request was removed.'));
+ }
+
return Promise.resolve(res);
} catch (e) {
return Promise.reject();
@@ -98,14 +108,19 @@ export default class SidebarMediator {
}
}
- async toggleAttentionRequested(type, { user, callback }) {
+ async toggleAttentionRequested(type, { user, callback, direction }) {
+ const mutations = {
+ add: (id) => this.service.requestAttention(id),
+ remove: (id) => this.service.removeAttentionRequest(id),
+ };
+
try {
const isReviewer = type === 'reviewer';
const reviewerOrAssignee = isReviewer
? this.store.findReviewer(user)
: this.store.findAssignee(user);
- await this.service.toggleAttentionRequested(user.id);
+ await mutations[direction]?.(user.id);
if (reviewerOrAssignee.attention_requested) {
toast(
@@ -115,12 +130,22 @@ export default class SidebarMediator {
);
} else {
const currentUserId = gon.current_user_id;
+ const { currentUserHasAttention } = this.store;
if (currentUserId !== user.id) {
this.removeCurrentUserAttentionRequested();
}
- toast(sprintf(__('Requested attention from @%{username}'), { username: user.username }));
+ toast(
+ currentUserHasAttention && currentUserId !== user.id
+ ? sprintf(
+ __(
+ 'Requested attention from @%{username}. Your own attention request was removed.',
+ ),
+ { username: user.username },
+ )
+ : sprintf(__('Requested attention from @%{username}'), { username: user.username }),
+ );
}
this.store.updateReviewer(user.id, 'attention_requested');
@@ -138,7 +163,7 @@ export default class SidebarMediator {
captureError: true,
actionConfig: {
title: __('Try again'),
- clickHandler: () => this.toggleAttentionRequired(type, { user, callback }),
+ clickHandler: () => this.toggleAttentionRequired(type, { user, callback, direction }),
},
});
}
diff --git a/app/assets/javascripts/sidebar/stores/sidebar_store.js b/app/assets/javascripts/sidebar/stores/sidebar_store.js
index 2caa6f4f0a0..ca85ee7fd94 100644
--- a/app/assets/javascripts/sidebar/stores/sidebar_store.js
+++ b/app/assets/javascripts/sidebar/stores/sidebar_store.js
@@ -18,7 +18,9 @@ export default class SidebarStore {
this.humanTimeSpent = '';
this.timeTrackingLimitToHours = timeTrackingLimitToHours;
this.assignees = [];
+ this.addingAssignees = [];
this.reviewers = [];
+ this.addingReviewers = [];
this.isFetching = {
assignees: true,
reviewers: true,
@@ -32,6 +34,7 @@ export default class SidebarStore {
this.subscribeDisabledDescription = '';
this.subscribed = null;
this.changing = false;
+ this.issuableType = options.issuableType;
SidebarStore.singleton = this;
}
@@ -73,12 +76,20 @@ export default class SidebarStore {
if (!this.findAssignee(assignee)) {
this.changing = true;
this.assignees.push(assignee);
+
+ if (assignee.id !== this.currentUser.id) {
+ this.addingAssignees.push(assignee.id);
+ }
}
}
addReviewer(reviewer) {
if (!this.findReviewer(reviewer)) {
this.reviewers.push(reviewer);
+
+ if (reviewer.id !== this.currentUser.id) {
+ this.addingReviewers.push(reviewer.id);
+ }
}
}
@@ -114,12 +125,14 @@ export default class SidebarStore {
if (assignee) {
this.changing = true;
this.assignees = this.assignees.filter(({ id }) => id !== assignee.id);
+ this.addingAssignees = this.addingAssignees.filter(({ id }) => id !== assignee.id);
}
}
removeReviewer(reviewer) {
if (reviewer) {
this.reviewers = this.reviewers.filter(({ id }) => id !== reviewer.id);
+ this.addingReviewers = this.addingReviewers.filter(({ id }) => id !== reviewer.id);
}
}
@@ -147,4 +160,26 @@ export default class SidebarStore {
setMoveToProjectId(moveToProjectId) {
this.moveToProjectId = moveToProjectId;
}
+
+ get currentUserHasAttention() {
+ if (!window.gon?.features?.mrAttentionRequests || !this.isMergeRequest) return false;
+
+ const currentUserId = this.currentUser.id;
+ const currentUserReviewer = this.findReviewer({ id: currentUserId });
+ const currentUserAssignee = this.findAssignee({ id: currentUserId });
+
+ return currentUserReviewer?.attention_requested || currentUserAssignee?.attention_requested;
+ }
+
+ get isAddingAssignee() {
+ return this.addingAssignees.length > 0;
+ }
+
+ get isAddingReviewer() {
+ return this.addingReviewers.length > 0;
+ }
+
+ get isMergeRequest() {
+ return this.issuableType === 'merge_request';
+ }
}