diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-20 16:37:47 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-20 16:37:47 +0300 |
commit | aee0a117a889461ce8ced6fcf73207fe017f1d99 (patch) | |
tree | 891d9ef189227a8445d83f35c1b0fc99573f4380 /app/assets/javascripts/ide | |
parent | 8d46af3258650d305f53b819eabf7ab18d22f59e (diff) |
Add latest changes from gitlab-org/gitlab@14-6-stable-eev14.6.0-rc42
Diffstat (limited to 'app/assets/javascripts/ide')
19 files changed, 187 insertions, 132 deletions
diff --git a/app/assets/javascripts/ide/components/activity_bar.vue b/app/assets/javascripts/ide/components/activity_bar.vue index c71d911adfb..846b4d92724 100644 --- a/app/assets/javascripts/ide/components/activity_bar.vue +++ b/app/assets/javascripts/ide/components/activity_bar.vue @@ -63,7 +63,7 @@ export default { class="ide-sidebar-link js-ide-review-mode" @click.prevent="changedActivityView($event, $options.leftSidebarViews.review.name)" > - <gl-icon name="file-modified" /> + <gl-icon name="review-list" /> </button> </li> <li> diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue index b987adc8bae..0fc7337ad26 100644 --- a/app/assets/javascripts/ide/components/ide_tree_list.vue +++ b/app/assets/javascripts/ide/components/ide_tree_list.vue @@ -29,14 +29,20 @@ export default { }, }, watch: { - showLoading(newVal) { - if (!newVal) { - this.$emit('tree-ready'); - } + showLoading() { + this.notifyTreeReady(); }, }, + mounted() { + this.notifyTreeReady(); + }, methods: { ...mapActions(['toggleTreeOpen']), + notifyTreeReady() { + if (!this.showLoading) { + this.$emit('tree-ready'); + } + }, clickedFile() { performanceMarkAndMeasure({ mark: WEBIDE_MARK_FILE_CLICKED }); }, diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue index bdd201aac1b..87b60eca73c 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/index.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue @@ -67,7 +67,7 @@ export default { data-qa-selector="dropdown_button" @click.stop="openDropdown()" > - <gl-icon name="ellipsis_v" /> <gl-icon name="chevron-down" /> + <gl-icon name="ellipsis_v" /> </button> <ul ref="dropdownMenu" class="dropdown-menu dropdown-menu-right"> <template v-if="type === 'tree'"> diff --git a/app/assets/javascripts/ide/components/pipelines/empty_state.vue b/app/assets/javascripts/ide/components/pipelines/empty_state.vue new file mode 100644 index 00000000000..194deb2ece0 --- /dev/null +++ b/app/assets/javascripts/ide/components/pipelines/empty_state.vue @@ -0,0 +1,35 @@ +<script> +import { GlEmptyState } from '@gitlab/ui'; +import { mapState } from 'vuex'; +import { s__ } from '~/locale'; +import { helpPagePath } from '~/helpers/help_page_helper'; + +export default { + components: { + GlEmptyState, + }, + computed: { + ...mapState(['pipelinesEmptyStateSvgPath']), + ciHelpPagePath() { + return helpPagePath('ci/quick_start/index.md'); + }, + }, + i18n: { + title: s__('Pipelines|Build with confidence'), + description: s__(`Pipelines|GitLab CI/CD can automatically build, + test, and deploy your code. Let GitLab take care of time + consuming tasks, so you can spend more time creating.`), + primaryButtonText: s__('Pipelines|Get started with GitLab CI/CD'), + }, +}; +</script> + +<template> + <gl-empty-state + :title="$options.i18n.title" + :svg-path="pipelinesEmptyStateSvgPath" + :description="$options.i18n.description" + :primary-button-text="$options.i18n.primaryButtonText" + :primary-button-link="ciHelpPagePath" + /> +</template> diff --git a/app/assets/javascripts/ide/components/pipelines/list.vue b/app/assets/javascripts/ide/components/pipelines/list.vue index e1caf1ba44a..7f513afe82e 100644 --- a/app/assets/javascripts/ide/components/pipelines/list.vue +++ b/app/assets/javascripts/ide/components/pipelines/list.vue @@ -11,10 +11,17 @@ import { import { escape } from 'lodash'; import { mapActions, mapGetters, mapState } from 'vuex'; import IDEServices from '~/ide/services'; -import { sprintf, __ } from '../../../locale'; -import EmptyState from '../../../pipelines/components/pipelines_list/empty_state.vue'; -import CiIcon from '../../../vue_shared/components/ci_icon.vue'; +import { sprintf, __ } from '~/locale'; +import CiIcon from '~/vue_shared/components/ci_icon.vue'; import JobsList from '../jobs/list.vue'; +import EmptyState from './empty_state.vue'; + +const CLASSES_FLEX_VERTICAL_CENTER = [ + 'gl-h-full', + 'gl-display-flex', + 'gl-flex-direction-column', + 'gl-justify-content-center', +]; export default { components: { @@ -32,7 +39,6 @@ export default { SafeHtml, }, computed: { - ...mapState(['pipelinesEmptyStateSvgPath']), ...mapGetters(['currentProject']), ...mapGetters('pipelines', ['jobsCount', 'failedJobsCount', 'failedStages', 'pipelineFailed']), ...mapState('pipelines', [ @@ -63,12 +69,15 @@ export default { methods: { ...mapActions('pipelines', ['fetchLatestPipeline']), }, + CLASSES_FLEX_VERTICAL_CENTER, }; </script> <template> <div class="ide-pipeline"> - <gl-loading-icon v-if="showLoadingIcon" size="lg" class="gl-mt-3" /> + <div v-if="showLoadingIcon" :class="$options.CLASSES_FLEX_VERTICAL_CENTER"> + <gl-loading-icon size="lg" /> + </div> <template v-else-if="hasLoadedPipeline"> <header v-if="latestPipeline" class="ide-tree-header ide-pipeline-header"> <ci-icon :status="latestPipeline.details.status" :size="24" class="d-flex" /> @@ -83,12 +92,9 @@ export default { </a> </span> </header> - <empty-state - v-if="!latestPipeline" - :empty-state-svg-path="pipelinesEmptyStateSvgPath" - :can-set-ci="true" - class="gl-p-5" - /> + <div v-if="!latestPipeline" :class="$options.CLASSES_FLEX_VERTICAL_CENTER"> + <empty-state /> + </div> <gl-alert v-else-if="latestPipeline.yamlError" variant="danger" diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue index 2bf99550bf2..05493db1dff 100644 --- a/app/assets/javascripts/ide/components/repo_editor.vue +++ b/app/assets/javascripts/ide/components/repo_editor.vue @@ -7,6 +7,7 @@ import { EDITOR_CODE_INSTANCE_FN, EDITOR_DIFF_INSTANCE_FN, } from '~/editor/constants'; +import { SourceEditorExtension } from '~/editor/extensions/source_editor_extension_base'; import { EditorWebIdeExtension } from '~/editor/extensions/source_editor_webide_ext'; import SourceEditor from '~/editor/source_editor'; import createFlash from '~/flash'; @@ -302,30 +303,32 @@ export default { ...instanceOptions, ...this.editorOptions, }); - - this.editor.use( - new EditorWebIdeExtension({ - instance: this.editor, - modelManager: this.modelManager, - store: this.$store, - file: this.file, - options: this.editorOptions, - }), - ); + this.editor.use([ + { + definition: SourceEditorExtension, + }, + { + definition: EditorWebIdeExtension, + setupOptions: { + modelManager: this.modelManager, + store: this.$store, + file: this.file, + options: this.editorOptions, + }, + }, + ]); if ( this.fileType === MARKDOWN_FILE_TYPE && this.editor?.getEditorType() === EDITOR_TYPE_CODE && this.previewMarkdownPath ) { - import('~/editor/extensions/source_editor_markdown_ext') - .then(({ EditorMarkdownExtension: MarkdownExtension } = {}) => { - this.editor.use( - new MarkdownExtension({ - instance: this.editor, - previewMarkdownPath: this.previewMarkdownPath, - }), - ); + import('~/editor/extensions/source_editor_markdown_livepreview_ext') + .then(({ EditorMarkdownPreviewExtension: MarkdownLivePreview }) => { + this.editor.use({ + definition: MarkdownLivePreview, + setupOptions: { previewMarkdownPath: this.previewMarkdownPath }, + }); }) .catch((e) => createFlash({ diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js index 706d98fdb90..775b6906498 100644 --- a/app/assets/javascripts/ide/constants.js +++ b/app/assets/javascripts/ide/constants.js @@ -76,15 +76,15 @@ export const stageKeys = { export const commitItemIconMap = { addition: { icon: 'file-addition', - class: 'ide-file-addition', + class: 'file-addition ide-file-addition', }, modified: { icon: 'file-modified', - class: 'ide-file-modified', + class: 'file-modified ide-file-modified', }, deleted: { icon: 'file-deletion', - class: 'ide-file-deletion', + class: 'file-deletion ide-file-deletion', }, }; diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js index 27cedd80347..1fc447886bb 100644 --- a/app/assets/javascripts/ide/ide_router.js +++ b/app/assets/javascripts/ide/ide_router.js @@ -1,8 +1,6 @@ import Vue from 'vue'; -import createFlash from '~/flash'; import IdeRouter from '~/ide/ide_router_extension'; import { joinPaths } from '~/lib/utils/url_utility'; -import { __ } from '~/locale'; import { WEBIDE_MARK_FETCH_PROJECT_DATA_START, WEBIDE_MARK_FETCH_PROJECT_DATA_FINISH, @@ -75,49 +73,34 @@ export const createRouter = (store, defaultBranch) => { router.beforeEach((to, from, next) => { if (to.params.namespace && to.params.project) { - performanceMarkAndMeasure({ mark: WEBIDE_MARK_FETCH_PROJECT_DATA_START }); - store - .dispatch('getProjectData', { - namespace: to.params.namespace, - projectId: to.params.project, - }) - .then(() => { - const basePath = to.params.pathMatch || ''; - const projectId = `${to.params.namespace}/${to.params.project}`; - const branchId = to.params.branchid; - const mergeRequestId = to.params.mrid; + const basePath = to.params.pathMatch || ''; + const projectId = `${to.params.namespace}/${to.params.project}`; + const branchId = to.params.branchid; + const mergeRequestId = to.params.mrid; - if (branchId) { - performanceMarkAndMeasure({ - mark: WEBIDE_MARK_FETCH_PROJECT_DATA_FINISH, - measures: [ - { - name: WEBIDE_MEASURE_FETCH_PROJECT_DATA, - start: WEBIDE_MARK_FETCH_PROJECT_DATA_START, - }, - ], - }); - store.dispatch('openBranch', { - projectId, - branchId, - basePath, - }); - } else if (mergeRequestId) { - store.dispatch('openMergeRequest', { - projectId, - mergeRequestId, - targetProjectId: to.query.target_project, - }); - } - }) - .catch((e) => { - createFlash({ - message: __('Error while loading the project data. Please try again.'), - fadeTransition: false, - addBodyClass: true, - }); - throw e; + performanceMarkAndMeasure({ mark: WEBIDE_MARK_FETCH_PROJECT_DATA_START }); + if (branchId) { + performanceMarkAndMeasure({ + mark: WEBIDE_MARK_FETCH_PROJECT_DATA_FINISH, + measures: [ + { + name: WEBIDE_MEASURE_FETCH_PROJECT_DATA, + start: WEBIDE_MARK_FETCH_PROJECT_DATA_START, + }, + ], + }); + store.dispatch('openBranch', { + projectId, + branchId, + basePath, + }); + } else if (mergeRequestId) { + store.dispatch('openMergeRequest', { + projectId, + mergeRequestId, + targetProjectId: to.query.target_project, }); + } } next(); diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js index bdffed70882..df643675357 100644 --- a/app/assets/javascripts/ide/index.js +++ b/app/assets/javascripts/ide/index.js @@ -34,11 +34,18 @@ Vue.use(PerformancePlugin, { * @param {extendStoreCallback} options.extendStore - * Function that receives the default store and returns an extended one. */ -export function initIde(el, options = {}) { +export const initIde = (el, options = {}) => { if (!el) return null; const { rootComponent = ide, extendStore = identity } = options; + const store = createStore(); + const project = JSON.parse(el.dataset.project); + store.dispatch('setProject', { project }); + + // fire and forget fetching non-critical project info + store.dispatch('fetchProjectPermissions'); + const router = createRouter(store, el.dataset.defaultBranch || DEFAULT_BRANCH); return new Vue({ @@ -77,7 +84,7 @@ export function initIde(el, options = {}) { return createElement(rootComponent); }, }); -} +}; /** * Start the IDE. diff --git a/app/assets/javascripts/ide/lib/themes/monokai.js b/app/assets/javascripts/ide/lib/themes/monokai.js index d7636574754..36fa5039be7 100644 --- a/app/assets/javascripts/ide/lib/themes/monokai.js +++ b/app/assets/javascripts/ide/lib/themes/monokai.js @@ -162,8 +162,8 @@ export default { 'editor.selectionBackground': '#49483E', 'editor.lineHighlightBackground': '#3E3D32', 'editorCursor.foreground': '#F8F8F0', - 'editorWhitespace.foreground': '#3B3A32', 'editorIndentGuide.activeBackground': '#9D550FB0', 'editor.selectionHighlightBorder': '#222218', + 'editorWhitespace.foreground': '#75715e', }, }; diff --git a/app/assets/javascripts/ide/lib/themes/none.js b/app/assets/javascripts/ide/lib/themes/none.js index 8e722c4ff88..0842bc04cff 100644 --- a/app/assets/javascripts/ide/lib/themes/none.js +++ b/app/assets/javascripts/ide/lib/themes/none.js @@ -13,5 +13,6 @@ export default { 'diffEditor.insertedTextBackground': '#a0f5b420', 'diffEditor.removedTextBackground': '#f9d7dc20', 'editorIndentGuide.activeBackground': '#cccccc', + 'editorSuggestWidget.focusHighlightForeground': '#96D8FD', }, }; diff --git a/app/assets/javascripts/ide/lib/themes/solarized_dark.js b/app/assets/javascripts/ide/lib/themes/solarized_dark.js index 3c9414b9dc9..8ae609285ac 100644 --- a/app/assets/javascripts/ide/lib/themes/solarized_dark.js +++ b/app/assets/javascripts/ide/lib/themes/solarized_dark.js @@ -1105,6 +1105,6 @@ export default { 'editor.selectionBackground': '#073642', 'editor.lineHighlightBackground': '#073642', 'editorCursor.foreground': '#819090', - 'editorWhitespace.foreground': '#073642', + 'editorWhitespace.foreground': '#586e75', }, }; diff --git a/app/assets/javascripts/ide/lib/themes/solarized_light.js b/app/assets/javascripts/ide/lib/themes/solarized_light.js index b7bfcf33b0f..2c9f3d904f1 100644 --- a/app/assets/javascripts/ide/lib/themes/solarized_light.js +++ b/app/assets/javascripts/ide/lib/themes/solarized_light.js @@ -1096,6 +1096,6 @@ export default { 'editor.selectionBackground': '#EEE8D5', 'editor.lineHighlightBackground': '#EEE8D5', 'editorCursor.foreground': '#000000', - 'editorWhitespace.foreground': '#EAE3C9', + 'editorWhitespace.foreground': '#93a1a1', }, }; diff --git a/app/assets/javascripts/ide/lib/themes/white.js b/app/assets/javascripts/ide/lib/themes/white.js index f06458d8a16..69c63c82021 100644 --- a/app/assets/javascripts/ide/lib/themes/white.js +++ b/app/assets/javascripts/ide/lib/themes/white.js @@ -142,5 +142,6 @@ export default { 'diffEditor.insertedTextBackground': '#a0f5b420', 'diffEditor.removedTextBackground': '#f9d7dc20', 'editorIndentGuide.activeBackground': '#cccccc', + 'editorSuggestWidget.focusHighlightForeground': '#96D8FD', }, }; diff --git a/app/assets/javascripts/ide/queries/ide_project.fragment.graphql b/app/assets/javascripts/ide/queries/ide_project.fragment.graphql index c107f2376f9..a0b520858e6 100644 --- a/app/assets/javascripts/ide/queries/ide_project.fragment.graphql +++ b/app/assets/javascripts/ide/queries/ide_project.fragment.graphql @@ -1,4 +1,5 @@ fragment IdeProject on Project { + id userPermissions { createMergeRequestIn readMergeRequest diff --git a/app/assets/javascripts/ide/services/index.js b/app/assets/javascripts/ide/services/index.js index ef4f47f226a..805476c71bc 100644 --- a/app/assets/javascripts/ide/services/index.js +++ b/app/assets/javascripts/ide/services/index.js @@ -1,19 +1,12 @@ -import getIdeProject from 'ee_else_ce/ide/queries/get_ide_project.query.graphql'; import Api from '~/api'; +import getIdeProject from 'ee_else_ce/ide/queries/get_ide_project.query.graphql'; import dismissUserCallout from '~/graphql_shared/mutations/dismiss_user_callout.mutation.graphql'; +import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import axios from '~/lib/utils/axios_utils'; import { joinPaths, escapeFileUrl } from '~/lib/utils/url_utility'; -import ciConfig from '~/pipeline_editor/graphql/queries/ci_config.graphql'; +import ciConfig from '~/pipeline_editor/graphql/queries/ci_config.query.graphql'; import { query, mutate } from './gql'; -const fetchApiProjectData = (projectPath) => Api.project(projectPath).then(({ data }) => data); - -const fetchGqlProjectData = (projectPath) => - query({ - query: getIdeProject, - variables: { projectPath }, - }).then(({ data }) => data.project); - export default { getFileData(endpoint) { return axios.get(endpoint, { @@ -61,18 +54,6 @@ export default { ) .then(({ data }) => data); }, - getProjectData(namespace, project) { - const projectPath = `${namespace}/${project}`; - - return Promise.all([fetchApiProjectData(projectPath), fetchGqlProjectData(projectPath)]).then( - ([apiProjectData, gqlProjectData]) => ({ - data: { - ...apiProjectData, - ...gqlProjectData, - }, - }), - ); - }, getProjectMergeRequests(projectId, params = {}) { return Api.projectMergeRequests(projectId, params); }, @@ -115,4 +96,13 @@ export default { variables: { input: { featureName: name } }, }).then(({ data }) => data); }, + getProjectPermissionsData(projectPath) { + return query({ + query: getIdeProject, + variables: { projectPath }, + }).then(({ data }) => ({ + ...data.project, + id: getIdFromGraphQLId(data.project.id), + })); + }, }; diff --git a/app/assets/javascripts/ide/stores/actions/project.js b/app/assets/javascripts/ide/stores/actions/project.js index 93ad19ba81e..0ec808339fb 100644 --- a/app/assets/javascripts/ide/stores/actions/project.js +++ b/app/assets/javascripts/ide/stores/actions/project.js @@ -1,35 +1,44 @@ import { escape } from 'lodash'; import createFlash from '~/flash'; import { __, sprintf } from '~/locale'; +import { logError } from '~/lib/logger'; import api from '../../../api'; import service from '../../services'; import * as types from '../mutation_types'; -export const getProjectData = ({ commit, state }, { namespace, projectId, force = false } = {}) => - new Promise((resolve, reject) => { - if (!state.projects[`${namespace}/${projectId}`] || force) { - commit(types.TOGGLE_LOADING, { entry: state }); - service - .getProjectData(namespace, projectId) - .then((res) => res.data) - .then((data) => { - commit(types.TOGGLE_LOADING, { entry: state }); - commit(types.SET_PROJECT, { projectPath: `${namespace}/${projectId}`, project: data }); - commit(types.SET_CURRENT_PROJECT, `${namespace}/${projectId}`); - resolve(data); - }) - .catch(() => { - createFlash({ - message: __('Error loading project data. Please try again.'), - fadeTransition: false, - addBodyClass: true, - }); - reject(new Error(`Project not loaded ${namespace}/${projectId}`)); - }); - } else { - resolve(state.projects[`${namespace}/${projectId}`]); - } +const ERROR_LOADING_PROJECT = __('Error loading project data. Please try again.'); + +const errorFetchingData = (e) => { + logError(ERROR_LOADING_PROJECT, e); + + createFlash({ + message: ERROR_LOADING_PROJECT, + fadeTransition: false, + addBodyClass: true, }); +}; + +export const setProject = ({ commit }, { project } = {}) => { + if (!project) { + return; + } + const projectPath = project.path_with_namespace; + commit(types.SET_PROJECT, { projectPath, project }); + commit(types.SET_CURRENT_PROJECT, projectPath); +}; + +export const fetchProjectPermissions = ({ commit, state }) => { + const projectPath = state.currentProjectId; + if (!projectPath) { + return undefined; + } + return service + .getProjectPermissionsData(projectPath) + .then((permissions) => { + commit(types.UPDATE_PROJECT, { projectPath, props: permissions }); + }) + .catch(errorFetchingData); +}; export const refreshLastCommitData = ({ commit }, { projectId, branchId } = {}) => service diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js index 77755b179ef..13f338c4a48 100644 --- a/app/assets/javascripts/ide/stores/mutation_types.js +++ b/app/assets/javascripts/ide/stores/mutation_types.js @@ -8,6 +8,7 @@ export const SET_LINKS = 'SET_LINKS'; // Project Mutation Types export const SET_PROJECT = 'SET_PROJECT'; export const SET_CURRENT_PROJECT = 'SET_CURRENT_PROJECT'; +export const UPDATE_PROJECT = 'UPDATE_PROJECT'; export const TOGGLE_EMPTY_STATE = 'TOGGLE_EMPTY_STATE'; // Merge request mutation types diff --git a/app/assets/javascripts/ide/stores/mutations/project.js b/app/assets/javascripts/ide/stores/mutations/project.js index 034fdad4305..9f65d3a543e 100644 --- a/app/assets/javascripts/ide/stores/mutations/project.js +++ b/app/assets/javascripts/ide/stores/mutations/project.js @@ -1,3 +1,4 @@ +import Vue from 'vue'; import * as types from '../mutation_types'; export default { @@ -24,4 +25,15 @@ export default { empty_repo: value, }); }, + [types.UPDATE_PROJECT](state, { projectPath, props }) { + const project = state.projects[projectPath]; + + if (!project || !props) { + return; + } + + Object.keys(props).forEach((key) => { + Vue.set(project, key, props[key]); + }); + }, }; |