diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-15 09:09:39 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-15 09:09:39 +0300 |
commit | 01ef10900ad5ce2efea5abe6bbbc6d118b9ee6f8 (patch) | |
tree | a71b64d6c9bda41468bac890a5614c8ff958ffa7 /app | |
parent | a3ac132686ea5e5e83c184334bf7f03bb641211c (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
19 files changed, 161 insertions, 84 deletions
diff --git a/app/assets/javascripts/boards/components/board_content_sidebar.vue b/app/assets/javascripts/boards/components/board_content_sidebar.vue index f927b03a22b..e014b82d362 100644 --- a/app/assets/javascripts/boards/components/board_content_sidebar.vue +++ b/app/assets/javascripts/boards/components/board_content_sidebar.vue @@ -11,6 +11,7 @@ import SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assig import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue'; import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue'; import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue'; +import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; export default { @@ -24,6 +25,7 @@ export default { BoardSidebarLabelsSelect, SidebarSubscriptionsWidget, SidebarDropdownWidget, + SidebarTodoWidget, MountingPortal, SidebarWeightWidget: () => import('ee_component/sidebar/components/weight/sidebar_weight_widget.vue'), @@ -90,6 +92,15 @@ export default { <template #title> <h2 class="gl-my-0 gl-font-size-h2 gl-line-height-24">{{ __('Issue details') }}</h2> </template> + <template #header> + <sidebar-todo-widget + class="gl-mt-3" + :issuable-id="activeBoardItem.fullId" + :issuable-iid="activeBoardItem.iid" + :full-path="fullPath" + :issuable-type="issuableType" + /> + </template> <template #default> <board-sidebar-title /> <sidebar-assignees-widget diff --git a/app/assets/javascripts/design_management/components/design_todo_button.vue b/app/assets/javascripts/design_management/components/design_todo_button.vue index c614fee17d1..013dd1d89f3 100644 --- a/app/assets/javascripts/design_management/components/design_todo_button.vue +++ b/app/assets/javascripts/design_management/components/design_todo_button.vue @@ -1,6 +1,6 @@ <script> import todoMarkDoneMutation from '~/graphql_shared/mutations/todo_mark_done.mutation.graphql'; -import TodoButton from '~/vue_shared/components/sidebar/todo_button.vue'; +import TodoButton from '~/vue_shared/components/sidebar/todo_toggle/todo_button.vue'; import createDesignTodoMutation from '../graphql/mutations/create_design_todo.mutation.graphql'; import getDesignQuery from '../graphql/queries/get_design.query.graphql'; import allVersionsMixin from '../mixins/all_versions'; diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js index 24041a60e3d..36f5e6f4ce1 100644 --- a/app/assets/javascripts/right_sidebar.js +++ b/app/assets/javascripts/right_sidebar.js @@ -2,7 +2,7 @@ import $ from 'jquery'; import Cookies from 'js-cookie'; -import { fixTitle, hide } from '~/tooltips'; +import { hide } from '~/tooltips'; import createFlash from './flash'; import axios from './lib/utils/axios_utils'; import { sprintf, s__, __ } from './locale'; @@ -107,36 +107,6 @@ Sidebar.prototype.toggleTodo = function (e) { ); }; -Sidebar.prototype.todoUpdateDone = function (data) { - const deletePath = data.delete_path ? data.delete_path : null; - const attrPrefix = deletePath ? 'mark' : 'todo'; - const $todoBtns = $('.js-issuable-todo'); - - $(document).trigger('todo:toggle', data.count); - - $todoBtns.each((i, el) => { - const $el = $(el); - const $elText = $el.find('.js-issuable-todo-inner'); - - $el - .removeClass('is-loading') - .enable() - .attr('aria-label', $el.data(`${attrPrefix}Text`)) - .attr('title', $el.data(`${attrPrefix}Text`)) - .data('deletePath', deletePath); - - if ($el.hasClass('has-tooltip')) { - fixTitle(el); - } - - if (typeof $el.data('isCollapsed') !== 'undefined') { - $elText.html($el.data(`${attrPrefix}Icon`)); - } else { - $elText.text($el.data(`${attrPrefix}Text`)); - } - }); -}; - Sidebar.prototype.sidebarCollapseClicked = function (e) { if ($(e.currentTarget).hasClass('dont-change-state')) { return; 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 18eab0126a4..a9c4203af22 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 @@ -1,18 +1,26 @@ <script> -import { GlTooltipDirective } from '@gitlab/ui'; +import { GlButton, GlIcon, GlTooltipDirective } from '@gitlab/ui'; import { produce } from 'immer'; import createFlash from '~/flash'; import { __, sprintf } from '~/locale'; import { todoQueries, TodoMutationTypes, todoMutations } from '~/sidebar/constants'; -import TodoButton from '~/vue_shared/components/sidebar/todo_button.vue'; +import { todoLabel } from '~/vue_shared/components/sidebar/todo_toggle//utils'; +import TodoButton from '~/vue_shared/components/sidebar/todo_toggle/todo_button.vue'; export default { components: { + GlButton, + GlIcon, TodoButton, }, directives: { GlTooltip: GlTooltipDirective, }, + inject: { + isClassicSidebar: { + default: false, + }, + }, props: { issuableId: { type: String, @@ -86,6 +94,12 @@ export default { } return TodoMutationTypes.Create; }, + collapsedButtonIcon() { + return this.hasTodo ? 'todo-done' : 'todo-add'; + }, + tootltipTitle() { + return todoLabel(this.hasTodo); + }, }, methods: { toggleTodo() { @@ -158,7 +172,24 @@ export default { :is-todo="hasTodo" :loading="isLoading" size="small" + class="hide-collapsed" @click.stop.prevent="toggleTodo" /> + <gl-button + v-if="isClassicSidebar" + category="tertiary" + type="reset" + class="sidebar-collapsed-icon sidebar-collapsed-container gl-rounded-0! gl-shadow-none!" + @click.stop.prevent="toggleTodo" + > + <gl-icon + v-gl-tooltip.left.viewport + :title="tootltipTitle" + :size="16" + :class="{ 'todo-undone': hasTodo }" + :name="collapsedButtonIcon" + :aria-label="collapsedButtonIcon" + /> + </gl-button> </div> </template> diff --git a/app/assets/javascripts/sidebar/constants.js b/app/assets/javascripts/sidebar/constants.js index 20ef783f093..08ee4379c0c 100644 --- a/app/assets/javascripts/sidebar/constants.js +++ b/app/assets/javascripts/sidebar/constants.js @@ -13,10 +13,12 @@ import issueDueDateQuery from '~/sidebar/queries/issue_due_date.query.graphql'; import issueReferenceQuery from '~/sidebar/queries/issue_reference.query.graphql'; import issueSubscribedQuery from '~/sidebar/queries/issue_subscribed.query.graphql'; import issueTimeTrackingQuery from '~/sidebar/queries/issue_time_tracking.query.graphql'; +import issueTodoQuery from '~/sidebar/queries/issue_todo.query.graphql'; import mergeRequestMilestone from '~/sidebar/queries/merge_request_milestone.query.graphql'; import mergeRequestReferenceQuery from '~/sidebar/queries/merge_request_reference.query.graphql'; import mergeRequestSubscribed from '~/sidebar/queries/merge_request_subscribed.query.graphql'; import mergeRequestTimeTrackingQuery from '~/sidebar/queries/merge_request_time_tracking.query.graphql'; +import mergeRequestTodoQuery from '~/sidebar/queries/merge_request_todo.query.graphql'; import todoCreateMutation from '~/sidebar/queries/todo_create.mutation.graphql'; import todoMarkDoneMutation from '~/sidebar/queries/todo_mark_done.mutation.graphql'; import updateEpicConfidentialMutation from '~/sidebar/queries/update_epic_confidential.mutation.graphql'; @@ -216,6 +218,12 @@ export const todoQueries = { [IssuableType.Epic]: { query: epicTodoQuery, }, + [IssuableType.Issue]: { + query: issueTodoQuery, + }, + [IssuableType.MergeRequest]: { + query: mergeRequestTodoQuery, + }, }; export const TodoMutationTypes = { diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js index e4227a1f42c..dd1b439c482 100644 --- a/app/assets/javascripts/sidebar/mount_sidebar.js +++ b/app/assets/javascripts/sidebar/mount_sidebar.js @@ -2,6 +2,8 @@ import $ from 'jquery'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createFlash from '~/flash'; +import { TYPE_ISSUE, TYPE_MERGE_REQUEST } from '~/graphql_shared/constants'; +import { convertToGraphQLId } from '~/graphql_shared/utils'; import initInviteMembersModal from '~/invite_members/init_invite_members_modal'; import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger'; import { IssuableType } from '~/issue_show/constants'; @@ -19,6 +21,7 @@ import SidebarDueDateWidget from '~/sidebar/components/date/sidebar_date_widget. import SidebarParticipantsWidget from '~/sidebar/components/participants/sidebar_participants_widget.vue'; import SidebarReferenceWidget from '~/sidebar/components/reference/sidebar_reference_widget.vue'; import SidebarDropdownWidget from '~/sidebar/components/sidebar_dropdown_widget.vue'; +import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue'; import { apolloProvider } from '~/sidebar/graphql'; import trackShowInviteMemberLink from '~/sidebar/track_invite_members'; import Translate from '../vue_shared/translate'; @@ -40,6 +43,40 @@ function getSidebarOptions(sidebarOptEl = document.querySelector('.js-sidebar-op return JSON.parse(sidebarOptEl.innerHTML); } +function mountSidebarToDoWidget() { + const el = document.querySelector('.js-issuable-todo'); + + if (!el) { + return false; + } + + const { projectPath, iid, id } = el.dataset; + + return new Vue({ + el, + apolloProvider, + components: { + SidebarTodoWidget, + }, + provide: { + isClassicSidebar: true, + }, + render: (createElement) => + createElement('sidebar-todo-widget', { + props: { + fullPath: projectPath, + issuableId: + isInIssuePage() || isInDesignPage() + ? convertToGraphQLId(TYPE_ISSUE, id) + : convertToGraphQLId(TYPE_MERGE_REQUEST, id), + issuableIid: iid, + issuableType: + isInIssuePage() || isInDesignPage() ? IssuableType.Issue : IssuableType.MergeRequest, + }, + }), + }); +} + function getSidebarAssigneeAvailabilityData() { const sidebarAssigneeEl = document.querySelectorAll('.js-sidebar-assignee-data input'); return Array.from(sidebarAssigneeEl) @@ -497,6 +534,7 @@ export function mountSidebar(mediator) { initInviteMembersModal(); initInviteMembersTrigger(); + mountSidebarToDoWidget(); if (isAssigneesWidgetShown) { mountAssigneesComponent(); } else { diff --git a/app/assets/javascripts/sidebar/queries/issue_todo.query.graphql b/app/assets/javascripts/sidebar/queries/issue_todo.query.graphql new file mode 100644 index 00000000000..783d36352fe --- /dev/null +++ b/app/assets/javascripts/sidebar/queries/issue_todo.query.graphql @@ -0,0 +1,14 @@ +query issueTodos($fullPath: ID!, $iid: String!) { + workspace: project(fullPath: $fullPath) { + __typename + issuable: issue(iid: $iid) { + __typename + id + currentUserTodos(state: pending) { + nodes { + id + } + } + } + } +} diff --git a/app/assets/javascripts/sidebar/queries/merge_request_todo.query.graphql b/app/assets/javascripts/sidebar/queries/merge_request_todo.query.graphql new file mode 100644 index 00000000000..93a1c9ea925 --- /dev/null +++ b/app/assets/javascripts/sidebar/queries/merge_request_todo.query.graphql @@ -0,0 +1,14 @@ +query mergeRequestTodos($fullPath: ID!, $iid: String!) { + workspace: project(fullPath: $fullPath) { + __typename + issuable: mergeRequest(iid: $iid) { + __typename + id + currentUserTodos(state: pending) { + nodes { + id + } + } + } + } +} diff --git a/app/assets/javascripts/vue_shared/components/sidebar/todo_button.stories.js b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js index db4d8724a0d..d2afc02233e 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/todo_button.stories.js +++ b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js @@ -4,7 +4,7 @@ import TodoButton from './todo_button.vue'; export default { component: TodoButton, - title: 'vue_shared/components/todo_button', + title: 'vue_shared/components/todo_toggle/todo_button', }; const Template = (args, { argTypes }) => ({ diff --git a/app/assets/javascripts/vue_shared/components/sidebar/todo_button.vue b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue index a3517183515..e6229cf0a93 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/todo_button.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue @@ -1,6 +1,6 @@ <script> import { GlButton } from '@gitlab/ui'; -import { __ } from '~/locale'; +import { todoLabel } from './utils'; export default { components: { @@ -15,7 +15,7 @@ export default { }, computed: { buttonLabel() { - return this.isTodo ? __('Mark as done') : __('Add a to do'); + return todoLabel(this.isTodo); }, }, methods: { diff --git a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js new file mode 100644 index 00000000000..59e72a2ffe3 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js @@ -0,0 +1,5 @@ +import { __ } from '~/locale'; + +export const todoLabel = (hasTodo) => { + return hasTodo ? __('Mark as done') : __('Add a to do'); +}; diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 1bc6dfbd84a..ee97e8af296 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -175,7 +175,8 @@ } } - .block { + .block, + .issuable-sidebar-header { @include clearfix; padding: $gl-padding 0; border-bottom: 1px solid $border-gray-normal; @@ -184,11 +185,6 @@ width: $gutter-inner-width; // -- - &.issuable-sidebar-header { - padding-top: 0; - padding-bottom: 10px; - } - &:last-child { border: 0; } @@ -273,10 +269,6 @@ padding: 0 20px; } - .issuable-sidebar-header { - padding-top: 10px; - } - &:not(.boards-sidebar):not([data-signed-in]):not([data-always-show-toggle]) { .issuable-sidebar-header { display: none; @@ -302,7 +294,6 @@ } .gutter-toggle { - margin-top: 7px; border-left: 1px solid $border-gray-normal; text-align: center; } @@ -331,20 +322,21 @@ width: $gutter-collapsed-width; padding: 0; - .block { + .block, + .issuable-sidebar-header { width: $gutter-collapsed-width - 2px; padding: 0; border-bottom: 0; overflow: hidden; + } + .block, + .gutter-toggle, + .sidebar-collapsed-container { &.with-sub-blocks .sub-block:hover, &:not(.with-sub-blocks):hover { background-color: $gray-100; } - - &.issuable-sidebar-header { - padding-top: 0; - } } .participants { diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb index d570fc06d93..88423bec915 100644 --- a/app/controllers/projects/merge_requests/diffs_controller.rb +++ b/app/controllers/projects/merge_requests/diffs_controller.rb @@ -3,6 +3,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::ApplicationController include DiffHelper include RendersNotes + include Gitlab::Cache::Helpers before_action :commit before_action :define_diff_vars @@ -40,7 +41,16 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic pagination_data: diffs.pagination_data } - render json: PaginatedDiffSerializer.new(current_user: current_user).represent(diffs, options) + if diff_options_hash[:paths].blank? && Feature.enabled?(:diffs_batch_render_cached, project, default_enabled: :yaml) + render_cached( + diffs, + with: PaginatedDiffSerializer.new(current_user: current_user), + cache_context: -> (_) { [diff_view, params[:w], params[:expanded], params[:per_page], params[:page]] }, + **options + ) + else + render json: PaginatedDiffSerializer.new(current_user: current_user).represent(diffs, options) + end end def diffs_metadata diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 2e15b3f22c2..1304bcb1c7e 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -285,7 +285,7 @@ module ApplicationHelper def page_class class_names = [] class_names << 'issue-boards-page gl-overflow-auto' if current_controller?(:boards) - class_names << 'epic-boards-page' if current_controller?(:epic_boards) + class_names << 'epic-boards-page gl-overflow-auto' if current_controller?(:epic_boards) class_names << 'environment-logs-page' if current_controller?(:logs) class_names << 'with-performance-bar' if performance_bar_enabled? class_names << system_message_class diff --git a/app/models/concerns/analytics/cycle_analytics/stage.rb b/app/models/concerns/analytics/cycle_analytics/stage.rb index 90d48aa81d0..2a0274f5706 100644 --- a/app/models/concerns/analytics/cycle_analytics/stage.rb +++ b/app/models/concerns/analytics/cycle_analytics/stage.rb @@ -50,6 +50,10 @@ module Analytics end end + def events_hash_code + Digest::SHA256.hexdigest("#{start_event.hash_code}-#{end_event.hash_code}") + end + def start_event_label_based? start_event_identifier && start_event.label_based? end diff --git a/app/models/diff_discussion.rb b/app/models/diff_discussion.rb index 6806008d676..642e93f7912 100644 --- a/app/models/diff_discussion.rb +++ b/app/models/diff_discussion.rb @@ -42,6 +42,13 @@ class DiffDiscussion < Discussion ) end + def cache_key + [ + super, + Digest::SHA1.hexdigest(position.to_json) + ].join(':') + end + private def get_params diff --git a/app/services/merge_requests/rebase_service.rb b/app/services/merge_requests/rebase_service.rb index ae8398e2335..9423194c01d 100644 --- a/app/services/merge_requests/rebase_service.rb +++ b/app/services/merge_requests/rebase_service.rb @@ -18,12 +18,6 @@ module MergeRequests end def rebase - # Ensure Gitaly isn't already running a rebase - if source_project.repository.rebase_in_progress?(merge_request.id) - log_error(exception: nil, message: 'Rebase task canceled: Another rebase is already in progress', save_message_on_model: true) - return false - end - repository.rebase(current_user, merge_request, skip_ci: @skip_ci) true diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 82f03421799..c76aa176696 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -10,19 +10,13 @@ %aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: signed_in }, issuable_type: issuable_type }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite', 'aria-label': issuable_type } .issuable-sidebar - .block.issuable-sidebar-header - - if signed_in - %span.issuable-header-text.hide-collapsed.float-left - = _('To Do') + .issuable-sidebar-header.gl-py-3 %a.gutter-toggle.float-right.js-sidebar-toggle.has-tooltip{ role: "button", href: "#", "aria-label" => _('Toggle sidebar'), title: sidebar_gutter_tooltip_text, data: { container: 'body', placement: 'left', boundary: 'viewport' } } = sidebar_gutter_toggle_icon - if signed_in - = render "shared/issuable/sidebar_todo", issuable_sidebar: issuable_sidebar + .js-issuable-todo{ data: { project_path: issuable_sidebar[:project_full_path], iid: issuable_sidebar[:iid], id: issuable_sidebar[:id] } } = form_for issuable_type, url: issuable_sidebar[:issuable_json_path], remote: true, html: { class: 'issuable-context-form inline-update js-issuable-update' } do |f| - - if signed_in - .block.todo.hide-expanded - = render "shared/issuable/sidebar_todo", issuable_sidebar: issuable_sidebar, is_collapsed: true .block.assignee.qa-assignee-block = render "shared/issuable/sidebar_assignees", issuable_sidebar: issuable_sidebar, assignees: assignees, signed_in: signed_in diff --git a/app/views/shared/issuable/_sidebar_todo.html.haml b/app/views/shared/issuable/_sidebar_todo.html.haml deleted file mode 100644 index a867421298b..00000000000 --- a/app/views/shared/issuable/_sidebar_todo.html.haml +++ /dev/null @@ -1,15 +0,0 @@ -- is_collapsed = local_assigns.fetch(:is_collapsed, false) -- has_todo = !!issuable_sidebar.dig(:current_user, :todo, :id) - -- todo_button_data = issuable_todo_button_data(issuable_sidebar, is_collapsed) -- button_title = has_todo ? todo_button_data[:mark_text] : todo_button_data[:todo_text] -- button_icon = has_todo ? todo_button_data[:mark_icon] : todo_button_data[:todo_icon] - -%button.issuable-todo-btn.js-issuable-todo{ type: 'button', - class: (is_collapsed ? 'btn-blank sidebar-collapsed-icon dont-change-state has-tooltip' : 'gl-button btn btn-default issuable-header-btn float-right'), - title: button_title, - 'aria-label' => button_title, - data: todo_button_data } - %span.issuable-todo-inner.js-issuable-todo-inner< - = is_collapsed ? button_icon : button_title - = loading_icon(css_class: is_collapsed ? '' : 'gl-ml-3') |