diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-06-16 15:10:18 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-06-16 15:10:18 +0300 |
commit | 111e0ef1fa06e79adf73a34ebd14f71bfeb99bff (patch) | |
tree | f3280afa857d3357006ada7e9a4483c8c6994584 /app | |
parent | 22e9f240efba67d752e33ebdb8ba8205f187dc83 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
26 files changed, 228 insertions, 177 deletions
diff --git a/app/assets/javascripts/diffs/components/settings_dropdown.vue b/app/assets/javascripts/diffs/components/settings_dropdown.vue index 443fe147d6b..178f93b651e 100644 --- a/app/assets/javascripts/diffs/components/settings_dropdown.vue +++ b/app/assets/javascripts/diffs/components/settings_dropdown.vue @@ -36,7 +36,7 @@ export default { this.setFileByFile({ fileByFile: !this.viewDiffsFileByFile }); }, toggleWhitespace(updatedSetting) { - this.setShowWhitespace({ showWhitespace: updatedSetting, pushState: true }); + this.setShowWhitespace({ showWhitespace: updatedSetting }); }, }, }; diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js index f0e15983336..d1e02fbc598 100644 --- a/app/assets/javascripts/diffs/constants.js +++ b/app/assets/javascripts/diffs/constants.js @@ -1,7 +1,3 @@ -// The backend actually uses "hide_whitespace" while the frontend -// uses "show whitspace" so these values are opposite what you might expect -export const NO_SHOW_WHITESPACE = '1'; -export const SHOW_WHITESPACE = '0'; export const INLINE_DIFF_VIEW_TYPE = 'inline'; export const PARALLEL_DIFF_VIEW_TYPE = 'parallel'; export const MATCH_LINE_TYPE = 'match'; diff --git a/app/assets/javascripts/diffs/index.js b/app/assets/javascripts/diffs/index.js index 5a8862c2b70..0ab72749760 100644 --- a/app/assets/javascripts/diffs/index.js +++ b/app/assets/javascripts/diffs/index.js @@ -98,10 +98,23 @@ export default function initDiffsApp(store) { this.setRenderTreeList(renderTreeList); - // Set whitespace default as per user preferences unless cookie is already set - if (!Cookies.get(DIFF_WHITESPACE_COOKIE_NAME)) { - const hideWhitespace = this.showWhitespaceDefault ? '0' : '1'; - this.setShowWhitespace({ showWhitespace: hideWhitespace !== '1' }); + // NOTE: A "true" or "checked" value for `showWhitespace` is '0' not '1'. + // Check for cookie and save that setting for future use. + // Then delete the cookie as we are phasing it out and using the database as SSOT. + // NOTE: This can/should be removed later + if (Cookies.get(DIFF_WHITESPACE_COOKIE_NAME)) { + const hideWhitespace = Cookies.get(DIFF_WHITESPACE_COOKIE_NAME); + this.setShowWhitespace({ + url: this.endpointUpdateUser, + showWhitespace: hideWhitespace !== '1', + }); + Cookies.remove(DIFF_WHITESPACE_COOKIE_NAME); + } else { + // This is only to set the the user preference in Vuex for use later + this.setShowWhitespace({ + showWhitespace: this.showWhitespaceDefault, + updateDatabase: false, + }); } }, methods: { diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js index bb13ad5f426..2e94f147086 100644 --- a/app/assets/javascripts/diffs/store/actions.js +++ b/app/assets/javascripts/diffs/store/actions.js @@ -26,9 +26,6 @@ import { START_RENDERING_INDEX, INLINE_DIFF_LINES_KEY, DIFFS_PER_PAGE, - DIFF_WHITESPACE_COOKIE_NAME, - SHOW_WHITESPACE, - NO_SHOW_WHITESPACE, DIFF_FILE_MANUAL_COLLAPSE, DIFF_FILE_AUTOMATIC_COLLAPSE, EVT_PERF_MARK_FILE_TREE_START, @@ -569,16 +566,15 @@ export const setRenderTreeList = ({ commit }, renderTreeList) => { } }; -export const setShowWhitespace = ({ commit }, { showWhitespace, pushState = false }) => { - commit(types.SET_SHOW_WHITESPACE, showWhitespace); - const w = showWhitespace ? SHOW_WHITESPACE : NO_SHOW_WHITESPACE; - - Cookies.set(DIFF_WHITESPACE_COOKIE_NAME, w); - - if (pushState) { - historyPushState(mergeUrlParams({ w }, window.location.href)); +export const setShowWhitespace = async ( + { state, commit }, + { url, showWhitespace, updateDatabase = true }, +) => { + if (updateDatabase) { + await axios.put(url || state.endpointUpdateUser, { show_whitespace_in_diffs: showWhitespace }); } + commit(types.SET_SHOW_WHITESPACE, showWhitespace); notesEventHub.$emit('refetchDiffData'); if (window.gon?.features?.diffSettingsUsageData) { diff --git a/app/assets/javascripts/diffs/store/modules/diff_state.js b/app/assets/javascripts/diffs/store/modules/diff_state.js index 1674d3d3b5a..348dd452698 100644 --- a/app/assets/javascripts/diffs/store/modules/diff_state.js +++ b/app/assets/javascripts/diffs/store/modules/diff_state.js @@ -1,20 +1,13 @@ import Cookies from 'js-cookie'; import { getParameterValues } from '~/lib/utils/url_utility'; -import { - INLINE_DIFF_VIEW_TYPE, - DIFF_VIEW_COOKIE_NAME, - DIFF_WHITESPACE_COOKIE_NAME, -} from '../../constants'; +import { INLINE_DIFF_VIEW_TYPE, DIFF_VIEW_COOKIE_NAME } from '../../constants'; import { fileByFile } from '../../utils/preferences'; -import { getDefaultWhitespace } from '../utils'; const getViewTypeFromQueryString = () => getParameterValues('view')[0]; const viewTypeFromCookie = Cookies.get(DIFF_VIEW_COOKIE_NAME); const defaultViewType = INLINE_DIFF_VIEW_TYPE; -const whiteSpaceFromQueryString = getParameterValues('w')[0]; -const whiteSpaceFromCookie = Cookies.get(DIFF_WHITESPACE_COOKIE_NAME); export default () => ({ isLoading: true, @@ -42,7 +35,7 @@ export default () => ({ commentForms: [], highlightedRow: null, renderTreeList: true, - showWhitespace: getDefaultWhitespace(whiteSpaceFromQueryString, whiteSpaceFromCookie), + showWhitespace: true, viewDiffsFileByFile: fileByFile(), fileFinderVisible: false, dismissEndpoint: '', diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js index 5eda509163e..75d2cf43b94 100644 --- a/app/assets/javascripts/diffs/store/utils.js +++ b/app/assets/javascripts/diffs/store/utils.js @@ -11,8 +11,6 @@ import { MATCH_LINE_TYPE, LINES_TO_BE_RENDERED_DIRECTLY, INLINE_DIFF_LINES_KEY, - SHOW_WHITESPACE, - NO_SHOW_WHITESPACE, CONFLICT_OUR, CONFLICT_THEIR, CONFLICT_MARKER, @@ -559,10 +557,3 @@ export const allDiscussionWrappersExpanded = (diff) => { return discussionsExpanded; }; - -export const getDefaultWhitespace = (queryString, cookie) => { - // Querystring should override stored cookie value - if (queryString) return queryString === SHOW_WHITESPACE; - if (cookie === NO_SHOW_WHITESPACE) return false; - return true; -}; diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/author_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/author_token.vue index 2e7b3e149b2..3b261f5ac25 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/author_token.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/author_token.vue @@ -71,9 +71,9 @@ export default { <template> <base-token - :token-config="config" - :token-value="value" - :token-active="active" + :config="config" + :value="value" + :active="active" :tokens-list-loading="loading" :token-values="authors" :fn-active-token-value="getActiveAuthor" @@ -81,6 +81,7 @@ export default { :preloaded-token-values="preloadedAuthors" :recent-token-values-storage-key="config.recentTokenValuesStorageKey" @fetch-token-values="fetchAuthorBySearchTerm" + v-on="$listeners" > <template #view="{ viewTokenProps: { inputValue, activeTokenValue } }"> <gl-avatar diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue index fb6b9e4bc0d..3dbac0d146c 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue @@ -19,29 +19,34 @@ export default { GlLoadingIcon, }, props: { - tokenConfig: { + config: { type: Object, required: true, }, - tokenValue: { + value: { type: Object, required: true, }, - tokenActive: { + active: { type: Boolean, required: true, }, tokensListLoading: { type: Boolean, - required: true, + required: false, + default: false, }, tokenValues: { type: Array, - required: true, + required: false, + default: () => [], }, fnActiveTokenValue: { type: Function, - required: true, + required: false, + default: (tokenValues, currentTokenValue) => { + return tokenValues.find(({ value }) => value === currentTokenValue); + }, }, defaultTokenValues: { type: Array, @@ -90,9 +95,9 @@ export default { }, currentTokenValue() { if (this.fnCurrentTokenValue) { - return this.fnCurrentTokenValue(this.tokenValue.data); + return this.fnCurrentTokenValue(this.value.data); } - return this.tokenValue.data.toLowerCase(); + return this.value.data.toLowerCase(); }, activeTokenValue() { return this.fnActiveTokenValue(this.tokenValues, this.currentTokenValue); @@ -113,11 +118,11 @@ export default { }, }, watch: { - tokenActive: { + active: { immediate: true, handler(newValue) { if (!newValue && !this.tokenValues.length) { - this.$emit('fetch-token-values', this.tokenValue.data); + this.$emit('fetch-token-values', this.value.data); } }, }, @@ -148,9 +153,11 @@ export default { <template> <gl-filtered-search-token - :config="tokenConfig" - v-bind="{ ...this.$parent.$props, ...this.$parent.$attrs }" - v-on="this.$parent.$listeners" + :config="config" + :value="value" + :active="active" + v-bind="$attrs" + v-on="$listeners" @input="handleInput" @select="handleTokenValueSelected(activeTokenValue)" > diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue index 20b8cbfe933..e496d099a42 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue @@ -93,15 +93,16 @@ export default { <template> <base-token - :token-config="config" - :token-value="value" - :token-active="active" + :config="config" + :value="value" + :active="active" :tokens-list-loading="loading" :token-values="labels" :fn-active-token-value="getActiveLabel" :default-token-values="defaultLabels" :recent-token-values-storage-key="config.recentTokenValuesStorageKey" @fetch-token-values="fetchLabelBySearchTerm" + v-on="$listeners" > <template #view-token="{ viewTokenProps: { inputValue, cssClasses, listeners, activeTokenValue } }" diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue index d80b66fd9be..1f0704f7308 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue @@ -1,5 +1,6 @@ <script> -import { mapGetters, mapState } from 'vuex'; +import { GlButton } from '@gitlab/ui'; +import { mapActions, mapGetters, mapState } from 'vuex'; import DropdownContentsCreateView from './dropdown_contents_create_view.vue'; import DropdownContentsLabelsView from './dropdown_contents_labels_view.vue'; @@ -8,6 +9,7 @@ export default { components: { DropdownContentsLabelsView, DropdownContentsCreateView, + GlButton, }, props: { renderOnTop: { @@ -15,10 +17,14 @@ export default { required: false, default: false, }, + labelsCreateTitle: { + type: String, + required: true, + }, }, computed: { - ...mapState(['showDropdownContentsCreateView']), - ...mapGetters(['isDropdownVariantSidebar']), + ...mapState(['showDropdownContentsCreateView', 'labelsListTitle']), + ...mapGetters(['isDropdownVariantSidebar', 'isDropdownVariantEmbedded']), dropdownContentsView() { if (this.showDropdownContentsCreateView) { return 'dropdown-contents-create-view'; @@ -29,6 +35,12 @@ export default { const bottom = this.isDropdownVariantSidebar ? '3rem' : '2rem'; return this.renderOnTop ? { bottom } : {}; }, + dropdownTitle() { + return this.showDropdownContentsCreateView ? this.labelsCreateTitle : this.labelsListTitle; + }, + }, + methods: { + ...mapActions(['toggleDropdownContentsCreateView', 'toggleDropdownContents']), }, }; </script> @@ -39,6 +51,30 @@ export default { data-qa-selector="labels_dropdown_content" :style="directionStyle" > - <component :is="dropdownContentsView" /> + <div + v-if="isDropdownVariantSidebar || isDropdownVariantEmbedded" + class="dropdown-title gl-display-flex gl-align-items-center gl-pt-0 gl-pb-3!" + data-testid="dropdown-title" + > + <gl-button + v-if="showDropdownContentsCreateView" + :aria-label="__('Go back')" + variant="link" + size="small" + class="js-btn-back dropdown-header-button p-0" + icon="arrow-left" + @click="toggleDropdownContentsCreateView" + /> + <span class="flex-grow-1">{{ dropdownTitle }}</span> + <gl-button + :aria-label="__('Close')" + variant="link" + size="small" + class="dropdown-header-button gl-p-0!" + icon="close" + @click="toggleDropdownContents" + /> + </div> + <component :is="dropdownContentsView" @hideCreateView="toggleDropdownContentsCreateView" /> </div> </template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue index f8cc981ba3d..a7f20fbe851 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue @@ -1,6 +1,10 @@ <script> import { GlTooltipDirective, GlButton, GlFormInput, GlLink, GlLoadingIcon } from '@gitlab/ui'; -import { mapState, mapActions } from 'vuex'; +import createFlash from '~/flash'; +import { __ } from '~/locale'; +import createLabelMutation from './graphql/create_label.mutation.graphql'; + +const errorMessage = __('Error creating label.'); export default { components: { @@ -12,14 +16,19 @@ export default { directives: { GlTooltip: GlTooltipDirective, }, + inject: { + projectPath: { + default: '', + }, + }, data() { return { labelTitle: '', selectedColor: '', + labelCreateInProgress: false, }; }, computed: { - ...mapState(['labelsCreateTitle', 'labelCreateInProgress']), disableCreate() { return !this.labelTitle.length || !this.selectedColor.length || this.labelCreateInProgress; }, @@ -29,7 +38,6 @@ export default { }, }, methods: { - ...mapActions(['toggleDropdownContents', 'toggleDropdownContentsCreateView', 'createLabel']), getColorCode(color) { return Object.keys(color).pop(); }, @@ -39,11 +47,27 @@ export default { handleColorClick(color) { this.selectedColor = this.getColorCode(color); }, - handleCreateClick() { - this.createLabel({ - title: this.labelTitle, - color: this.selectedColor, - }); + async createLabel() { + this.labelCreateInProgress = true; + try { + const { + data: { labelCreate }, + } = await this.$apollo.mutate({ + mutation: createLabelMutation, + variables: { + title: this.labelTitle, + color: this.selectedColor, + projectPath: this.projectPath, + }, + }); + if (labelCreate.errors.length) { + createFlash({ message: errorMessage }); + } + } catch { + createFlash({ message: errorMessage }); + } + this.labelCreateInProgress = false; + this.$emit('hideCreateView'); }, }, }; @@ -51,34 +75,16 @@ export default { <template> <div class="labels-select-contents-create js-labels-create"> - <div class="dropdown-title d-flex align-items-center pt-0 pb-2"> - <gl-button - :aria-label="__('Go back')" - variant="link" - size="small" - class="js-btn-back dropdown-header-button p-0" - icon="arrow-left" - @click="toggleDropdownContentsCreateView" - /> - <span class="flex-grow-1">{{ labelsCreateTitle }}</span> - <gl-button - :aria-label="__('Close')" - variant="link" - size="small" - class="dropdown-header-button p-0" - icon="close" - @click="toggleDropdownContents" - /> - </div> <div class="dropdown-input"> <gl-form-input v-model.trim="labelTitle" :placeholder="__('Name new label')" :autofocus="true" + data-testid="label-title-input" /> </div> - <div class="dropdown-content px-2"> - <div class="suggest-colors suggest-colors-dropdown mt-0 mb-2"> + <div class="dropdown-content gl-px-3"> + <div class="suggest-colors suggest-colors-dropdown gl-mt-0! gl-mb-3!"> <gl-link v-for="(color, index) in suggestedColors" :key="index" @@ -90,28 +96,35 @@ export default { </div> <div class="color-input-container gl-display-flex"> <span - class="dropdown-label-color-preview position-relative position-relative d-inline-block" + class="dropdown-label-color-preview gl-relative gl-display-inline-block" + data-testid="selected-color" :style="{ backgroundColor: selectedColor }" ></span> <gl-form-input v-model.trim="selectedColor" class="gl-rounded-top-left-none gl-rounded-bottom-left-none" :placeholder="__('Use custom color #FF0000')" + data-testid="selected-color-text" /> </div> </div> - <div class="dropdown-actions clearfix pt-2 px-2"> + <div class="dropdown-actions gl-display-flex gl-justify-content-space-between gl-pt-3 gl-px-3"> <gl-button :disabled="disableCreate" category="primary" variant="success" - class="float-left d-flex align-items-center" - @click="handleCreateClick" + class="gl-display-flex gl-align-items-center" + data-testid="create-button" + @click="createLabel" > - <gl-loading-icon v-show="labelCreateInProgress" :inline="true" class="mr-1" /> + <gl-loading-icon v-if="labelCreateInProgress" :inline="true" class="mr-1" /> {{ __('Create') }} </gl-button> - <gl-button class="float-right js-btn-cancel-create" @click="toggleDropdownContentsCreateView"> + <gl-button + class="js-btn-cancel-create" + data-testid="cancel-button" + @click="$emit('hideCreateView')" + > {{ __('Cancel') }} </gl-button> </div> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue index 86788a84260..bff34743344 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue @@ -1,11 +1,5 @@ <script> -import { - GlIntersectionObserver, - GlLoadingIcon, - GlButton, - GlSearchBoxByType, - GlLink, -} from '@gitlab/ui'; +import { GlIntersectionObserver, GlLoadingIcon, GlSearchBoxByType, GlLink } from '@gitlab/ui'; import fuzzaldrinPlus from 'fuzzaldrin-plus'; import { mapState, mapGetters, mapActions } from 'vuex'; @@ -17,7 +11,6 @@ export default { components: { GlIntersectionObserver, GlLoadingIcon, - GlButton, GlSearchBoxByType, GlLink, LabelItem, @@ -149,21 +142,6 @@ export default { <template> <gl-intersection-observer @appear="handleComponentAppear" @disappear="handleComponentDisappear"> <div class="labels-select-contents-list js-labels-list" @keydown="handleKeyDown"> - <div - v-if="isDropdownVariantSidebar || isDropdownVariantEmbedded" - class="dropdown-title gl-display-flex gl-align-items-center gl-pt-0 gl-pb-3!" - data-testid="dropdown-title" - > - <span class="flex-grow-1">{{ labelsListTitle }}</span> - <gl-button - :aria-label="__('Close')" - variant="link" - size="small" - class="dropdown-header-button gl-p-0!" - icon="close" - @click="toggleDropdownContents" - /> - </div> <div class="dropdown-input" @click.stop="() => {}"> <gl-search-box-by-type ref="searchInput" diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value_collapsed.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value_collapsed.vue new file mode 100644 index 00000000000..122250d1ce7 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value_collapsed.vue @@ -0,0 +1,55 @@ +<script> +import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; +import { s__, sprintf } from '~/locale'; + +export default { + directives: { + GlTooltip: GlTooltipDirective, + }, + components: { + GlIcon, + }, + props: { + labels: { + type: Array, + required: true, + }, + }, + computed: { + labelsList() { + const labelsString = this.labels.length + ? this.labels + .slice(0, 5) + .map((label) => label.title) + .join(', ') + : s__('LabelSelect|Labels'); + + if (this.labels.length > 5) { + return sprintf(s__('LabelSelect|%{labelsString}, and %{remainingLabelCount} more'), { + labelsString, + remainingLabelCount: this.labels.length - 5, + }); + } + + return labelsString; + }, + }, + methods: { + handleClick() { + this.$emit('onValueClick'); + }, + }, +}; +</script> + +<template> + <div + v-gl-tooltip.left.viewport + :title="labelsList" + class="sidebar-collapsed-icon" + @click="handleClick" + > + <gl-icon name="labels" /> + <span>{{ labels.length }}</span> + </div> +</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/create_label.mutation.graphql b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/create_label.mutation.graphql new file mode 100644 index 00000000000..9aa4f5d165e --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/create_label.mutation.graphql @@ -0,0 +1,15 @@ +mutation createLabel($title: String!, $color: String, $projectPath: ID, $groupPath: ID) { + labelCreate( + input: { title: $title, color: $color, projectPath: $projectPath, groupPath: $groupPath } + ) { + label { + id + color + description + descriptionHtml + title + textColor + } + errors + } +} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue index bf30e3cfac5..7728c758e18 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue @@ -5,13 +5,12 @@ import Vuex, { mapState, mapActions, mapGetters } from 'vuex'; import { isInViewport } from '~/lib/utils/common_utils'; import { __ } from '~/locale'; -import DropdownValueCollapsed from '~/vue_shared/components/sidebar/labels_select_vue/dropdown_value_collapsed.vue'; - import { DropdownVariant } from './constants'; import DropdownButton from './dropdown_button.vue'; import DropdownContents from './dropdown_contents.vue'; import DropdownTitle from './dropdown_title.vue'; import DropdownValue from './dropdown_value.vue'; +import DropdownValueCollapsed from './dropdown_value_collapsed.vue'; import labelsSelectModule from './store'; Vue.use(Vuex); @@ -163,7 +162,6 @@ export default { labelsFilterBasePath: this.labelsFilterBasePath, labelsFilterParam: this.labelsFilterParam, labelsListTitle: this.labelsListTitle, - labelsCreateTitle: this.labelsCreateTitle, footerCreateLabelTitle: this.footerCreateLabelTitle, footerManageLabelTitle: this.footerManageLabelTitle, }); @@ -313,6 +311,7 @@ export default { v-show="dropdownButtonVisible && showDropdownContents" ref="dropdownContents" :render-on-top="!contentIsOnViewport" + :labels-create-title="labelsCreateTitle" /> </template> <template v-if="isDropdownVariantStandalone || isDropdownVariantEmbedded"> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/actions.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/actions.js index 89f96ab916b..2b96b159ca3 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/actions.js +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/actions.js @@ -28,31 +28,5 @@ export const fetchLabels = ({ state, dispatch }) => { .catch(() => dispatch('receiveLabelsFailure')); }; -export const requestCreateLabel = ({ commit }) => commit(types.REQUEST_CREATE_LABEL); -export const receiveCreateLabelSuccess = ({ commit }) => commit(types.RECEIVE_CREATE_LABEL_SUCCESS); -export const receiveCreateLabelFailure = ({ commit }) => { - commit(types.RECEIVE_CREATE_LABEL_FAILURE); - flash(__('Error creating label.')); -}; -export const createLabel = ({ state, dispatch }, label) => { - dispatch('requestCreateLabel'); - axios - .post(state.labelsManagePath, { - label, - }) - .then(({ data }) => { - if (data.id) { - dispatch('receiveCreateLabelSuccess'); - dispatch('toggleDropdownContentsCreateView'); - } else { - // eslint-disable-next-line @gitlab/require-i18n-strings - throw new Error('Error Creating Label'); - } - }) - .catch(() => { - dispatch('receiveCreateLabelFailure'); - }); -}; - export const updateSelectedLabels = ({ commit }, labels) => commit(types.UPDATE_SELECTED_LABELS, { labels }); diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/mutation_types.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/mutation_types.js index 2e044dc3b3c..b8da7a90b36 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/mutation_types.js +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/mutation_types.js @@ -8,10 +8,6 @@ export const REQUEST_SET_LABELS = 'REQUEST_SET_LABELS'; export const RECEIVE_SET_LABELS_SUCCESS = 'RECEIVE_SET_LABELS_SUCCESS'; export const RECEIVE_SET_LABELS_FAILURE = 'RECEIVE_SET_LABELS_FAILURE'; -export const REQUEST_CREATE_LABEL = 'REQUEST_CREATE_LABEL'; -export const RECEIVE_CREATE_LABEL_SUCCESS = 'RECEIVE_CREATE_LABEL_SUCCESS'; -export const RECEIVE_CREATE_LABEL_FAILURE = 'RECEIVE_CREATE_LABEL_FAILURE'; - export const TOGGLE_DROPDOWN_BUTTON = 'TOGGLE_DROPDOWN_VISIBILITY'; export const TOGGLE_DROPDOWN_CONTENTS = 'TOGGLE_DROPDOWN_CONTENTS'; diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/mutations.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/mutations.js index 55716e1105e..131c6e6fb57 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/mutations.js +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/mutations.js @@ -46,17 +46,6 @@ export default { [types.RECEIVE_SET_LABELS_FAILURE](state) { state.labelsFetchInProgress = false; }, - - [types.REQUEST_CREATE_LABEL](state) { - state.labelCreateInProgress = true; - }, - [types.RECEIVE_CREATE_LABEL_SUCCESS](state) { - state.labelCreateInProgress = false; - }, - [types.RECEIVE_CREATE_LABEL_FAILURE](state) { - state.labelCreateInProgress = false; - }, - [types.UPDATE_SELECTED_LABELS](state, { labels }) { // Find the label to update from all the labels // and change `set` prop value to represent their current state. diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/state.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/state.js index d66cfed4163..220bab05ed2 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/state.js +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/state.js @@ -3,7 +3,6 @@ export default () => ({ labels: [], selectedLabels: [], labelsListTitle: '', - labelsCreateTitle: '', footerCreateLabelTitle: '', footerManageLabelTitle: '', dropdownButtonText: '', diff --git a/app/controllers/projects/merge_requests/content_controller.rb b/app/controllers/projects/merge_requests/content_controller.rb index dfc060c9204..399745151b1 100644 --- a/app/controllers/projects/merge_requests/content_controller.rb +++ b/app/controllers/projects/merge_requests/content_controller.rb @@ -14,8 +14,6 @@ class Projects::MergeRequests::ContentController < Projects::MergeRequests::Appl SLOW_POLLING_INTERVAL = 5.minutes.in_milliseconds def widget - check_mergeability_async! - respond_to do |format| format.json do render json: serializer(MergeRequestPollWidgetEntity) @@ -40,13 +38,6 @@ class Projects::MergeRequests::ContentController < Projects::MergeRequests::Appl def serializer(entity) serializer = MergeRequestSerializer.new(current_user: current_user, project: merge_request.project) - serializer.represent(merge_request, { async_mergeability_check: params[:async_mergeability_check] }, entity) - end - - def check_mergeability_async! - return unless Feature.enabled?(:check_mergeability_async_in_widget, merge_request.project, default_enabled: :yaml) - return if params[:async_mergeability_check].blank? - - merge_request.check_mergeability(async: true) + serializer.represent(merge_request, {}, entity) end end diff --git a/app/models/issue.rb b/app/models/issue.rb index b0a126c4442..48f388ea48d 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -23,6 +23,7 @@ class Issue < ApplicationRecord include IssueAvailableFeatures include Todoable include FromUnion + include EachBatch extend ::Gitlab::Utils::Override diff --git a/app/policies/base_policy.rb b/app/policies/base_policy.rb index 0f7a6b852ab..ea1ea87ff2f 100644 --- a/app/policies/base_policy.rb +++ b/app/policies/base_policy.rb @@ -27,6 +27,10 @@ class BasePolicy < DeclarativePolicy::Base with_options scope: :user, score: 0 condition(:security_bot) { @user&.security_bot? } + desc "User is automation bot" + with_options scope: :user, score: 0 + condition(:automation_bot) { @user&.automation_bot? } + desc "User email is unconfirmed or user account is locked" with_options scope: :user, score: 0 condition(:inactive) { @user&.confirmation_required_on_sign_in? || @user&.access_locked? } diff --git a/app/policies/concerns/policy_actor.rb b/app/policies/concerns/policy_actor.rb index cbc34bdeed3..513bb85f538 100644 --- a/app/policies/concerns/policy_actor.rb +++ b/app/policies/concerns/policy_actor.rb @@ -53,6 +53,10 @@ module PolicyActor false end + def automation_bot? + false + end + def deactivated? false end diff --git a/app/serializers/merge_request_poll_widget_entity.rb b/app/serializers/merge_request_poll_widget_entity.rb index c00dceadf22..3ce67d92af1 100644 --- a/app/serializers/merge_request_poll_widget_entity.rb +++ b/app/serializers/merge_request_poll_widget_entity.rb @@ -31,7 +31,6 @@ class MergeRequestPollWidgetEntity < Grape::Entity expose :mergeable do |merge_request, options| next merge_request.mergeable? if Feature.disabled?(:check_mergeability_async_in_widget, merge_request.project, default_enabled: :yaml) - next false if options[:async_mergeability_check].present? && merge_request.checking? merge_request.mergeable? end diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb index ac9970579ed..0616d94a1ed 100644 --- a/app/serializers/merge_request_widget_entity.rb +++ b/app/serializers/merge_request_widget_entity.rb @@ -36,7 +36,7 @@ class MergeRequestWidgetEntity < Grape::Entity end expose :merge_request_widget_path do |merge_request| - widget_project_json_merge_request_path(merge_request.target_project, merge_request, async_mergeability_check: true, format: :json) + widget_project_json_merge_request_path(merge_request.target_project, merge_request, format: :json) end expose :merge_request_cached_widget_path do |merge_request| diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml index 49f2795538c..691ce8dc5fc 100644 --- a/app/views/projects/merge_requests/show.html.haml +++ b/app/views/projects/merge_requests/show.html.haml @@ -62,7 +62,7 @@ - add_page_startup_api_call notes_url - else - add_page_startup_api_call discussions_path(@merge_request) - - add_page_startup_api_call widget_project_json_merge_request_path(@project, @merge_request, async_mergeability_check: true, format: :json) + - add_page_startup_api_call widget_project_json_merge_request_path(@project, @merge_request, format: :json) - add_page_startup_api_call cached_widget_project_json_merge_request_path(@project, @merge_request, format: :json) #js-vue-mr-discussions{ data: { notes_data: notes_data(@merge_request, Feature.enabled?(:paginated_notes, @project)).to_json, endpoint_metadata: @endpoint_metadata_url, |