diff --git a/.gitignore b/.gitignore index 65befc20963..6bec6d2596f 100644 --- a/.gitignore +++ b/.gitignore @@ -82,3 +82,4 @@ jsdoc/ **/tmp/rubocop_cache/** .overcommit.yml .projections.json +/qa/.rakeTasks diff --git a/Gemfile b/Gemfile index ffe456cae08..d3689e4a211 100644 --- a/Gemfile +++ b/Gemfile @@ -159,6 +159,7 @@ gem 'icalendar' # Diffs gem 'diffy', '~> 3.1.0' +gem 'diff_match_patch', '~> 0.1.0' # Application server gem 'rack', '~> 2.0.7' diff --git a/Gemfile.lock b/Gemfile.lock index 7582eb28e49..5951eb4bf71 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -224,6 +224,7 @@ GEM railties rotp (~> 2.0) diff-lcs (1.3) + diff_match_patch (0.1.0) diffy (3.1.0) discordrb-webhooks-blackst0ne (3.3.0) rest-client (~> 2.0) @@ -1133,6 +1134,7 @@ DEPENDENCIES device_detector devise (~> 4.6) devise-two-factor (~> 3.0.0) + diff_match_patch (~> 0.1.0) diffy (~> 3.1.0) discordrb-webhooks-blackst0ne (~> 3.3) doorkeeper (~> 4.3) diff --git a/app/assets/javascripts/helpers/monitor_helper.js b/app/assets/javascripts/helpers/monitor_helper.js index 900f0cf5bb8..d172aa8a444 100644 --- a/app/assets/javascripts/helpers/monitor_helper.js +++ b/app/assets/javascripts/helpers/monitor_helper.js @@ -1,11 +1,9 @@ -/* eslint-disable import/prefer-default-export */ -import _ from 'underscore'; - /** * @param {Array} queryResults - Array of Result objects * @param {Object} defaultConfig - Default chart config values (e.g. lineStyle, name) * @returns {Array} The formatted values */ +// eslint-disable-next-line import/prefer-default-export export const makeDataSeries = (queryResults, defaultConfig) => queryResults .map(result => { @@ -19,10 +17,13 @@ export const makeDataSeries = (queryResults, defaultConfig) => if (name) { series.name = `${defaultConfig.name}: ${name}`; } else { - const template = _.template(defaultConfig.name, { - interpolate: /\{\{(.+?)\}\}/g, + series.name = defaultConfig.name; + Object.keys(result.metric).forEach(templateVar => { + const value = result.metric[templateVar]; + const regex = new RegExp(`{{\\s*${templateVar}\\s*}}`, 'g'); + + series.name = series.name.replace(regex, value); }); - series.name = template(result.metric); } return { ...defaultConfig, ...series }; diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js index a9e086fade8..9136a47d542 100644 --- a/app/assets/javascripts/issue.js +++ b/app/assets/javascripts/issue.js @@ -1,4 +1,4 @@ -/* eslint-disable no-var, one-var, consistent-return */ +/* eslint-disable consistent-return */ import $ from 'jquery'; import axios from './lib/utils/axios_utils'; @@ -91,18 +91,17 @@ export default class Issue { 'click', '.js-issuable-actions a.btn-close, .js-issuable-actions a.btn-reopen', e => { - var $button, shouldSubmit, url; e.preventDefault(); e.stopImmediatePropagation(); - $button = $(e.currentTarget); - shouldSubmit = $button.hasClass('btn-comment'); + const $button = $(e.currentTarget); + const shouldSubmit = $button.hasClass('btn-comment'); if (shouldSubmit) { Issue.submitNoteForm($button.closest('form')); } this.disableCloseReopenButton($button); - url = $button.attr('href'); + const url = $button.attr('href'); return axios .put(url) .then(({ data }) => { @@ -139,16 +138,14 @@ export default class Issue { } static submitNoteForm(form) { - var noteText; - noteText = form.find('textarea.js-note-text').val(); + const noteText = form.find('textarea.js-note-text').val(); if (noteText && noteText.trim().length > 0) { return form.submit(); } } static initRelatedBranches() { - var $container; - $container = $('#related-branches'); + const $container = $('#related-branches'); return axios .get($container.data('url')) .then(({ data }) => { diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index 72de3b5d726..6abf723be9a 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -1,4 +1,4 @@ -/* eslint-disable no-useless-return, func-names, no-var, no-underscore-dangle, one-var, no-new, consistent-return, no-shadow, no-param-reassign, vars-on-top, no-lonely-if, no-else-return, dot-notation, no-empty */ +/* eslint-disable no-useless-return, func-names, no-underscore-dangle, no-new, consistent-return, no-shadow, no-param-reassign, no-lonely-if, no-else-return, dot-notation, no-empty */ /* global Issuable */ /* global ListLabel */ @@ -15,63 +15,39 @@ import { isScopedLabel } from '~/lib/utils/common_utils'; export default class LabelsSelect { constructor(els, options = {}) { - var _this, $els; - _this = this; + const _this = this; - $els = $(els); + let $els = $(els); if (!els) { $els = $('.js-label-select'); } $els.each((i, dropdown) => { - var $block, - $dropdown, - $form, - $loading, - $selectbox, - $sidebarCollapsedValue, - $value, - $dropdownMenu, - abilityName, - defaultLabel, - issueUpdateURL, - labelUrl, - namespacePath, - projectPath, - saveLabelData, - selectedLabel, - showAny, - showNo, - $sidebarLabelTooltip, - initialSelected, - fieldName, - showMenuAbove, - $dropdownContainer; - $dropdown = $(dropdown); - $dropdownContainer = $dropdown.closest('.labels-filter'); - namespacePath = $dropdown.data('namespacePath'); - projectPath = $dropdown.data('projectPath'); - issueUpdateURL = $dropdown.data('issueUpdate'); - selectedLabel = $dropdown.data('selected'); + const $dropdown = $(dropdown); + const $dropdownContainer = $dropdown.closest('.labels-filter'); + const namespacePath = $dropdown.data('namespacePath'); + const projectPath = $dropdown.data('projectPath'); + const issueUpdateURL = $dropdown.data('issueUpdate'); + let selectedLabel = $dropdown.data('selected'); if (selectedLabel != null && !$dropdown.hasClass('js-multiselect')) { selectedLabel = selectedLabel.split(','); } - showNo = $dropdown.data('showNo'); - showAny = $dropdown.data('showAny'); - showMenuAbove = $dropdown.data('showMenuAbove'); - defaultLabel = $dropdown.data('defaultLabel') || __('Label'); - abilityName = $dropdown.data('abilityName'); - $selectbox = $dropdown.closest('.selectbox'); - $block = $selectbox.closest('.block'); - $form = $dropdown.closest('form, .js-issuable-update'); - $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span'); - $sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip'); - $value = $block.find('.value'); - $dropdownMenu = $dropdown.parent().find('.dropdown-menu'); - $loading = $block.find('.block-loading').fadeOut(); - fieldName = $dropdown.data('fieldName'); - initialSelected = $selectbox + const showNo = $dropdown.data('showNo'); + const showAny = $dropdown.data('showAny'); + const showMenuAbove = $dropdown.data('showMenuAbove'); + const defaultLabel = $dropdown.data('defaultLabel') || __('Label'); + const abilityName = $dropdown.data('abilityName'); + const $selectbox = $dropdown.closest('.selectbox'); + const $block = $selectbox.closest('.block'); + const $form = $dropdown.closest('form, .js-issuable-update'); + const $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span'); + const $sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip'); + const $value = $block.find('.value'); + const $dropdownMenu = $dropdown.parent().find('.dropdown-menu'); + const $loading = $block.find('.block-loading').fadeOut(); + const fieldName = $dropdown.data('fieldName'); + let initialSelected = $selectbox .find(`input[name="${$dropdown.data('fieldName')}"]`) .map(function() { return this.value; @@ -90,9 +66,8 @@ export default class LabelsSelect { ); } - saveLabelData = function() { - var data, selected; - selected = $dropdown + const saveLabelData = function() { + const selected = $dropdown .closest('.selectbox') .find(`input[name='${fieldName}']`) .map(function() { @@ -103,7 +78,7 @@ export default class LabelsSelect { if (_.isEqual(initialSelected, selected)) return; initialSelected = selected; - data = {}; + const data = {}; data[abilityName] = {}; data[abilityName].label_ids = selected; if (!selected.length) { @@ -114,12 +89,13 @@ export default class LabelsSelect { axios .put(issueUpdateURL, data) .then(({ data }) => { - var labelCount, template, labelTooltipTitle, labelTitles; + let labelTooltipTitle; + let template; $loading.fadeOut(); $dropdown.trigger('loaded.gl.dropdown'); $selectbox.hide(); data.issueUpdateURL = issueUpdateURL; - labelCount = 0; + let labelCount = 0; if (data.labels.length && issueUpdateURL) { template = LabelsSelect.getLabelTemplate({ labels: _.sortBy(data.labels, 'title'), @@ -174,7 +150,7 @@ export default class LabelsSelect { $sidebarCollapsedValue.text(labelCount); if (data.labels.length) { - labelTitles = data.labels.map(label => label.title); + let labelTitles = data.labels.map(label => label.title); if (labelTitles.length > 5) { labelTitles = labelTitles.slice(0, 5); @@ -199,13 +175,13 @@ export default class LabelsSelect { $dropdown.glDropdown({ showMenuAbove, data(term, callback) { - labelUrl = $dropdown.attr('data-labels'); + const labelUrl = $dropdown.attr('data-labels'); axios .get(labelUrl) .then(res => { let { data } = res; if ($dropdown.hasClass('js-extra-options')) { - var extraData = []; + const extraData = []; if (showNo) { extraData.unshift({ id: 0, @@ -232,22 +208,14 @@ export default class LabelsSelect { .catch(() => flash(__('Error fetching labels.'))); }, renderRow(label) { - var linkEl, - listItemEl, - colorEl, - indeterminate, - removesAll, - selectedClass, - i, - marked, - dropdownValue; + let colorEl; - selectedClass = []; - removesAll = label.id <= 0 || label.id == null; + const selectedClass = []; + const removesAll = label.id <= 0 || label.id == null; if ($dropdown.hasClass('js-filter-bulk-update')) { - indeterminate = $dropdown.data('indeterminate') || []; - marked = $dropdown.data('marked') || []; + const indeterminate = $dropdown.data('indeterminate') || []; + const marked = $dropdown.data('marked') || []; if (indeterminate.indexOf(label.id) !== -1) { selectedClass.push('is-indeterminate'); @@ -255,7 +223,7 @@ export default class LabelsSelect { if (marked.indexOf(label.id) !== -1) { // Remove is-indeterminate class if the item will be marked as active - i = selectedClass.indexOf('is-indeterminate'); + const i = selectedClass.indexOf('is-indeterminate'); if (i !== -1) { selectedClass.splice(i, 1); } @@ -263,7 +231,7 @@ export default class LabelsSelect { } } else { if (this.id(label)) { - dropdownValue = this.id(label) + const dropdownValue = this.id(label) .toString() .replace(/'/g, "\\'"); @@ -287,7 +255,7 @@ export default class LabelsSelect { colorEl = ''; } - linkEl = document.createElement('a'); + const linkEl = document.createElement('a'); linkEl.href = '#'; // We need to identify which items are actually labels @@ -300,7 +268,7 @@ export default class LabelsSelect { linkEl.className = selectedClass.join(' '); linkEl.innerHTML = `${colorEl} ${_.escape(label.title)}`; - listItemEl = document.createElement('li'); + const listItemEl = document.createElement('li'); listItemEl.appendChild(linkEl); return listItemEl; @@ -312,12 +280,12 @@ export default class LabelsSelect { filterable: true, selected: $dropdown.data('selected') || [], toggleLabel(selected, el) { - var $dropdownParent = $dropdown.parent(); - var $dropdownInputField = $dropdownParent.find('.dropdown-input-field'); - var isSelected = el !== null ? el.hasClass('is-active') : false; + const $dropdownParent = $dropdown.parent(); + const $dropdownInputField = $dropdownParent.find('.dropdown-input-field'); + const isSelected = el !== null ? el.hasClass('is-active') : false; - var title = selected ? selected.title : null; - var selectedLabels = this.selected; + const title = selected ? selected.title : null; + const selectedLabels = this.selected; if ($dropdownInputField.length && $dropdownInputField.val().length) { $dropdownParent.find('.dropdown-input-clear').trigger('click'); @@ -329,7 +297,7 @@ export default class LabelsSelect { } else if (isSelected) { this.selected.push(title); } else if (!isSelected && title) { - var index = this.selected.indexOf(title); + const index = this.selected.indexOf(title); this.selected.splice(index, 1); } @@ -359,10 +327,9 @@ export default class LabelsSelect { } }, hidden() { - var isIssueIndex, isMRIndex, page; - page = $('body').attr('data-page'); - isIssueIndex = page === 'projects:issues:index'; - isMRIndex = page === 'projects:merge_requests:index'; + const page = $('body').attr('data-page'); + const isIssueIndex = page === 'projects:issues:index'; + const isMRIndex = page === 'projects:merge_requests:index'; $selectbox.hide(); // display:block overrides the hide-collapse rule $value.removeAttr('style'); @@ -393,14 +360,13 @@ export default class LabelsSelect { const { $el, e, isMarking } = clickEvent; const label = clickEvent.selectedObj; - var isIssueIndex, isMRIndex, page, boardsModel; - var fadeOutLoader = () => { + const fadeOutLoader = () => { $loading.fadeOut(); }; - page = $('body').attr('data-page'); - isIssueIndex = page === 'projects:issues:index'; - isMRIndex = page === 'projects:merge_requests:index'; + const page = $('body').attr('data-page'); + const isIssueIndex = page === 'projects:issues:index'; + const isMRIndex = page === 'projects:merge_requests:index'; if ($dropdown.parent().find('.is-active:not(.dropdown-clear-active)').length) { $dropdown @@ -419,6 +385,7 @@ export default class LabelsSelect { return; } + let boardsModel; if ($dropdown.closest('.add-issues-modal').length) { boardsModel = ModalStore.store.filter; } @@ -450,7 +417,7 @@ export default class LabelsSelect { }), ); } else { - var { labels } = boardsStore.detail.issue; + let { labels } = boardsStore.detail.issue; labels = labels.filter(selectedLabel => selectedLabel.id !== label.id); boardsStore.detail.issue.labels = labels; } @@ -578,16 +545,14 @@ export default class LabelsSelect { } // eslint-disable-next-line class-methods-use-this setDropdownData($dropdown, isMarking, value) { - var i, markedIds, unmarkedIds, indeterminateIds; - - markedIds = $dropdown.data('marked') || []; - unmarkedIds = $dropdown.data('unmarked') || []; - indeterminateIds = $dropdown.data('indeterminate') || []; + const markedIds = $dropdown.data('marked') || []; + const unmarkedIds = $dropdown.data('unmarked') || []; + const indeterminateIds = $dropdown.data('indeterminate') || []; if (isMarking) { markedIds.push(value); - i = indeterminateIds.indexOf(value); + let i = indeterminateIds.indexOf(value); if (i > -1) { indeterminateIds.splice(i, 1); } @@ -598,7 +563,7 @@ export default class LabelsSelect { } } else { // If marked item (not common) is unmarked - i = markedIds.indexOf(value); + const i = markedIds.indexOf(value); if (i > -1) { markedIds.splice(i, 1); } diff --git a/app/assets/javascripts/monitoring/stores/actions.js b/app/assets/javascripts/monitoring/stores/actions.js index a670becf91a..6a8e3cc82f5 100644 --- a/app/assets/javascripts/monitoring/stores/actions.js +++ b/app/assets/javascripts/monitoring/stores/actions.js @@ -84,7 +84,10 @@ export const fetchDashboard = ({ state, dispatch }, params) => { return backOffRequest(() => axios.get(state.dashboardEndpoint, { params })) .then(resp => resp.data) .then(response => { - dispatch('receiveMetricsDashboardSuccess', { response, params }); + dispatch('receiveMetricsDashboardSuccess', { + response, + params, + }); }) .catch(error => { dispatch('receiveMetricsDashboardFailure', error); diff --git a/app/assets/javascripts/monitoring/stores/mutations.js b/app/assets/javascripts/monitoring/stores/mutations.js index 26d4c56ca78..696af5aed75 100644 --- a/app/assets/javascripts/monitoring/stores/mutations.js +++ b/app/assets/javascripts/monitoring/stores/mutations.js @@ -94,7 +94,7 @@ export default { state.emptyState = 'noData'; }, [types.SET_ALL_DASHBOARDS](state, dashboards) { - state.allDashboards = dashboards; + state.allDashboards = dashboards || []; }, [types.SET_SHOW_ERROR_BANNER](state, enabled) { state.showErrorBanner = enabled; diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue index 3158e086f6c..e4f09492d9c 100644 --- a/app/assets/javascripts/notes/components/note_header.vue +++ b/app/assets/javascripts/notes/components/note_header.vue @@ -101,6 +101,7 @@ export default { + export const removeConvertedDiscussion = ({ commit }, noteId) => commit(types.REMOVE_CONVERTED_DISCUSSION, noteId); +export const fetchDescriptionVersion = (_, { endpoint, startingVersion }) => { + let requestUrl = endpoint; + + if (startingVersion) { + requestUrl = mergeUrlParams({ start_version_id: startingVersion }, requestUrl); + } + + return axios + .get(requestUrl) + .then(res => res.data) + .catch(() => { + Flash(__('Something went wrong while fetching description changes. Please try again.')); + }); +}; + // prevent babel-plugin-rewire from generating an invalid default during karma tests export default () => {}; diff --git a/app/assets/javascripts/notes/stores/collapse_utils.js b/app/assets/javascripts/notes/stores/collapse_utils.js index bee6d4f0329..3cdcc7a05b8 100644 --- a/app/assets/javascripts/notes/stores/collapse_utils.js +++ b/app/assets/javascripts/notes/stores/collapse_utils.js @@ -1,34 +1,9 @@ -import { n__, s__, sprintf } from '~/locale'; import { DESCRIPTION_TYPE } from '../constants'; -/** - * Changes the description from a note, returns 'changed the description n number of times' - */ -export const changeDescriptionNote = (note, descriptionChangedTimes, timeDifferenceMinutes) => { - const descriptionNote = Object.assign({}, note); - - descriptionNote.note_html = sprintf( - s__(`MergeRequest| - %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}`), - { - paragraphStart: '

', - paragraphEnd: '

', - descriptionChangedTimes, - timeDifferenceMinutes: n__('within %d minute ', 'within %d minutes ', timeDifferenceMinutes), - }, - false, - ); - - descriptionNote.times_updated = descriptionChangedTimes; - - return descriptionNote; -}; - /** * Checks the time difference between two notes from their 'created_at' dates * returns an integer */ - export const getTimeDifferenceMinutes = (noteBeggining, noteEnd) => { const descriptionNoteBegin = new Date(noteBeggining.created_at); const descriptionNoteEnd = new Date(noteEnd.created_at); @@ -57,7 +32,6 @@ export const isDescriptionSystemNote = note => note.system && note.note === DESC export const collapseSystemNotes = notes => { let lastDescriptionSystemNote = null; let lastDescriptionSystemNoteIndex = -1; - let descriptionChangedTimes = 1; return notes.slice(0).reduce((acc, currentNote) => { const note = currentNote.notes[0]; @@ -70,32 +44,24 @@ export const collapseSystemNotes = notes => { } else if (lastDescriptionSystemNote) { const timeDifferenceMinutes = getTimeDifferenceMinutes(lastDescriptionSystemNote, note); - // are they less than 10 minutes apart? - if (timeDifferenceMinutes > 10) { - // reset counter - descriptionChangedTimes = 1; + // are they less than 10 minutes apart from the same user? + if (timeDifferenceMinutes > 10 || note.author.id !== lastDescriptionSystemNote.author.id) { // update the previous system note lastDescriptionSystemNote = note; lastDescriptionSystemNoteIndex = acc.length; } else { - // increase counter - descriptionChangedTimes += 1; + // set the first version to fetch grouped system note versions + note.start_description_version_id = lastDescriptionSystemNote.description_version_id; // delete the previous one acc.splice(lastDescriptionSystemNoteIndex, 1); - // replace the text of the current system note with the collapsed note. - currentNote.notes.splice( - 0, - 1, - changeDescriptionNote(note, descriptionChangedTimes, timeDifferenceMinutes), - ); - // update the previous system note index lastDescriptionSystemNoteIndex = acc.length; } } } + acc.push(currentNote); return acc; }, []); diff --git a/app/assets/javascripts/registry/components/collapsible_container.vue b/app/assets/javascripts/registry/components/collapsible_container.vue index 95f8270b5d0..5a6f9370564 100644 --- a/app/assets/javascripts/registry/components/collapsible_container.vue +++ b/app/assets/javascripts/registry/components/collapsible_container.vue @@ -8,12 +8,13 @@ import { GlModalDirective, GlEmptyState, } from '@gitlab/ui'; -import createFlash from '../../flash'; -import ClipboardButton from '../../vue_shared/components/clipboard_button.vue'; -import Icon from '../../vue_shared/components/icon.vue'; +import createFlash from '~/flash'; +import Tracking from '~/tracking'; +import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; +import Icon from '~/vue_shared/components/icon.vue'; import TableRegistry from './table_registry.vue'; -import { errorMessages, errorMessagesTypes } from '../constants'; -import { __ } from '../../locale'; +import { DELETE_REPO_ERROR_MESSAGE } from '../constants'; +import { __ } from '~/locale'; export default { name: 'CollapsibeContainerRegisty', @@ -30,6 +31,7 @@ export default { GlTooltip: GlTooltipDirective, GlModal: GlModalDirective, }, + mixins: [Tracking.mixin({})], props: { repo: { type: Object, @@ -40,6 +42,10 @@ export default { return { isOpen: false, modalId: `confirm-repo-deletion-modal-${this.repo.id}`, + tracking: { + category: document.body.dataset.page, + label: 'registry_repository_delete', + }, }; }, computed: { @@ -61,15 +67,13 @@ export default { } }, handleDeleteRepository() { + this.track('confirm_delete', {}); return this.deleteItem(this.repo) .then(() => { createFlash(__('This container registry has been scheduled for deletion.'), 'notice'); this.fetchRepos(); }) - .catch(() => this.showError(errorMessagesTypes.DELETE_REPO)); - }, - showError(message) { - createFlash(errorMessages[message]); + .catch(() => createFlash(DELETE_REPO_ERROR_MESSAGE)); }, }, }; @@ -97,10 +101,9 @@ export default { v-gl-modal="modalId" :title="s__('ContainerRegistry|Remove repository')" :aria-label="s__('ContainerRegistry|Remove repository')" - data-track-event="click_button" - data-track-label="registry_repository_delete" class="js-remove-repo btn-inverted" variant="danger" + @click="track('click_button', {})" > @@ -124,7 +127,13 @@ export default { class="mx-auto my-0" /> - +

- +

diff --git a/app/assets/javascripts/registry/constants.js b/app/assets/javascripts/registry/constants.js index 712b0fade3d..db798fb88ac 100644 --- a/app/assets/javascripts/registry/constants.js +++ b/app/assets/javascripts/registry/constants.js @@ -1,15 +1,8 @@ import { __ } from '../locale'; -export const errorMessagesTypes = { - FETCH_REGISTRY: 'FETCH_REGISTRY', - FETCH_REPOS: 'FETCH_REPOS', - DELETE_REPO: 'DELETE_REPO', - DELETE_REGISTRY: 'DELETE_REGISTRY', -}; - -export const errorMessages = { - [errorMessagesTypes.FETCH_REGISTRY]: __('Something went wrong while fetching the registry list.'), - [errorMessagesTypes.FETCH_REPOS]: __('Something went wrong while fetching the projects.'), - [errorMessagesTypes.DELETE_REPO]: __('Something went wrong on our end.'), - [errorMessagesTypes.DELETE_REGISTRY]: __('Something went wrong on our end.'), -}; +export const FETCH_REGISTRY_ERROR_MESSAGE = __( + 'Something went wrong while fetching the registry list.', +); +export const FETCH_REPOS_ERROR_MESSAGE = __('Something went wrong while fetching the projects.'); +export const DELETE_REPO_ERROR_MESSAGE = __('Something went wrong on our end.'); +export const DELETE_REGISTRY_ERROR_MESSAGE = __('Something went wrong on our end.'); diff --git a/app/assets/javascripts/registry/stores/actions.js b/app/assets/javascripts/registry/stores/actions.js index 2121f518a7a..6afba618486 100644 --- a/app/assets/javascripts/registry/stores/actions.js +++ b/app/assets/javascripts/registry/stores/actions.js @@ -1,7 +1,7 @@ import axios from '~/lib/utils/axios_utils'; import createFlash from '~/flash'; import * as types from './mutation_types'; -import { errorMessages, errorMessagesTypes } from '../constants'; +import { FETCH_REPOS_ERROR_MESSAGE, FETCH_REGISTRY_ERROR_MESSAGE } from '../constants'; export const fetchRepos = ({ commit, state }) => { commit(types.TOGGLE_MAIN_LOADING); @@ -14,7 +14,7 @@ export const fetchRepos = ({ commit, state }) => { }) .catch(() => { commit(types.TOGGLE_MAIN_LOADING); - createFlash(errorMessages[errorMessagesTypes.FETCH_REPOS]); + createFlash(FETCH_REPOS_ERROR_MESSAGE); }); }; @@ -30,7 +30,7 @@ export const fetchList = ({ commit }, { repo, page }) => { }) .catch(() => { commit(types.TOGGLE_REGISTRY_LIST_LOADING, repo); - createFlash(errorMessages[errorMessagesTypes.FETCH_REGISTRY]); + createFlash(FETCH_REGISTRY_ERROR_MESSAGE); }); }; diff --git a/app/assets/javascripts/registry/stores/mutations.js b/app/assets/javascripts/registry/stores/mutations.js index ea5925247d1..419de848883 100644 --- a/app/assets/javascripts/registry/stores/mutations.js +++ b/app/assets/javascripts/registry/stores/mutations.js @@ -1,33 +1,31 @@ import * as types from './mutation_types'; -import { parseIntPagination, normalizeHeaders } from '../../lib/utils/common_utils'; +import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils'; export default { [types.SET_MAIN_ENDPOINT](state, endpoint) { - Object.assign(state, { endpoint }); + state.endpoint = endpoint; }, [types.SET_IS_DELETE_DISABLED](state, isDeleteDisabled) { - Object.assign(state, { isDeleteDisabled }); + state.isDeleteDisabled = isDeleteDisabled; }, [types.SET_REPOS_LIST](state, list) { - Object.assign(state, { - repos: list.map(el => ({ - canDelete: Boolean(el.destroy_path), - destroyPath: el.destroy_path, - id: el.id, - isLoading: false, - list: [], - location: el.location, - name: el.path, - tagsPath: el.tags_path, - projectId: el.project_id, - })), - }); + state.repos = list.map(el => ({ + canDelete: Boolean(el.destroy_path), + destroyPath: el.destroy_path, + id: el.id, + isLoading: false, + list: [], + location: el.location, + name: el.path, + tagsPath: el.tags_path, + projectId: el.project_id, + })); }, [types.TOGGLE_MAIN_LOADING](state) { - Object.assign(state, { isLoading: !state.isLoading }); + state.isLoading = !state.isLoading; }, [types.SET_REGISTRY_LIST](state, { repo, resp, headers }) { diff --git a/app/assets/javascripts/tree.js b/app/assets/javascripts/tree.js index 69b3d20914a..a530c4a99e2 100644 --- a/app/assets/javascripts/tree.js +++ b/app/assets/javascripts/tree.js @@ -1,4 +1,4 @@ -/* eslint-disable func-names, consistent-return, no-var, one-var, no-else-return, class-methods-use-this */ +/* eslint-disable func-names, consistent-return, one-var, no-else-return, class-methods-use-this */ import $ from 'jquery'; import { visitUrl } from './lib/utils/url_utility'; @@ -9,9 +9,8 @@ export default class TreeView { // Code browser tree slider // Make the entire tree-item row clickable, but not if clicking another link (like a commit message) $('.tree-content-holder .tree-item').on('click', function(e) { - var $clickedEl, path; - $clickedEl = $(e.target); - path = $('.tree-item-file-name a', this).attr('href'); + const $clickedEl = $(e.target); + const path = $('.tree-item-file-name a', this).attr('href'); if (!$clickedEl.is('a') && !$clickedEl.is('.str-truncated')) { if (e.metaKey || e.which === 2) { e.preventDefault(); @@ -26,11 +25,10 @@ export default class TreeView { } initKeyNav() { - var li, liSelected; - li = $('tr.tree-item'); - liSelected = null; + const li = $('tr.tree-item'); + let liSelected = null; return $('body').keydown(e => { - var next, path; + let next, path; if ($('input:focus').length > 0 && (e.which === 38 || e.which === 40)) { return false; } diff --git a/app/assets/javascripts/vue_shared/components/notes/system_note.vue b/app/assets/javascripts/vue_shared/components/notes/system_note.vue index d6dfe9eded8..f8e010c4f42 100644 --- a/app/assets/javascripts/vue_shared/components/notes/system_note.vue +++ b/app/assets/javascripts/vue_shared/components/notes/system_note.vue @@ -17,9 +17,11 @@ * /> */ import $ from 'jquery'; -import { mapGetters } from 'vuex'; +import { mapGetters, mapActions } from 'vuex'; +import { GlSkeletonLoading } from '@gitlab/ui'; import noteHeader from '~/notes/components/note_header.vue'; import Icon from '~/vue_shared/components/icon.vue'; +import descriptionVersionHistoryMixin from 'ee_else_ce/notes/mixins/description_version_history'; import TimelineEntryItem from './timeline_entry_item.vue'; import { spriteIcon } from '../../../lib/utils/common_utils'; import initMRPopovers from '~/mr_popover/'; @@ -32,7 +34,9 @@ export default { Icon, noteHeader, TimelineEntryItem, + GlSkeletonLoading, }, + mixins: [descriptionVersionHistoryMixin], props: { note: { type: Object, @@ -75,13 +79,16 @@ export default { mounted() { initMRPopovers(this.$el.querySelectorAll('.gfm-merge_request')); }, + methods: { + ...mapActions(['fetchDescriptionVersion']), + }, };