summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/ide
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-12-20 13:37:47 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-12-20 13:37:47 +0000
commitaee0a117a889461ce8ced6fcf73207fe017f1d99 (patch)
tree891d9ef189227a8445d83f35c1b0fc99573f4380 /app/assets/javascripts/ide
parent8d46af3258650d305f53b819eabf7ab18d22f59e (diff)
downloadgitlab-ce-aee0a117a889461ce8ced6fcf73207fe017f1d99.tar.gz
Add latest changes from gitlab-org/gitlab@14-6-stable-eev14.6.0-rc42
Diffstat (limited to 'app/assets/javascripts/ide')
-rw-r--r--app/assets/javascripts/ide/components/activity_bar.vue2
-rw-r--r--app/assets/javascripts/ide/components/ide_tree_list.vue14
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/index.vue2
-rw-r--r--app/assets/javascripts/ide/components/pipelines/empty_state.vue35
-rw-r--r--app/assets/javascripts/ide/components/pipelines/list.vue28
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue39
-rw-r--r--app/assets/javascripts/ide/constants.js6
-rw-r--r--app/assets/javascripts/ide/ide_router.js69
-rw-r--r--app/assets/javascripts/ide/index.js11
-rw-r--r--app/assets/javascripts/ide/lib/themes/monokai.js2
-rw-r--r--app/assets/javascripts/ide/lib/themes/none.js1
-rw-r--r--app/assets/javascripts/ide/lib/themes/solarized_dark.js2
-rw-r--r--app/assets/javascripts/ide/lib/themes/solarized_light.js2
-rw-r--r--app/assets/javascripts/ide/lib/themes/white.js1
-rw-r--r--app/assets/javascripts/ide/queries/ide_project.fragment.graphql1
-rw-r--r--app/assets/javascripts/ide/services/index.js34
-rw-r--r--app/assets/javascripts/ide/stores/actions/project.js57
-rw-r--r--app/assets/javascripts/ide/stores/mutation_types.js1
-rw-r--r--app/assets/javascripts/ide/stores/mutations/project.js12
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]);
+ });
+ },
};