diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-11-21 09:06:16 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-11-21 09:06:16 +0000 |
commit | a048261403ea7e12992ccffe704f0779235712d7 (patch) | |
tree | 59254549db6d39a4da824379a7bf354e7c8e7e67 /app | |
parent | 80e5134020483299c039114e76b734436f006c66 (diff) | |
download | gitlab-ce-a048261403ea7e12992ccffe704f0779235712d7.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/autosave.js | 16 | ||||
-rw-r--r-- | app/assets/javascripts/clusters/components/crossplane_provider_stack.vue | 10 | ||||
-rw-r--r-- | app/assets/javascripts/issuable_form.js | 54 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/url_utility.js | 32 | ||||
-rw-r--r-- | app/assets/javascripts/monitoring/components/graph_group.vue | 3 | ||||
-rw-r--r-- | app/assets/javascripts/pages/projects/project.js | 24 | ||||
-rw-r--r-- | app/models/diff_note.rb | 6 | ||||
-rw-r--r-- | app/models/discussion.rb | 4 | ||||
-rw-r--r-- | app/models/note.rb | 4 | ||||
-rw-r--r-- | app/services/issuable/common_system_notes_service.rb | 26 | ||||
-rw-r--r-- | app/services/issuable_base_service.rb | 14 | ||||
-rw-r--r-- | app/services/notes/base_service.rb | 2 | ||||
-rw-r--r-- | app/views/notify/_note_email.html.haml | 2 | ||||
-rw-r--r-- | app/views/notify/_note_email.text.erb | 2 |
14 files changed, 131 insertions, 68 deletions
diff --git a/app/assets/javascripts/autosave.js b/app/assets/javascripts/autosave.js index 7652b67ae1e..07d79ea1c70 100644 --- a/app/assets/javascripts/autosave.js +++ b/app/assets/javascripts/autosave.js @@ -1,9 +1,9 @@ -/* eslint-disable no-param-reassign, no-void, consistent-return */ +/* eslint-disable no-param-reassign, consistent-return */ import AccessorUtilities from './lib/utils/accessor'; export default class Autosave { - constructor(field, key) { + constructor(field, key, fallbackKey) { this.field = field; this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe(); @@ -11,6 +11,7 @@ export default class Autosave { key = key.join('/'); } this.key = `autosave/${key}`; + this.fallbackKey = fallbackKey; this.field.data('autosave', this); this.restore(); this.field.on('input', () => this.save()); @@ -21,9 +22,12 @@ export default class Autosave { if (!this.field.length) return; const text = window.localStorage.getItem(this.key); + const fallbackText = window.localStorage.getItem(this.fallbackKey); - if ((text != null ? text.length : void 0) > 0) { + if (text) { this.field.val(text); + } else if (fallbackText) { + this.field.val(fallbackText); } this.field.trigger('input'); @@ -41,7 +45,10 @@ export default class Autosave { const text = this.field.val(); - if (this.isLocalStorageAvailable && (text != null ? text.length : void 0) > 0) { + if (this.isLocalStorageAvailable && text) { + if (this.fallbackKey) { + window.localStorage.setItem(this.fallbackKey, text); + } return window.localStorage.setItem(this.key, text); } @@ -51,6 +58,7 @@ export default class Autosave { reset() { if (!this.isLocalStorageAvailable) return; + window.localStorage.removeItem(this.fallbackKey); return window.localStorage.removeItem(this.key); } diff --git a/app/assets/javascripts/clusters/components/crossplane_provider_stack.vue b/app/assets/javascripts/clusters/components/crossplane_provider_stack.vue index 966918ae636..6b99bb09504 100644 --- a/app/assets/javascripts/clusters/components/crossplane_provider_stack.vue +++ b/app/assets/javascripts/clusters/components/crossplane_provider_stack.vue @@ -1,6 +1,5 @@ <script> -import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; -import Icon from '~/vue_shared/components/icon.vue'; +import { GlDropdown, GlDropdownItem, GlIcon } from '@gitlab/ui'; import { s__ } from '../../locale'; export default { @@ -8,7 +7,7 @@ export default { components: { GlDropdown, GlDropdownItem, - Icon, + GlIcon, }, props: { stacks: { @@ -86,8 +85,9 @@ export default { href="https://crossplane.io/docs/master/stacks-guide.html" target="_blank" rel="noopener noreferrer" - >{{ __('Crossplane') }}</a - > + >{{ __('Crossplane') }} + <gl-icon name="external-link" class="vertical-align-middle" /> + </a> </p> </div> </template> diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js index 7576d36f27d..1d0807dc15d 100644 --- a/app/assets/javascripts/issuable_form.js +++ b/app/assets/javascripts/issuable_form.js @@ -6,6 +6,36 @@ import UsersSelect from './users_select'; import ZenMode from './zen_mode'; import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select'; import { parsePikadayDate, pikadayToString } from './lib/utils/datetime_utility'; +import { queryToObject, objectToQuery } from './lib/utils/url_utility'; + +function organizeQuery(obj, isFallbackKey = false) { + const sourceBranch = 'merge_request[source_branch]'; + const targetBranch = 'merge_request[target_branch]'; + + if (isFallbackKey) { + return { + [sourceBranch]: obj[sourceBranch], + }; + } + + return { + [sourceBranch]: obj[sourceBranch], + [targetBranch]: obj[targetBranch], + }; +} + +function format(searchTerm, isFallbackKey = false) { + const queryObject = queryToObject(searchTerm); + const organizeQueryObject = organizeQuery(queryObject, isFallbackKey); + const formattedQuery = objectToQuery(organizeQueryObject); + + return formattedQuery; +} + +function getFallbackKey() { + const searchTerm = format(document.location.search, true); + return ['autosave', document.location.pathname, searchTerm].join('/'); +} export default class IssuableForm { constructor(form) { @@ -57,16 +87,20 @@ export default class IssuableForm { } initAutosave() { - this.autosave = new Autosave(this.titleField, [ - document.location.pathname, - document.location.search, - 'title', - ]); - return new Autosave(this.descriptionField, [ - document.location.pathname, - document.location.search, - 'description', - ]); + const searchTerm = format(document.location.search); + const fallbackKey = getFallbackKey(); + + this.autosave = new Autosave( + this.titleField, + [document.location.pathname, searchTerm, 'title'], + `${fallbackKey}=title`, + ); + + return new Autosave( + this.descriptionField, + [document.location.pathname, searchTerm, 'description'], + `${fallbackKey}=description`, + ); } handleSubmit() { diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js index 4be0d05a9b7..202a44d5694 100644 --- a/app/assets/javascripts/lib/utils/url_utility.js +++ b/app/assets/javascripts/lib/utils/url_utility.js @@ -181,4 +181,36 @@ export function getWebSocketUrl(path) { return `${getWebSocketProtocol()}//${joinPaths(window.location.host, path)}`; } +/** + * Convert search query into an object + * + * @param {String} query from "document.location.search" + * @returns {Object} + * + * ex: "?one=1&two=2" into {one: 1, two: 2} + */ +export function queryToObject(query) { + const removeQuestionMarkFromQuery = String(query).startsWith('?') ? query.slice(1) : query; + return removeQuestionMarkFromQuery.split('&').reduce((accumulator, curr) => { + const p = curr.split('='); + accumulator[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); + return accumulator; + }, {}); +} + +/** + * Convert search query object back into a search query + * + * @param {Object} obj that needs to be converted + * @returns {String} + * + * ex: {one: 1, two: 2} into "one=1&two=2" + * + */ +export function objectToQuery(obj) { + return Object.keys(obj) + .map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`) + .join('&'); +} + export { joinPaths }; diff --git a/app/assets/javascripts/monitoring/components/graph_group.vue b/app/assets/javascripts/monitoring/components/graph_group.vue index 3cb6ccb64b1..e01324372a7 100644 --- a/app/assets/javascripts/monitoring/components/graph_group.vue +++ b/app/assets/javascripts/monitoring/components/graph_group.vue @@ -30,9 +30,6 @@ export default { return this.collapseGroup && this.showGroup ? 'angle-down' : 'angle-right'; }, }, - created() { - this.showGroup = this.collapseGroup; - }, methods: { collapse() { this.showGroup = !this.showGroup; diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js index 01acfca158f..739ae1cea16 100644 --- a/app/assets/javascripts/pages/projects/project.js +++ b/app/assets/javascripts/pages/projects/project.js @@ -1,4 +1,4 @@ -/* eslint-disable func-names, no-var, no-return-assign */ +/* eslint-disable func-names, no-return-assign */ import $ from 'jquery'; import Cookies from 'js-cookie'; @@ -90,19 +90,19 @@ export default class Project { } static initRefSwitcher() { - var refListItem = document.createElement('li'); - var refLink = document.createElement('a'); + const refListItem = document.createElement('li'); + const refLink = document.createElement('a'); refLink.href = '#'; return $('.js-project-refs-dropdown').each(function() { - var $dropdown = $(this); - var selected = $dropdown.data('selected'); - var fieldName = $dropdown.data('fieldName'); - var shouldVisit = Boolean($dropdown.data('visit')); - var $form = $dropdown.closest('form'); - var action = $form.attr('action'); - var linkTarget = mergeUrlParams(serializeForm($form[0]), action); + const $dropdown = $(this); + const selected = $dropdown.data('selected'); + const fieldName = $dropdown.data('fieldName'); + const shouldVisit = Boolean($dropdown.data('visit')); + const $form = $dropdown.closest('form'); + const action = $form.attr('action'); + const linkTarget = mergeUrlParams(serializeForm($form[0]), action); return $dropdown.glDropdown({ data(term, callback) { @@ -123,9 +123,9 @@ export default class Project { inputFieldName: $dropdown.data('inputFieldName'), fieldName, renderRow(ref) { - var li = refListItem.cloneNode(false); + const li = refListItem.cloneNode(false); - var link = refLink.cloneNode(false); + const link = refLink.cloneNode(false); if (ref === selected) { link.className = 'is-active'; diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb index 65e87bb08a7..0623ba746c2 100644 --- a/app/models/diff_note.rb +++ b/app/models/diff_note.rb @@ -88,10 +88,6 @@ class DiffNote < Note line&.suggestible? end - def discussion_first_note? - self == discussion.first_note - end - def banzai_render_context(field) super.merge(suggestions_filter_enabled: true) end @@ -108,7 +104,7 @@ class DiffNote < Note end def should_create_diff_file? - on_text? && note_diff_file.nil? && discussion_first_note? + on_text? && note_diff_file.nil? && start_of_discussion? end def fetch_diff_file diff --git a/app/models/discussion.rb b/app/models/discussion.rb index b8525f7b135..d0a7db39a30 100644 --- a/app/models/discussion.rb +++ b/app/models/discussion.rb @@ -139,10 +139,6 @@ class Discussion false end - def new_discussion? - notes.length == 1 - end - def last_note @last_note ||= notes.last end diff --git a/app/models/note.rb b/app/models/note.rb index ce60413b8a0..1996fd4bff5 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -409,6 +409,10 @@ class Note < ApplicationRecord full_discussion || to_discussion end + def start_of_discussion? + discussion.first_note == self + end + def part_of_discussion? !to_discussion.individual_note? end diff --git a/app/services/issuable/common_system_notes_service.rb b/app/services/issuable/common_system_notes_service.rb index a170a4dcae2..846b881e819 100644 --- a/app/services/issuable/common_system_notes_service.rb +++ b/app/services/issuable/common_system_notes_service.rb @@ -7,20 +7,24 @@ module Issuable def execute(issuable, old_labels: [], is_update: true) @issuable = issuable - if is_update - if issuable.previous_changes.include?('title') - create_title_change_note(issuable.previous_changes['title'].first) + # We disable touch so that created system notes do not update + # the noteable's updated_at field + ActiveRecord::Base.no_touching do + if is_update + if issuable.previous_changes.include?('title') + create_title_change_note(issuable.previous_changes['title'].first) + end + + handle_description_change_note + + handle_time_tracking_note if issuable.is_a?(TimeTrackable) + create_discussion_lock_note if issuable.previous_changes.include?('discussion_locked') end - handle_description_change_note - - handle_time_tracking_note if issuable.is_a?(TimeTrackable) - create_discussion_lock_note if issuable.previous_changes.include?('discussion_locked') + create_due_date_note if issuable.previous_changes.include?('due_date') + create_milestone_note if issuable.previous_changes.include?('milestone_id') + create_labels_note(old_labels) if old_labels && issuable.labels != old_labels end - - create_due_date_note if issuable.previous_changes.include?('due_date') - create_milestone_note if issuable.previous_changes.include?('milestone_id') - create_labels_note(old_labels) if old_labels && issuable.labels != old_labels end private diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index 200eca0e43c..bb65a8f402d 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -164,9 +164,7 @@ class IssuableBaseService < BaseService before_create(issuable) if issuable.save - ActiveRecord::Base.no_touching do - Issuable::CommonSystemNotesService.new(project, current_user).execute(issuable, is_update: false) - end + Issuable::CommonSystemNotesService.new(project, current_user).execute(issuable, is_update: false) after_create(issuable) execute_hooks(issuable) @@ -227,10 +225,7 @@ class IssuableBaseService < BaseService ensure_milestone_available(issuable) if issuable.with_transaction_returning_status { issuable.save(touch: should_touch) } - # We do not touch as it will affect a update on updated_at field - ActiveRecord::Base.no_touching do - Issuable::CommonSystemNotesService.new(project, current_user).execute(issuable, old_labels: old_associations[:labels]) - end + Issuable::CommonSystemNotesService.new(project, current_user).execute(issuable, old_labels: old_associations[:labels]) handle_changes(issuable, old_associations: old_associations) @@ -264,10 +259,7 @@ class IssuableBaseService < BaseService before_update(issuable, skip_spam_check: true) if issuable.with_transaction_returning_status { issuable.save } - # We do not touch as it will affect a update on updated_at field - ActiveRecord::Base.no_touching do - Issuable::CommonSystemNotesService.new(project, current_user).execute(issuable, old_labels: nil) - end + Issuable::CommonSystemNotesService.new(project, current_user).execute(issuable, old_labels: nil) handle_task_changes(issuable) invalidate_cache_counts(issuable, users: issuable.assignees.to_a) diff --git a/app/services/notes/base_service.rb b/app/services/notes/base_service.rb index b4d04c47cc0..87f7cb0e8ac 100644 --- a/app/services/notes/base_service.rb +++ b/app/services/notes/base_service.rb @@ -4,7 +4,7 @@ module Notes class BaseService < ::BaseService def clear_noteable_diffs_cache(note) if note.is_a?(DiffNote) && - note.discussion_first_note? && + note.start_of_discussion? && note.position.unfolded_diff?(project.repository) note.noteable.diffs.clear_cache end diff --git a/app/views/notify/_note_email.html.haml b/app/views/notify/_note_email.html.haml index dc5529b489b..c558358725c 100644 --- a/app/views/notify/_note_email.html.haml +++ b/app/views/notify/_note_email.html.haml @@ -11,7 +11,7 @@ - if discussion.nil? commented - else - - if discussion.new_discussion? + - if note.start_of_discussion? started a new - else commented on a diff --git a/app/views/notify/_note_email.text.erb b/app/views/notify/_note_email.text.erb index a25daad8458..8e2f7e6f76e 100644 --- a/app/views/notify/_note_email.text.erb +++ b/app/views/notify/_note_email.text.erb @@ -7,7 +7,7 @@ <% if discussion.nil? -%> <%= 'commented' -%>: <% else -%> -<% if discussion.new_discussion? -%> +<% if note.start_of_discussion? -%> <%= 'started a new discussion' -%> <% else -%> <%= 'commented on a discussion' -%> |