diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-12-05 21:07:51 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-12-05 21:07:51 +0300 |
commit | 6a7cc8c14727f6fac64a5be6838764d8d5d41468 (patch) | |
tree | 97c8a3c2f180d26f0f8f0baaa3230352b8ef1efb /app | |
parent | 872319738757edc0483346c75a2407f7019b963f (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
17 files changed, 143 insertions, 153 deletions
diff --git a/app/assets/javascripts/boards/services/board_service.js b/app/assets/javascripts/boards/services/board_service.js deleted file mode 100644 index 03369febb4a..00000000000 --- a/app/assets/javascripts/boards/services/board_service.js +++ /dev/null @@ -1,98 +0,0 @@ -/* eslint-disable class-methods-use-this */ -/** - * This file is intended to be deleted. - * The existing functions will removed one by one in favor of using the board store directly. - * see https://gitlab.com/gitlab-org/gitlab-foss/issues/61621 - */ - -import boardsStore from '~/boards/stores/boards_store'; - -export default class BoardService { - generateBoardsPath(id) { - return boardsStore.generateBoardsPath(id); - } - - generateIssuesPath(id) { - return boardsStore.generateIssuesPath(id); - } - - static generateIssuePath(boardId, id) { - return boardsStore.generateIssuePath(boardId, id); - } - - all() { - return boardsStore.all(); - } - - generateDefaultLists() { - return boardsStore.generateDefaultLists(); - } - - createList(entityId, entityType) { - return boardsStore.createList(entityId, entityType); - } - - updateList(id, position, collapsed) { - return boardsStore.updateList(id, position, collapsed); - } - - destroyList(id) { - return boardsStore.destroyList(id); - } - - getIssuesForList(id, filter = {}) { - return boardsStore.getIssuesForList(id, filter); - } - - moveIssue(id, fromListId = null, toListId = null, moveBeforeId = null, moveAfterId = null) { - return boardsStore.moveIssue(id, fromListId, toListId, moveBeforeId, moveAfterId); - } - - moveMultipleIssues({ - ids, - fromListId = null, - toListId = null, - moveBeforeId = null, - moveAfterId = null, - }) { - return boardsStore.moveMultipleIssues({ ids, fromListId, toListId, moveBeforeId, moveAfterId }); - } - - newIssue(id, issue) { - return boardsStore.newIssue(id, issue); - } - - getBacklog(data) { - return boardsStore.getBacklog(data); - } - - bulkUpdate(issueIds, extraData = {}) { - return boardsStore.bulkUpdate(issueIds, extraData); - } - - static getIssueInfo(endpoint) { - return boardsStore.getIssueInfo(endpoint); - } - - static toggleIssueSubscription(endpoint) { - return boardsStore.toggleIssueSubscription(endpoint); - } - - allBoards() { - return boardsStore.allBoards(); - } - - recentBoards() { - return boardsStore.recentBoards(); - } - - createBoard(board) { - return boardsStore.createBoard(board); - } - - deleteBoard({ id }) { - return boardsStore.deleteBoard({ id }); - } -} - -window.BoardService = BoardService; diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue index 9d1de4ef8a0..7df99610132 100644 --- a/app/assets/javascripts/notes/components/notes_app.vue +++ b/app/assets/javascripts/notes/components/notes_app.vue @@ -1,6 +1,6 @@ <script> -import { __ } from '~/locale'; import { mapGetters, mapActions } from 'vuex'; +import { __ } from '~/locale'; import { getLocationHash, doesHashExistInUrl } from '../../lib/utils/url_utility'; import Flash from '../../flash'; import * as constants from '../constants'; @@ -71,6 +71,9 @@ export default { 'userCanReply', 'discussionTabCounter', ]), + discussionTabCounterText() { + return this.isLoading ? '' : this.discussionTabCounter; + }, noteableType() { return this.noteableData.noteableType; }, @@ -95,9 +98,9 @@ export default { this.fetchNotes(); } }, - allDiscussions() { - if (this.discussionsCount && !this.isLoading) { - this.discussionsCount.textContent = this.discussionTabCounter; + discussionTabCounterText(val) { + if (this.discussionsCount) { + this.discussionsCount.textContent = val; } }, }, diff --git a/app/assets/javascripts/vue_merge_request_widget/components/loading.vue b/app/assets/javascripts/vue_merge_request_widget/components/loading.vue new file mode 100644 index 00000000000..78dc28ee92b --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/components/loading.vue @@ -0,0 +1,29 @@ +<script> +import { GlSkeletonLoader } from '@gitlab/ui'; + +export default { + components: { + GlSkeletonLoader, + }, +}; +</script> + +<template> + <div class="prepend-top-default"> + <div class="mr-widget-heading p-3"> + <gl-skeleton-loader :width="577" :height="12"> + <rect width="86" height="12" rx="2" /> + <rect x="96" width="300" height="12" rx="2" /> + </gl-skeleton-loader> + </div> + <div class="mr-widget-heading mr-widget-workflow p-3"> + <gl-skeleton-loader :width="577" :height="72"> + <rect width="120" height="12" rx="2" /> + <rect y="20" width="300" height="12" rx="2" /> + <rect y="40" width="60" height="12" rx="2" /> + <rect y="40" x="68" width="100" height="12" rx="2" /> + <rect y="60" width="40" height="12" rx="2" /> + </gl-skeleton-loader> + </div> + </div> +</template> diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index 363fe226f15..31dbddbd21a 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -7,6 +7,7 @@ import MRWidgetStore from 'ee_else_ce/vue_merge_request_widget/stores/mr_widget_ import MRWidgetService from 'ee_else_ce/vue_merge_request_widget/services/mr_widget_service'; import stateMaps from 'ee_else_ce/vue_merge_request_widget/stores/state_maps'; import createFlash from '../flash'; +import Loading from './components/loading.vue'; import WidgetHeader from './components/mr_widget_header.vue'; import WidgetMergeHelp from './components/mr_widget_merge_help.vue'; import MrWidgetPipelineContainer from './components/mr_widget_pipeline_container.vue'; @@ -44,6 +45,7 @@ export default { // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings name: 'MRWidget', components: { + Loading, 'mr-widget-header': WidgetHeader, 'mr-widget-merge-help': WidgetMergeHelp, MrWidgetPipelineContainer, @@ -80,12 +82,12 @@ export default { }, }, data() { - const store = new MRWidgetStore(this.mrData || window.gl.mrWidgetData); - const service = this.createService(store); + const store = this.mrData && new MRWidgetStore(this.mrData); + return { mr: store, - state: store.state, - service, + state: store && store.state, + service: store && this.createService(store), }; }, computed: { @@ -133,29 +135,58 @@ export default { } }, }, - created() { - this.initPolling(); - this.bindEventHubListeners(); - eventHub.$on('mr.discussion.updated', this.checkStatus); - }, mounted() { - this.setFaviconHelper(); - this.initDeploymentsPolling(); - - if (this.shouldRenderMergedPipeline) { - this.initPostMergeDeploymentsPolling(); + if (gon && gon.features && gon.features.asyncMrWidget) { + MRWidgetService.fetchInitialData() + .then(({ data }) => this.initWidget(data)) + .catch(() => + createFlash(__('Unable to load the merge request widget. Try reloading the page.')), + ); + } else { + this.initWidget(); } }, beforeDestroy() { eventHub.$off('mr.discussion.updated', this.checkStatus); - this.pollingInterval.destroy(); - this.deploymentsInterval.destroy(); + if (this.pollingInterval) { + this.pollingInterval.destroy(); + } + + if (this.deploymentsInterval) { + this.deploymentsInterval.destroy(); + } if (this.postMergeDeploymentsInterval) { this.postMergeDeploymentsInterval.destroy(); } }, methods: { + initWidget(data = {}) { + if (this.mr) { + this.mr.setData({ ...window.gl.mrWidgetData, ...data }); + } else { + this.mr = new MRWidgetStore({ ...window.gl.mrWidgetData, ...data }); + } + + if (!this.state) { + this.state = this.mr.state; + } + + if (!this.service) { + this.service = this.createService(this.mr); + } + + this.setFaviconHelper(); + this.initDeploymentsPolling(); + + if (this.shouldRenderMergedPipeline) { + this.initPostMergeDeploymentsPolling(); + } + + this.initPolling(); + this.bindEventHubListeners(); + eventHub.$on('mr.discussion.updated', this.checkStatus); + }, getServiceEndpoints(store) { return { mergePath: store.mergePath, @@ -319,7 +350,7 @@ export default { }; </script> <template> - <div class="mr-state-widget prepend-top-default"> + <div v-if="mr" class="mr-state-widget prepend-top-default"> <mr-widget-header :mr="mr" /> <mr-widget-pipeline-container v-if="shouldRenderPipelines" @@ -377,4 +408,5 @@ export default { :is-post-merge="true" /> </div> + <loading v-else /> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js b/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js index 8a229d80954..d22cb4ced80 100644 --- a/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js +++ b/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js @@ -61,4 +61,11 @@ export default class MRWidgetService { static fetchMetrics(metricsUrl) { return axios.get(`${metricsUrl}.json`); } + + static fetchInitialData() { + return Promise.all([ + axios.get(window.gl.mrWidgetData.merge_request_cached_widget_path), + axios.get(window.gl.mrWidgetData.merge_request_widget_path), + ]).then(axios.spread((res, cachedRes) => ({ data: Object.assign(res.data, cachedRes.data) }))); + } } diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index eb7d162e38c..c023c9e5cbd 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -51,6 +51,10 @@ position: relative; border: 1px solid $border-color; border-radius: $border-radius-default; + + .gl-skeleton-loader { + display: block; + } } .mr-widget-extension { diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 844f1d04679..07f568e2a04 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -25,6 +25,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo before_action do push_frontend_feature_flag(:vue_issuable_sidebar, @project.group) push_frontend_feature_flag(:release_search_filter, @project, default_enabled: true) + push_frontend_feature_flag(:async_mr_widget, @project) end around_action :allow_gitaly_ref_name_caching, only: [:index, :show, :discussions] diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 4d35353d5f5..e3ef8f3f2ff 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -11,7 +11,6 @@ class Projects::PipelinesController < Projects::ApplicationController before_action :authorize_create_pipeline!, only: [:new, :create] before_action :authorize_update_pipeline!, only: [:retry, :cancel] before_action do - push_frontend_feature_flag(:hide_dismissed_vulnerabilities) push_frontend_feature_flag(:junit_pipeline_view) end diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index 38ca12e6f90..3810041b2af 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -195,7 +195,7 @@ module GitlabRoutingHelper end def snippet_path(snippet, *args) - if snippet.is_a?(ProjectSnippet) + if snippet.type == "ProjectSnippet" application_url_helpers.project_snippet_path(snippet.project, snippet, *args) else new_args = snippet_query_params(snippet, *args) @@ -204,7 +204,7 @@ module GitlabRoutingHelper end def snippet_url(snippet, *args) - if snippet.is_a?(ProjectSnippet) + if snippet.type == "ProjectSnippet" application_url_helpers.project_snippet_url(snippet.project, snippet, *args) else new_args = snippet_query_params(snippet, *args) @@ -213,7 +213,7 @@ module GitlabRoutingHelper end def raw_snippet_path(snippet, *args) - if snippet.is_a?(ProjectSnippet) + if snippet.type == "ProjectSnippet" application_url_helpers.raw_project_snippet_path(snippet.project, snippet, *args) else new_args = snippet_query_params(snippet, *args) @@ -222,7 +222,7 @@ module GitlabRoutingHelper end def raw_snippet_url(snippet, *args) - if snippet.is_a?(ProjectSnippet) + if snippet.type == "ProjectSnippet" application_url_helpers.raw_project_snippet_url(snippet.project, snippet, *args) else new_args = snippet_query_params(snippet, *args) diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index 777fe82e4c0..a89fea4b7b8 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -31,13 +31,14 @@ module SearchHelper from = collection.offset_value + 1 to = collection.offset_value + collection.to_a.size count = collection.total_count + term_element = "<span> <code>#{h(term)}</code> </span>".html_safe search_entries_info_template(collection) % { from: from, to: to, count: count, scope: search_entries_scope_label(scope, count), - term: term + term_element: term_element } end @@ -72,9 +73,9 @@ module SearchHelper def search_entries_info_template(collection) if collection.total_pages > 1 - s_("SearchResults|Showing %{from} - %{to} of %{count} %{scope} for \"%{term}\"") + s_("SearchResults|Showing %{from} - %{to} of %{count} %{scope} for%{term_element}").html_safe else - s_("SearchResults|Showing %{count} %{scope} for \"%{term}\"") + s_("SearchResults|Showing %{count} %{scope} for%{term_element}").html_safe end end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 5821cc1a1a5..c3292d7524e 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -204,7 +204,7 @@ module Ci end scope :internal, -> { where(source: internal_sources) } - scope :ci_sources, -> { where(config_source: ci_sources_values) } + scope :ci_sources, -> { where(config_source: ::Ci::PipelineEnums.ci_config_sources_values) } scope :for_user, -> (user) { where(user: user) } scope :for_sha, -> (sha) { where(sha: sha) } scope :for_source_sha, -> (source_sha) { where(source_sha: source_sha) } @@ -315,10 +315,6 @@ module Ci sources.reject { |source| source == "external" }.values end - def self.ci_sources_values - config_sources.values_at(:repository_source, :auto_devops_source, :unknown_source) - end - def self.bridgeable_statuses ::Ci::Pipeline::AVAILABLE_STATUSES - %w[created preparing pending] end diff --git a/app/models/ci/pipeline_enums.rb b/app/models/ci/pipeline_enums.rb index 859abc4a0d5..ac930f63abf 100644 --- a/app/models/ci/pipeline_enums.rb +++ b/app/models/ci/pipeline_enums.rb @@ -35,9 +35,20 @@ module Ci { unknown_source: nil, repository_source: 1, - auto_devops_source: 2 + auto_devops_source: 2, + remote_source: 4, + external_project_source: 5 } end + + def self.ci_config_sources_values + config_sources.values_at( + :unknown_source, + :repository_source, + :auto_devops_source, + :remote_source, + :external_project_source) + end end end diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index f02ccd9e55e..48c96203921 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -58,7 +58,7 @@ class ProjectWiki end def wiki_base_path - [Gitlab.config.gitlab.relative_url_root, '/', @project.full_path, '/wikis'].join('') + [Gitlab.config.gitlab.relative_url_root, '/', @project.full_path, '/-', '/wikis'].join('') end # Returns the Gitlab::Git::Wiki object. diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb index eda7a36c2ee..2a81931c49f 100644 --- a/app/serializers/merge_request_widget_entity.rb +++ b/app/serializers/merge_request_widget_entity.rb @@ -3,6 +3,9 @@ class MergeRequestWidgetEntity < Grape::Entity include RequestAwareEntity + expose :id + expose :iid + expose :source_project_full_path do |merge_request| merge_request.source_project&.full_path end @@ -65,6 +68,8 @@ class MergeRequestWidgetEntity < Grape::Entity end def as_json(options = {}) + return super(options) if Feature.enabled?(:async_mr_widget) + super(options) .merge(MergeRequestPollCachedWidgetEntity.new(object, **@options.opts_hash).as_json(options)) .merge(MergeRequestPollWidgetEntity.new(object, **@options.opts_hash).as_json(options)) diff --git a/app/views/ci/variables/_variable_row.html.haml b/app/views/ci/variables/_variable_row.html.haml index ed9b3ab1940..4244556a24a 100644 --- a/app/views/ci/variables/_variable_row.html.haml +++ b/app/views/ci/variables/_variable_row.html.haml @@ -44,31 +44,21 @@ .ci-variable-body-item.ci-variable-protected-item.table-section.section-20.mr-0.border-top-0 .append-right-default = s_("CiVariable|Protected") - %button{ type: 'button', - class: "js-project-feature-toggle project-feature-toggle #{'is-checked' if is_protected}", - "aria-label": s_("CiVariable|Toggle protected") } + = render "shared/buttons/project_feature_toggle", is_checked: is_protected, label: s_("CiVariable|Toggle protected") do %input{ type: "hidden", class: 'js-ci-variable-input-protected js-project-feature-toggle-input', name: protected_input_name, value: is_protected, data: { default: is_protected_default.to_s } } - %span.toggle-icon - = sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked') - = sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked') .ci-variable-body-item.ci-variable-masked-item.table-section.section-20.mr-0.border-top-0 .append-right-default = s_("CiVariable|Masked") - %button{ type: 'button', - class: "js-project-feature-toggle project-feature-toggle qa-variable-masked #{'is-checked' if is_masked}", - "aria-label": s_("CiVariable|Toggle masked") } + = render "shared/buttons/project_feature_toggle", is_checked: is_masked, label: s_("CiVariable|Toggle masked"), class_list: "js-project-feature-toggle project-feature-toggle qa-variable-masked" do %input{ type: "hidden", class: 'js-ci-variable-input-masked js-project-feature-toggle-input', name: masked_input_name, value: is_masked, data: { default: is_masked_default.to_s } } - %span.toggle-icon - = sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked') - = sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked') = render_if_exists 'ci/variables/environment_scope', form_field: form_field, variable: variable %button.js-row-remove-button.ci-variable-row-remove-button.table-section.section-5.border-top-0{ type: 'button', 'aria-label': s_('CiVariables|Remove variable row') } = icon('minus-circle') diff --git a/app/views/clusters/clusters/_form.html.haml b/app/views/clusters/clusters/_form.html.haml index 3d0266a2d5b..f9085b781fb 100644 --- a/app/views/clusters/clusters/_form.html.haml +++ b/app/views/clusters/clusters/_form.html.haml @@ -3,14 +3,8 @@ .form-group %h5= s_('ClusterIntegration|Integration status') %label.append-bottom-0.js-cluster-enable-toggle-area - %button{ type: 'button', - class: "js-project-feature-toggle project-feature-toggle #{'is-checked' if @cluster.enabled?} #{'is-disabled' unless can?(current_user, :update_cluster, @cluster)}", - "aria-label": s_("ClusterIntegration|Toggle Kubernetes cluster"), - disabled: !can?(current_user, :update_cluster, @cluster) } + = render "shared/buttons/project_feature_toggle", is_checked: @cluster.enabled?, label: s_("ClusterIntegration|Toggle Kubernetes cluster"), disabled: !can?(current_user, :update_cluster, @cluster) do = field.hidden_field :enabled, { class: 'js-project-feature-toggle-input'} - %span.toggle-icon - = sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked') - = sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked') .form-text.text-muted= s_('ClusterIntegration|Enable or disable GitLab\'s connection to your Kubernetes cluster.') .form-group diff --git a/app/views/shared/buttons/_project_feature_toggle.html.haml b/app/views/shared/buttons/_project_feature_toggle.html.haml new file mode 100644 index 00000000000..0f630786455 --- /dev/null +++ b/app/views/shared/buttons/_project_feature_toggle.html.haml @@ -0,0 +1,16 @@ +- class_list ||= "js-project-feature-toggle project-feature-toggle" +- data ||= nil +- disabled ||= false +- is_checked ||= false +- label ||= nil + +%button{ type: 'button', + class: "#{class_list} #{'is-disabled' if disabled} #{'is-checked' if is_checked}", + "aria-label": label, + disabled: disabled, + data: data } + - if yield.present? + = yield + %span.toggle-icon + = sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked') + = sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked') |