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>2021-12-09 12:11:03 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-12-09 12:11:03 +0300
commit3d233a67cf4ab39773e3ee10a09df9eb7c3b4167 (patch)
tree66a33a656fb9d69c5e11e56cc5c7d20f2f5d064d /app/assets/javascripts/boards
parent130e0444c6e5498ea95d38dd96bf758a724e2345 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/boards')
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue16
-rw-r--r--app/assets/javascripts/boards/components/board_list_header.vue39
-rw-r--r--app/assets/javascripts/boards/components/sidebar/board_sidebar_labels_select.vue173
-rw-r--r--app/assets/javascripts/boards/components/sidebar/board_sidebar_subscription.vue75
-rw-r--r--app/assets/javascripts/boards/graphql/board_list_shared.fragment.graphql1
-rw-r--r--app/assets/javascripts/boards/graphql/board_lists_deferred.query.graphql6
-rw-r--r--app/assets/javascripts/boards/graphql/lists_issues.query.graphql2
-rw-r--r--app/assets/javascripts/boards/stores/actions.js88
8 files changed, 136 insertions, 264 deletions
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index 47dffc985aa..91bee8f3474 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -8,6 +8,7 @@ import defaultSortableConfig from '~/sortable/sortable_config';
import Tracking from '~/tracking';
import { toggleFormEventPrefix, DraggableItemTypes } from '../constants';
import eventHub from '../eventhub';
+import listQuery from '../graphql/board_lists_deferred.query.graphql';
import BoardCard from './board_card.vue';
import BoardNewIssue from './board_new_issue.vue';
@@ -50,11 +51,22 @@ export default {
showEpicForm: false,
};
},
+ apollo: {
+ boardList: {
+ query: listQuery,
+ variables() {
+ return {
+ id: this.list.id,
+ filters: this.filterParams,
+ };
+ },
+ },
+ },
computed: {
- ...mapState(['pageInfoByListId', 'listsFlags']),
+ ...mapState(['pageInfoByListId', 'listsFlags', 'filterParams']),
...mapGetters(['isEpicBoard']),
listItemsCount() {
- return this.isEpicBoard ? this.list.epicsCount : this.list.issuesCount;
+ return this.isEpicBoard ? this.list.epicsCount : this.boardList?.issuesCount;
},
paginatedIssueText() {
return sprintf(__('Showing %{pageSize} of %{total} %{issuableType}'), {
diff --git a/app/assets/javascripts/boards/components/board_list_header.vue b/app/assets/javascripts/boards/components/board_list_header.vue
index e985a368e64..c9cf044aaa0 100644
--- a/app/assets/javascripts/boards/components/board_list_header.vue
+++ b/app/assets/javascripts/boards/components/board_list_header.vue
@@ -20,6 +20,7 @@ import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import AccessorUtilities from '../../lib/utils/accessor';
import { inactiveId, LIST, ListType, toggleFormEventPrefix } from '../constants';
import eventHub from '../eventhub';
+import listQuery from '../graphql/board_lists_deferred.query.graphql';
import ItemCount from './item_count.vue';
export default {
@@ -74,7 +75,7 @@ export default {
},
},
computed: {
- ...mapState(['activeId']),
+ ...mapState(['activeId', 'filterParams']),
...mapGetters(['isEpicBoard', 'isSwimlanesOn']),
isLoggedIn() {
return Boolean(this.currentUserId);
@@ -119,14 +120,11 @@ export default {
}
return false;
},
- itemsCount() {
- return this.list.issuesCount;
- },
countIcon() {
return 'issues';
},
itemsTooltipLabel() {
- return n__(`%d issue`, `%d issues`, this.itemsCount);
+ return n__(`%d issue`, `%d issues`, this.boardLists?.issuesCount);
},
chevronTooltip() {
return this.list.collapsed ? this.$options.i18n.expand : this.$options.i18n.collapse;
@@ -158,6 +156,23 @@ export default {
userCanDrag() {
return !this.disabled && isListDraggable(this.list);
},
+ isLoading() {
+ return this.$apollo.queries.boardList.loading;
+ },
+ },
+ apollo: {
+ boardList: {
+ query: listQuery,
+ variables() {
+ return {
+ id: this.list.id,
+ filters: this.filterParams,
+ };
+ },
+ skip() {
+ return this.isEpicBoard;
+ },
+ },
},
created() {
const localCollapsed = parseBoolean(localStorage.getItem(`${this.uniqueKey}.collapsed`));
@@ -375,10 +390,10 @@ export default {
</gl-sprintf>
</div>
<div v-else>• {{ itemsTooltipLabel }}</div>
- <div v-if="weightFeatureAvailable">
+ <div v-if="weightFeatureAvailable && !isLoading">
<gl-sprintf :message="__('%{totalWeight} total weight')">
- <template #totalWeight>{{ list.totalWeight }}</template>
+ <template #totalWeight>{{ boardList.totalWeight }}</template>
</gl-sprintf>
</div>
</gl-tooltip>
@@ -396,14 +411,18 @@ export default {
<gl-tooltip :target="() => $refs.itemCount" :title="itemsTooltipLabel" />
<span ref="itemCount" class="gl-display-inline-flex gl-align-items-center">
<gl-icon class="gl-mr-2" :name="countIcon" />
- <item-count :items-size="itemsCount" :max-issue-count="list.maxIssueCount" />
+ <item-count
+ v-if="!isLoading"
+ :items-size="isEpicBoard ? list.epicsCount : boardList.issuesCount"
+ :max-issue-count="list.maxIssueCount"
+ />
</span>
<!-- EE start -->
- <template v-if="weightFeatureAvailable && !isEpicBoard">
+ <template v-if="weightFeatureAvailable && !isEpicBoard && !isLoading">
<gl-tooltip :target="() => $refs.weightTooltip" :title="weightCountToolTip" />
<span ref="weightTooltip" class="gl-display-inline-flex gl-ml-3">
<gl-icon class="gl-mr-2" name="weight" />
- {{ list.totalWeight }}
+ {{ boardList.totalWeight }}
</span>
</template>
<!-- EE end -->
diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_labels_select.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_labels_select.vue
deleted file mode 100644
index ec53947fd5f..00000000000
--- a/app/assets/javascripts/boards/components/sidebar/board_sidebar_labels_select.vue
+++ /dev/null
@@ -1,173 +0,0 @@
-<script>
-import { GlLabel } from '@gitlab/ui';
-import { mapGetters, mapActions } from 'vuex';
-import Api from '~/api';
-import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { isScopedLabel } from '~/lib/utils/common_utils';
-import { mergeUrlParams } from '~/lib/utils/url_utility';
-import { __ } from '~/locale';
-import LabelsSelect from '~/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue';
-
-export default {
- components: {
- BoardEditableItem,
- LabelsSelect,
- GlLabel,
- },
- inject: {
- labelsFetchPath: {
- default: null,
- },
- labelsManagePath: {},
- labelsFilterBasePath: {},
- },
- data() {
- return {
- loading: false,
- oldIid: null,
- isEditing: false,
- };
- },
- computed: {
- ...mapGetters(['activeBoardItem', 'projectPathForActiveIssue']),
- selectedLabels() {
- const { labels = [] } = this.activeBoardItem;
-
- return labels.map((label) => ({
- ...label,
- id: getIdFromGraphQLId(label.id),
- }));
- },
- issueLabels() {
- const { labels = [] } = this.activeBoardItem;
-
- return labels.map((label) => ({
- ...label,
- scoped: isScopedLabel(label),
- }));
- },
- fetchPath() {
- /*
- Labels fetched in epic boards are always group-level labels
- and the correct path are passed from the backend (injected through labelsFetchPath)
-
- For issue boards, we should always include project-level labels and use a different endpoint.
- (it requires knowing the project path of a selected issue.)
-
- Note 1. that we will be using GraphQL to fetch labels when we create a labels select widget.
- And this component will be removed _wholesale_ https://gitlab.com/gitlab-org/gitlab/-/issues/300653.
-
- Note 2. Moreover, 'fetchPath' needs to be used as a key for 'labels-select' component to force updates.
- 'labels-select' has its own vuex store and initializes the passed props as states
- and these states aren't reactively bound to the passed props.
- */
-
- const projectLabelsFetchPath = mergeUrlParams(
- { include_ancestor_groups: true },
- Api.buildUrl(Api.projectLabelsPath).replace(
- ':namespace_path/:project_path',
- this.projectPathForActiveIssue,
- ),
- );
-
- return this.labelsFetchPath || projectLabelsFetchPath;
- },
- },
- watch: {
- activeBoardItem(_, oldVal) {
- if (this.isEditing) {
- this.oldIid = oldVal.iid;
- } else {
- this.oldIid = null;
- }
- },
- },
- methods: {
- ...mapActions(['setActiveBoardItemLabels', 'setError']),
- async setLabels(payload) {
- this.loading = true;
- this.$refs.sidebarItem.collapse();
-
- try {
- const addLabelIds = payload.filter((label) => label.set).map((label) => label.id);
- const removeLabelIds = payload.filter((label) => !label.set).map((label) => label.id);
-
- const input = {
- addLabelIds,
- removeLabelIds,
- projectPath: this.projectPathForActiveIssue,
- iid: this.oldIid,
- };
- await this.setActiveBoardItemLabels(input);
- this.oldIid = null;
- } catch (e) {
- this.setError({ error: e, message: __('An error occurred while updating labels.') });
- } finally {
- this.loading = false;
- }
- },
- async removeLabel(id) {
- this.loading = true;
-
- try {
- const removeLabelIds = [getIdFromGraphQLId(id)];
- const input = { removeLabelIds, projectPath: this.projectPathForActiveIssue };
- await this.setActiveBoardItemLabels(input);
- } catch (e) {
- this.setError({ error: e, message: __('An error occurred when removing the label.') });
- } finally {
- this.loading = false;
- }
- },
- },
-};
-</script>
-
-<template>
- <board-editable-item
- ref="sidebarItem"
- :title="__('Labels')"
- :loading="loading"
- data-testid="sidebar-labels"
- @open="isEditing = true"
- @close="isEditing = false"
- >
- <template #collapsed>
- <gl-label
- v-for="label in issueLabels"
- :key="label.id"
- :background-color="label.color"
- :title="label.title"
- :description="label.description"
- :scoped="label.scoped"
- :show-close-button="true"
- :disabled="loading"
- class="gl-mr-2 gl-mb-2"
- @close="removeLabel(label.id)"
- />
- </template>
- <template #default="{ edit }">
- <labels-select
- ref="labelsSelect"
- :key="fetchPath"
- :allow-label-edit="false"
- :allow-label-create="false"
- :allow-multiselect="true"
- :allow-scoped-labels="true"
- :selected-labels="selectedLabels"
- :labels-fetch-path="fetchPath"
- :labels-manage-path="labelsManagePath"
- :labels-filter-base-path="labelsFilterBasePath"
- :labels-list-title="__('Select label')"
- :dropdown-button-text="__('Choose labels')"
- :is-editing="edit"
- variant="sidebar"
- class="gl-display-block labels gl-w-full"
- @updateSelectedLabels="setLabels"
- >
- {{ __('None') }}
- </labels-select>
- </template>
- </board-editable-item>
-</template>
diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_subscription.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_subscription.vue
deleted file mode 100644
index 4f5c55d0c5d..00000000000
--- a/app/assets/javascripts/boards/components/sidebar/board_sidebar_subscription.vue
+++ /dev/null
@@ -1,75 +0,0 @@
-<script>
-import { GlToggle } from '@gitlab/ui';
-import { mapGetters, mapActions } from 'vuex';
-import { __, s__ } from '~/locale';
-
-export default {
- i18n: {
- header: {
- title: __('Notifications'),
- /* Any change to subscribeDisabledDescription
- must be reflected in app/helpers/notifications_helper.rb */
- subscribeDisabledDescription: __(
- 'Notifications have been disabled by the project or group owner',
- ),
- },
- updateSubscribedErrorMessage: s__(
- 'IssueBoards|An error occurred while setting notifications status. Please try again.',
- ),
- },
- components: {
- GlToggle,
- },
- inject: ['emailsDisabled'],
- data() {
- return {
- loading: false,
- };
- },
- computed: {
- ...mapGetters(['activeBoardItem', 'projectPathForActiveIssue', 'isEpicBoard']),
- isEmailsDisabled() {
- return this.isEpicBoard ? this.emailsDisabled : this.activeBoardItem.emailsDisabled;
- },
- notificationText() {
- return this.isEmailsDisabled
- ? this.$options.i18n.header.subscribeDisabledDescription
- : this.$options.i18n.header.title;
- },
- },
- methods: {
- ...mapActions(['setActiveItemSubscribed', 'setError']),
- async handleToggleSubscription() {
- this.loading = true;
- try {
- await this.setActiveItemSubscribed({
- subscribed: !this.activeBoardItem.subscribed,
- projectPath: this.projectPathForActiveIssue,
- });
- } catch (error) {
- this.setError({ error, message: this.$options.i18n.updateSubscribedErrorMessage });
- } finally {
- this.loading = false;
- }
- },
- },
-};
-</script>
-
-<template>
- <div
- class="gl-display-flex gl-align-items-center gl-justify-content-space-between"
- data-testid="sidebar-notifications"
- >
- <span data-testid="notification-header-text"> {{ notificationText }} </span>
- <gl-toggle
- v-if="!isEmailsDisabled"
- :value="activeBoardItem.subscribed"
- :is-loading="loading"
- :label="$options.i18n.header.title"
- label-position="hidden"
- data-testid="notification-subscribe-toggle"
- @change="handleToggleSubscription"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/boards/graphql/board_list_shared.fragment.graphql b/app/assets/javascripts/boards/graphql/board_list_shared.fragment.graphql
index d85b736720b..5b532906f6a 100644
--- a/app/assets/javascripts/boards/graphql/board_list_shared.fragment.graphql
+++ b/app/assets/javascripts/boards/graphql/board_list_shared.fragment.graphql
@@ -4,7 +4,6 @@ fragment BoardListShared on BoardList {
position
listType
collapsed
- issuesCount
label {
id
title
diff --git a/app/assets/javascripts/boards/graphql/board_lists_deferred.query.graphql b/app/assets/javascripts/boards/graphql/board_lists_deferred.query.graphql
new file mode 100644
index 00000000000..bae3220dfad
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/board_lists_deferred.query.graphql
@@ -0,0 +1,6 @@
+query BoardList($id: ID!, $filters: BoardIssueInput) {
+ boardList(id: $id, issueFilters: $filters) {
+ id
+ issuesCount
+ }
+}
diff --git a/app/assets/javascripts/boards/graphql/lists_issues.query.graphql b/app/assets/javascripts/boards/graphql/lists_issues.query.graphql
index 4e47b1123ed..105f2931caa 100644
--- a/app/assets/javascripts/boards/graphql/lists_issues.query.graphql
+++ b/app/assets/javascripts/boards/graphql/lists_issues.query.graphql
@@ -1,6 +1,6 @@
#import "ee_else_ce/boards/graphql/issue.fragment.graphql"
-query BoardListEE(
+query BoardListsEE(
$fullPath: ID!
$boardId: ID!
$id: ID
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index 901461ae603..1ebfcfc331b 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -30,6 +30,7 @@ import {
} from 'ee_else_ce/boards/boards_util';
import createBoardListMutation from 'ee_else_ce/boards/graphql/board_list_create.mutation.graphql';
import issueMoveListMutation from 'ee_else_ce/boards/graphql/issue_move_list.mutation.graphql';
+import totalCountAndWeightQuery from 'ee_else_ce/boards/graphql/board_lists_deferred.query.graphql';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { queryToObject } from '~/lib/utils/url_utility';
@@ -501,9 +502,10 @@ export default {
updateIssueOrder: async ({ commit, dispatch, state }, { moveData, mutationVariables = {} }) => {
try {
- const { itemId, fromListId, toListId, moveBeforeId, moveAfterId } = moveData;
+ const { itemId, fromListId, toListId, moveBeforeId, moveAfterId, itemNotInToList } = moveData;
const {
fullBoardId,
+ filterParams,
boardItems: {
[itemId]: { iid, referencePath },
},
@@ -522,6 +524,67 @@ export default {
// 'mutationVariables' allows EE code to pass in extra parameters.
...mutationVariables,
},
+ update(
+ cache,
+ {
+ data: {
+ issueMoveList: {
+ issue: { weight },
+ },
+ },
+ },
+ ) {
+ if (fromListId === toListId) return;
+
+ const updateFromList = () => {
+ const fromList = cache.readQuery({
+ query: totalCountAndWeightQuery,
+ variables: { id: fromListId, filters: filterParams },
+ });
+
+ const updatedFromList = {
+ boardList: {
+ __typename: 'BoardList',
+ id: fromList.boardList.id,
+ issuesCount: fromList.boardList.issuesCount - 1,
+ totalWeight: fromList.boardList.totalWeight - Number(weight),
+ },
+ };
+
+ cache.writeQuery({
+ query: totalCountAndWeightQuery,
+ variables: { id: fromListId, filters: filterParams },
+ data: updatedFromList,
+ });
+ };
+
+ const updateToList = () => {
+ if (!itemNotInToList) return;
+
+ const toList = cache.readQuery({
+ query: totalCountAndWeightQuery,
+ variables: { id: toListId, filters: filterParams },
+ });
+
+ const updatedToList = {
+ boardList: {
+ __typename: 'BoardList',
+ id: toList.boardList.id,
+ issuesCount: toList.boardList.issuesCount + 1,
+ totalWeight: toList.boardList.totalWeight + Number(weight),
+ },
+ };
+
+ cache.writeQuery({
+ query: totalCountAndWeightQuery,
+ variables: { id: toListId, filters: filterParams },
+ data: updatedToList,
+ });
+ };
+
+ updateFromList();
+ updateToList();
+ },
});
if (data?.issueMoveList?.errors.length || !data.issueMoveList) {
@@ -565,7 +628,7 @@ export default {
},
addListNewIssue: (
- { state: { boardConfig, boardType, fullPath }, dispatch, commit },
+ { state: { boardConfig, boardType, fullPath, filterParams }, dispatch, commit },
{ issueInput, list, placeholderId = `tmp-${new Date().getTime()}` },
) => {
const input = formatIssueInput(issueInput, boardConfig);
@@ -581,6 +644,27 @@ export default {
.mutate({
mutation: issueCreateMutation,
variables: { input },
+ update(cache) {
+ const fromList = cache.readQuery({
+ query: totalCountAndWeightQuery,
+ variables: { id: list.id, filters: filterParams },
+ });
+
+ const updatedList = {
+ boardList: {
+ __typename: 'BoardList',
+ id: fromList.boardList.id,
+ issuesCount: fromList.boardList.issuesCount + 1,
+ totalWeight: fromList.boardList.totalWeight,
+ },
+ };
+
+ cache.writeQuery({
+ query: totalCountAndWeightQuery,
+ variables: { id: list.id, filters: filterParams },
+ data: updatedList,
+ });
+ },
})
.then(({ data }) => {
if (data.createIssue.errors.length) {