summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/ide
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/ide')
-rw-r--r--app/assets/javascripts/ide/components/ide.vue40
-rw-r--r--app/assets/javascripts/ide/components/ide_tree_list.vue19
-rw-r--r--app/assets/javascripts/ide/components/preview/clientside.vue2
-rw-r--r--app/assets/javascripts/ide/components/repo_commit_section.vue2
-rw-r--r--app/assets/javascripts/ide/lib/editor_options.js2
-rw-r--r--app/assets/javascripts/ide/stores/actions.js52
-rw-r--r--app/assets/javascripts/ide/stores/actions/project.js118
-rw-r--r--app/assets/javascripts/ide/stores/actions/tree.js18
-rw-r--r--app/assets/javascripts/ide/stores/getters.js10
-rw-r--r--app/assets/javascripts/ide/stores/modules/commit/actions.js13
-rw-r--r--app/assets/javascripts/ide/stores/modules/pipelines/getters.js2
-rw-r--r--app/assets/javascripts/ide/stores/mutation_types.js1
-rw-r--r--app/assets/javascripts/ide/stores/mutations.js2
-rw-r--r--app/assets/javascripts/ide/stores/mutations/branch.js6
-rw-r--r--app/assets/javascripts/ide/stores/mutations/project.js5
15 files changed, 190 insertions, 102 deletions
diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue
index 9894ebb0624..e41b1530226 100644
--- a/app/assets/javascripts/ide/components/ide.vue
+++ b/app/assets/javascripts/ide/components/ide.vue
@@ -1,6 +1,7 @@
<script>
import Vue from 'vue';
import { mapActions, mapState, mapGetters } from 'vuex';
+import { GlButton, GlLoadingIcon } from '@gitlab/ui';
import { __ } from '~/locale';
import FindFile from '~/vue_shared/components/file_finder/index.vue';
import NewModal from './new_dropdown/modal.vue';
@@ -22,6 +23,8 @@ export default {
FindFile,
ErrorMessage,
CommitEditorHeader,
+ GlButton,
+ GlLoadingIcon,
},
props: {
rightPaneComponent: {
@@ -47,13 +50,15 @@ export default {
'someUncommittedChanges',
'isCommitModeActive',
'allBlobs',
+ 'emptyRepo',
+ 'currentTree',
]),
},
mounted() {
window.onbeforeunload = e => this.onBeforeUnload(e);
},
methods: {
- ...mapActions(['toggleFileFinder']),
+ ...mapActions(['toggleFileFinder', 'openNewEntryModal']),
onBeforeUnload(e = {}) {
const returnValue = __('Are you sure you want to lose unsaved changes?');
@@ -98,17 +103,40 @@ export default {
<repo-editor :file="activeFile" class="multi-file-edit-pane-content" />
</template>
<template v-else>
- <div v-once class="ide-empty-state">
+ <div class="ide-empty-state">
<div class="row js-empty-state">
<div class="col-12">
<div class="svg-content svg-250"><img :src="emptyStateSvgPath" /></div>
</div>
<div class="col-12">
<div class="text-content text-center">
- <h4>Welcome to the GitLab IDE</h4>
- <p>
- Select a file from the left sidebar to begin editing. Afterwards, you'll be able
- to commit your changes.
+ <h4>
+ {{ __('Make and review changes in the browser with the Web IDE') }}
+ </h4>
+ <template v-if="emptyRepo">
+ <p>
+ {{
+ __(
+ "Create a new file as there are no files yet. Afterwards, you'll be able to commit your changes.",
+ )
+ }}
+ </p>
+ <gl-button
+ variant="success"
+ :title="__('New file')"
+ :aria-label="__('New file')"
+ @click="openNewEntryModal({ type: 'blob' })"
+ >
+ {{ __('New file') }}
+ </gl-button>
+ </template>
+ <gl-loading-icon v-else-if="!currentTree || currentTree.loading" size="md" />
+ <p v-else>
+ {{
+ __(
+ "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes.",
+ )
+ }}
</p>
</div>
</div>
diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue
index 81374f26645..95782b2c88a 100644
--- a/app/assets/javascripts/ide/components/ide_tree_list.vue
+++ b/app/assets/javascripts/ide/components/ide_tree_list.vue
@@ -54,14 +54,17 @@ export default {
<slot name="header"></slot>
</header>
<div class="ide-tree-body h-100">
- <file-row
- v-for="file in currentTree.tree"
- :key="file.key"
- :file="file"
- :level="0"
- :extra-component="$options.FileRowExtra"
- @toggleTreeOpen="toggleTreeOpen"
- />
+ <template v-if="currentTree.tree.length">
+ <file-row
+ v-for="file in currentTree.tree"
+ :key="file.key"
+ :file="file"
+ :level="0"
+ :extra-component="$options.FileRowExtra"
+ @toggleTreeOpen="toggleTreeOpen"
+ />
+ </template>
+ <div v-else class="file-row">{{ __('No files') }}</div>
</div>
</template>
</div>
diff --git a/app/assets/javascripts/ide/components/preview/clientside.vue b/app/assets/javascripts/ide/components/preview/clientside.vue
index c98dda00817..6999746f115 100644
--- a/app/assets/javascripts/ide/components/preview/clientside.vue
+++ b/app/assets/javascripts/ide/components/preview/clientside.vue
@@ -105,7 +105,7 @@ export default {
.then(() => {
this.initManager('#ide-preview', this.sandboxOpts, {
fileResolver: {
- isFile: p => Promise.resolve(!!this.entries[createPathWithExt(p)]),
+ isFile: p => Promise.resolve(Boolean(this.entries[createPathWithExt(p)])),
readFile: p => this.loadFileContent(createPathWithExt(p)).then(content => content),
},
});
diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue
index 99f1d4a573d..5201c33b1b4 100644
--- a/app/assets/javascripts/ide/components/repo_commit_section.vue
+++ b/app/assets/javascripts/ide/components/repo_commit_section.vue
@@ -30,7 +30,7 @@ export default {
...mapGetters(['lastOpenedFile', 'hasChanges', 'someUncommittedChanges', 'activeFile']),
...mapGetters('commit', ['discardDraftButtonDisabled']),
showStageUnstageArea() {
- return !!(this.someUncommittedChanges || this.lastCommitMsg || !this.unusedSeal);
+ return Boolean(this.someUncommittedChanges || this.lastCommitMsg || !this.unusedSeal);
},
activeFileKey() {
return this.activeFile ? this.activeFile.key : null;
diff --git a/app/assets/javascripts/ide/lib/editor_options.js b/app/assets/javascripts/ide/lib/editor_options.js
index e35595ab1fd..dac2a8e8b51 100644
--- a/app/assets/javascripts/ide/lib/editor_options.js
+++ b/app/assets/javascripts/ide/lib/editor_options.js
@@ -11,7 +11,7 @@ export const defaultEditorOptions = {
export default [
{
- readOnly: model => !!model.file.file_lock,
+ readOnly: model => Boolean(model.file.file_lock),
quickSuggestions: model => !(model.language === 'markdown'),
},
];
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
index fd678e6e10c..dc8ca732879 100644
--- a/app/assets/javascripts/ide/stores/actions.js
+++ b/app/assets/javascripts/ide/stores/actions.js
@@ -1,12 +1,15 @@
import $ from 'jquery';
import Vue from 'vue';
+import { __, sprintf } from '~/locale';
import { visitUrl } from '~/lib/utils/url_utility';
import flash from '~/flash';
+import _ from 'underscore';
import * as types from './mutation_types';
import { decorateFiles } from '../lib/files';
import { stageKeys } from '../constants';
+import service from '../services';
-export const redirectToUrl = (_, url) => visitUrl(url);
+export const redirectToUrl = (self, url) => visitUrl(url);
export const setInitialData = ({ commit }, data) => commit(types.SET_INITIAL_DATA, data);
@@ -239,6 +242,53 @@ export const renameEntry = (
}
};
+export const getBranchData = ({ commit, state }, { projectId, branchId, force = false } = {}) =>
+ new Promise((resolve, reject) => {
+ const currentProject = state.projects[projectId];
+ if (!currentProject || !currentProject.branches[branchId] || force) {
+ service
+ .getBranchData(projectId, branchId)
+ .then(({ data }) => {
+ const { id } = data.commit;
+ commit(types.SET_BRANCH, {
+ projectPath: projectId,
+ branchName: branchId,
+ branch: data,
+ });
+ commit(types.SET_BRANCH_WORKING_REFERENCE, { projectId, branchId, reference: id });
+ resolve(data);
+ })
+ .catch(e => {
+ if (e.response.status === 404) {
+ reject(e);
+ } else {
+ flash(
+ __('Error loading branch data. Please try again.'),
+ 'alert',
+ document,
+ null,
+ false,
+ true,
+ );
+
+ reject(
+ new Error(
+ sprintf(
+ __('Branch not loaded - %{branchId}'),
+ {
+ branchId: `<strong>${_.escape(projectId)}/${_.escape(branchId)}</strong>`,
+ },
+ false,
+ ),
+ ),
+ );
+ }
+ });
+ } else {
+ resolve(currentProject.branches[branchId]);
+ }
+ });
+
export * from './actions/tree';
export * from './actions/file';
export * from './actions/project';
diff --git a/app/assets/javascripts/ide/stores/actions/project.js b/app/assets/javascripts/ide/stores/actions/project.js
index 4b10d148ebf..dd8f17e4f3a 100644
--- a/app/assets/javascripts/ide/stores/actions/project.js
+++ b/app/assets/javascripts/ide/stores/actions/project.js
@@ -35,48 +35,6 @@ export const getProjectData = ({ commit, state }, { namespace, projectId, force
}
});
-export const getBranchData = (
- { commit, dispatch, state },
- { projectId, branchId, force = false } = {},
-) =>
- new Promise((resolve, reject) => {
- if (
- typeof state.projects[`${projectId}`] === 'undefined' ||
- !state.projects[`${projectId}`].branches[branchId] ||
- force
- ) {
- service
- .getBranchData(`${projectId}`, branchId)
- .then(({ data }) => {
- const { id } = data.commit;
- commit(types.SET_BRANCH, {
- projectPath: `${projectId}`,
- branchName: branchId,
- branch: data,
- });
- commit(types.SET_BRANCH_WORKING_REFERENCE, { projectId, branchId, reference: id });
- resolve(data);
- })
- .catch(e => {
- if (e.response.status === 404) {
- dispatch('showBranchNotFoundError', branchId);
- } else {
- flash(
- __('Error loading branch data. Please try again.'),
- 'alert',
- document,
- null,
- false,
- true,
- );
- }
- reject(new Error(`Branch not loaded - ${projectId}/${branchId}`));
- });
- } else {
- resolve(state.projects[`${projectId}`].branches[branchId]);
- }
- });
-
export const refreshLastCommitData = ({ commit }, { projectId, branchId } = {}) =>
service
.getBranchData(projectId, branchId)
@@ -125,40 +83,66 @@ export const showBranchNotFoundError = ({ dispatch }, branchId) => {
});
};
-export const openBranch = ({ dispatch, state }, { projectId, branchId, basePath }) => {
- dispatch('setCurrentBranchId', branchId);
-
- dispatch('getBranchData', {
- projectId,
- branchId,
+export const showEmptyState = ({ commit, state }, { projectId, branchId }) => {
+ const treePath = `${projectId}/${branchId}`;
+ commit(types.CREATE_TREE, { treePath });
+ commit(types.TOGGLE_LOADING, {
+ entry: state.trees[treePath],
+ forceValue: false,
});
+};
- return dispatch('getFiles', {
+export const openBranch = ({ dispatch, state, getters }, { projectId, branchId, basePath }) => {
+ dispatch('setCurrentBranchId', branchId);
+
+ if (getters.emptyRepo) {
+ return dispatch('showEmptyState', { projectId, branchId });
+ }
+ return dispatch('getBranchData', {
projectId,
branchId,
})
.then(() => {
- if (basePath) {
- const path = basePath.slice(-1) === '/' ? basePath.slice(0, -1) : basePath;
- const treeEntryKey = Object.keys(state.entries).find(
- key => key === path && !state.entries[key].pending,
- );
- const treeEntry = state.entries[treeEntryKey];
-
- if (treeEntry) {
- dispatch('handleTreeEntryAction', treeEntry);
- } else {
- dispatch('createTempEntry', {
- name: path,
- type: 'blob',
- });
- }
- }
- })
- .then(() => {
dispatch('getMergeRequestsForBranch', {
projectId,
branchId,
});
+ dispatch('getFiles', {
+ projectId,
+ branchId,
+ })
+ .then(() => {
+ if (basePath) {
+ const path = basePath.slice(-1) === '/' ? basePath.slice(0, -1) : basePath;
+ const treeEntryKey = Object.keys(state.entries).find(
+ key => key === path && !state.entries[key].pending,
+ );
+ const treeEntry = state.entries[treeEntryKey];
+
+ if (treeEntry) {
+ dispatch('handleTreeEntryAction', treeEntry);
+ } else {
+ dispatch('createTempEntry', {
+ name: path,
+ type: 'blob',
+ });
+ }
+ }
+ })
+ .catch(
+ () =>
+ new Error(
+ sprintf(
+ __('An error occurred whilst getting files for - %{branchId}'),
+ {
+ branchId: `<strong>${_.escape(projectId)}/${_.escape(branchId)}</strong>`,
+ },
+ false,
+ ),
+ ),
+ );
+ })
+ .catch(() => {
+ dispatch('showBranchNotFoundError', branchId);
});
};
diff --git a/app/assets/javascripts/ide/stores/actions/tree.js b/app/assets/javascripts/ide/stores/actions/tree.js
index 3d83e4a9ba5..75511574d3e 100644
--- a/app/assets/javascripts/ide/stores/actions/tree.js
+++ b/app/assets/javascripts/ide/stores/actions/tree.js
@@ -74,17 +74,13 @@ export const getFiles = ({ state, commit, dispatch }, { projectId, branchId } =
resolve();
})
.catch(e => {
- if (e.response.status === 404) {
- dispatch('showBranchNotFoundError', branchId);
- } else {
- dispatch('setErrorMessage', {
- text: __('An error occurred whilst loading all the files.'),
- action: payload =>
- dispatch('getFiles', payload).then(() => dispatch('setErrorMessage', null)),
- actionText: __('Please try again'),
- actionPayload: { projectId, branchId },
- });
- }
+ dispatch('setErrorMessage', {
+ text: __('An error occurred whilst loading all the files.'),
+ action: payload =>
+ dispatch('getFiles', payload).then(() => dispatch('setErrorMessage', null)),
+ actionText: __('Please try again'),
+ actionPayload: { projectId, branchId },
+ });
reject(e);
});
} else {
diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js
index 490658a4543..5a736805fdc 100644
--- a/app/assets/javascripts/ide/stores/getters.js
+++ b/app/assets/javascripts/ide/stores/getters.js
@@ -36,12 +36,16 @@ export const currentMergeRequest = state => {
export const currentProject = state => state.projects[state.currentProjectId];
+export const emptyRepo = state =>
+ state.projects[state.currentProjectId] && state.projects[state.currentProjectId].empty_repo;
+
export const currentTree = state =>
state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
-export const hasChanges = state => !!state.changedFiles.length || !!state.stagedFiles.length;
+export const hasChanges = state =>
+ Boolean(state.changedFiles.length) || Boolean(state.stagedFiles.length);
-export const hasMergeRequest = state => !!state.currentMergeRequestId;
+export const hasMergeRequest = state => Boolean(state.currentMergeRequestId);
export const allBlobs = state =>
Object.keys(state.entries)
@@ -67,7 +71,7 @@ export const isCommitModeActive = state => state.currentActivityView === activit
export const isReviewModeActive = state => state.currentActivityView === activityBarViews.review;
export const someUncommittedChanges = state =>
- !!(state.changedFiles.length || state.stagedFiles.length);
+ Boolean(state.changedFiles.length || state.stagedFiles.length);
export const getChangesInFolder = state => path => {
const changedFilesCount = state.changedFiles.filter(f => filePathMatches(f.path, path)).length;
diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js
index c2760eb1554..77ea2084877 100644
--- a/app/assets/javascripts/ide/stores/modules/commit/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js
@@ -102,7 +102,7 @@ export const updateFilesAfterCommit = ({ commit, dispatch, rootState, rootGetter
eventHub.$emit(`editor.update.model.content.${file.key}`, {
content: file.content,
- changed: !!changedFile,
+ changed: Boolean(changedFile),
});
});
};
@@ -135,6 +135,17 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo
return null;
}
+ if (!data.parent_ids.length) {
+ commit(
+ rootTypes.TOGGLE_EMPTY_STATE,
+ {
+ projectPath: rootState.currentProjectId,
+ value: false,
+ },
+ { root: true },
+ );
+ }
+
dispatch('setLastCommitMessage', data);
dispatch('updateCommitMessage', '');
return dispatch('updateFilesAfterCommit', {
diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/getters.js b/app/assets/javascripts/ide/stores/modules/pipelines/getters.js
index ef7cd4ff8e8..1d127d915d7 100644
--- a/app/assets/javascripts/ide/stores/modules/pipelines/getters.js
+++ b/app/assets/javascripts/ide/stores/modules/pipelines/getters.js
@@ -1,6 +1,6 @@
import { states } from './constants';
-export const hasLatestPipeline = state => !state.isLoadingPipeline && !!state.latestPipeline;
+export const hasLatestPipeline = state => !state.isLoadingPipeline && Boolean(state.latestPipeline);
export const pipelineFailed = state =>
state.latestPipeline && state.latestPipeline.details.status.text === states.failed;
diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js
index a5f8098dc17..86ab76136df 100644
--- a/app/assets/javascripts/ide/stores/mutation_types.js
+++ b/app/assets/javascripts/ide/stores/mutation_types.js
@@ -12,6 +12,7 @@ export const SET_LINKS = 'SET_LINKS';
export const SET_PROJECT = 'SET_PROJECT';
export const SET_CURRENT_PROJECT = 'SET_CURRENT_PROJECT';
export const TOGGLE_PROJECT_OPEN = 'TOGGLE_PROJECT_OPEN';
+export const TOGGLE_EMPTY_STATE = 'TOGGLE_EMPTY_STATE';
// Merge Request Mutation Types
export const SET_MERGE_REQUEST = 'SET_MERGE_REQUEST';
diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js
index 344b189decf..ae42b87c9a7 100644
--- a/app/assets/javascripts/ide/stores/mutations.js
+++ b/app/assets/javascripts/ide/stores/mutations.js
@@ -142,7 +142,7 @@ export default {
Object.assign(state.entries[file.path], {
raw: file.content,
- changed: !!changedFile,
+ changed: Boolean(changedFile),
staged: false,
prevPath: '',
moved: false,
diff --git a/app/assets/javascripts/ide/stores/mutations/branch.js b/app/assets/javascripts/ide/stores/mutations/branch.js
index e09f88878f4..6afd8de2aa4 100644
--- a/app/assets/javascripts/ide/stores/mutations/branch.js
+++ b/app/assets/javascripts/ide/stores/mutations/branch.js
@@ -19,6 +19,12 @@ export default {
});
},
[types.SET_BRANCH_WORKING_REFERENCE](state, { projectId, branchId, reference }) {
+ if (!state.projects[projectId].branches[branchId]) {
+ Object.assign(state.projects[projectId].branches, {
+ [branchId]: {},
+ });
+ }
+
Object.assign(state.projects[projectId].branches[branchId], {
workingReference: reference,
});
diff --git a/app/assets/javascripts/ide/stores/mutations/project.js b/app/assets/javascripts/ide/stores/mutations/project.js
index 284b39a2c72..9230f3839c1 100644
--- a/app/assets/javascripts/ide/stores/mutations/project.js
+++ b/app/assets/javascripts/ide/stores/mutations/project.js
@@ -21,4 +21,9 @@ export default {
}),
});
},
+ [types.TOGGLE_EMPTY_STATE](state, { projectPath, value }) {
+ Object.assign(state.projects[projectPath], {
+ empty_repo: value,
+ });
+ },
};