diff options
author | Tim Zallmann <tzallmann@gitlab.com> | 2017-12-06 14:30:16 +0100 |
---|---|---|
committer | Tim Zallmann <tzallmann@gitlab.com> | 2017-12-19 12:26:02 +0100 |
commit | 179b05771100a603ba84164fd0b699ef40f9b47c (patch) | |
tree | 5faa23ed390fab0c84912b0139d4e7ac662f2758 /app | |
parent | 4bc348ce5fd9b143622a52e001949eabbc27142f (diff) | |
download | gitlab-ce-179b05771100a603ba84164fd0b699ef40f9b47c.tar.gz |
Fixes based on MR review
Diffstat (limited to 'app')
33 files changed, 208 insertions, 268 deletions
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index a04b4937bc9..21d8c790e90 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -1,5 +1,5 @@ import $ from 'jquery'; -import axios from '~/lib/utils/axios_utils'; +import axios from './lib/utils/axios_utils'; const Api = { groupsPath: '/api/:version/groups.json', diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue index f39896e9262..0be30c5cba1 100644 --- a/app/assets/javascripts/ide/components/ide.vue +++ b/app/assets/javascripts/ide/components/ide.vue @@ -1,14 +1,12 @@ -/* global Sidebar */ - <script> import { mapState, mapGetters } from 'vuex'; -import IdeSidebar from './ide_side_bar.vue'; -import IdeContextbar from './ide_context_bar.vue'; -import RepoTabs from './repo_tabs.vue'; -import RepoFileButtons from './repo_file_buttons.vue'; -import IdeStatusBar from './ide_status_bar.vue'; -import RepoPreview from './repo_preview.vue'; -import RepoEditor from './repo_editor.vue'; +import ideSidebar from './ide_side_bar.vue'; +import ideContextbar from './ide_context_bar.vue'; +import repoTabs from './repo_tabs.vue'; +import repoFileButtons from './repo_file_buttons.vue'; +import ideStatusBar from './ide_status_bar.vue'; +import repoPreview from './repo_preview.vue'; +import repoEditor from './repo_editor.vue'; export default { computed: { @@ -23,16 +21,16 @@ export default { ]), }, components: { - IdeSidebar, - IdeContextbar, - RepoTabs, - RepoFileButtons, - IdeStatusBar, - RepoEditor, - RepoPreview, + ideSidebar, + ideContextbar, + repoTabs, + repoFileButtons, + ideStatusBar, + repoEditor, + repoPreview, }, mounted() { - /* const returnValue = 'Are you sure you want to lose unsaved changes?'; + const returnValue = 'Are you sure you want to lose unsaved changes?'; window.onbeforeunload = (e) => { if (!this.changedFiles.length) return undefined; @@ -40,7 +38,7 @@ export default { returnValue, }); return returnValue; - }; */ + }; }, }; </script> @@ -66,8 +64,9 @@ export default { </template> <template v-else> - <br/><br/><br/><br/><br/> - <h4 class="clgray text-center">Welcome to the GitLab IDE</h4> + <div class="ide-empty-state"> + <h2 class="clgray">Welcome to the GitLab IDE</h2> + </div> </template> </div> <ide-contextbar/> diff --git a/app/assets/javascripts/ide/components/ide_context_bar.vue b/app/assets/javascripts/ide/components/ide_context_bar.vue index 17737622506..2e319c6309d 100644 --- a/app/assets/javascripts/ide/components/ide_context_bar.vue +++ b/app/assets/javascripts/ide/components/ide_context_bar.vue @@ -1,11 +1,11 @@ <script> import { mapGetters } from 'vuex'; -import RepoCommitSection from './repo_commit_section.vue'; +import repoCommitSection from './repo_commit_section.vue'; import icon from '../../vue_shared/components/icon.vue'; export default { components: { - RepoCommitSection, + repoCommitSection, icon, }, data() { diff --git a/app/assets/javascripts/ide/components/ide_project_branches_tree.vue b/app/assets/javascripts/ide/components/ide_project_branches_tree.vue index 84b650939ee..bd3a521ff43 100644 --- a/app/assets/javascripts/ide/components/ide_project_branches_tree.vue +++ b/app/assets/javascripts/ide/components/ide_project_branches_tree.vue @@ -1,12 +1,12 @@ <script> -import RepoTree from './ide_repo_tree.vue'; -import Icon from '../../vue_shared/components/icon.vue'; +import repoTree from './ide_repo_tree.vue'; +import icon from '../../vue_shared/components/icon.vue'; import newDropdown from './new_dropdown/index.vue'; export default { components: { - RepoTree, - Icon, + repoTree, + icon, newDropdown, }, props: { @@ -34,7 +34,7 @@ export default { </div> <div class="branch-header-btns"> <new-dropdown - :projectId="projectId" + :project-id="projectId" :branch="branch.name" path=""/> </div> diff --git a/app/assets/javascripts/ide/components/ide_project_tree.vue b/app/assets/javascripts/ide/components/ide_project_tree.vue index ca499b650fc..4df27da3d35 100644 --- a/app/assets/javascripts/ide/components/ide_project_tree.vue +++ b/app/assets/javascripts/ide/components/ide_project_tree.vue @@ -1,10 +1,10 @@ <script> -import BranchesTree from './ide_project_branches_tree.vue'; +import branchesTree from './ide_project_branches_tree.vue'; import projectAvatarImage from '../../vue_shared/components/project_avatar/project_avatar_image.vue'; export default { components: { - BranchesTree, + branchesTree, projectAvatarImage, }, props: { @@ -40,7 +40,7 @@ export default { <branches-tree v-for="(branch, index) in project.branches" :key="branch.name" - :projectId="project.path_with_namespace" + :project-id="project.path_with_namespace" :branch="branch"/> </div> </div> diff --git a/app/assets/javascripts/ide/components/ide_repo_tree.vue b/app/assets/javascripts/ide/components/ide_repo_tree.vue index 0c45864c09b..932f9c955fe 100644 --- a/app/assets/javascripts/ide/components/ide_repo_tree.vue +++ b/app/assets/javascripts/ide/components/ide_repo_tree.vue @@ -30,6 +30,12 @@ export default { 'treeList', 'isCollapsed', ]), + hasPreviousDirectory() { + return !this.isRoot && this.treeList(this.treeId).length; + }, + showLoading() { + return (!this.treeId || !this.treeList(this.treeId).length) && this.loading; + }, }, }; </script> @@ -41,10 +47,10 @@ export default { <tbody v-if="treeId"> <repo-previous-directory - v-if="!isRoot && treeList(treeId).length" + v-if="hasPreviousDirectory" /> <repo-loading-file - v-if="(!treeId || !treeList(treeId).length) && loading" + v-if="showLoading" v-for="n in 5" :key="n" /> diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue index 2644c681d06..de793c347aa 100644 --- a/app/assets/javascripts/ide/components/ide_side_bar.vue +++ b/app/assets/javascripts/ide/components/ide_side_bar.vue @@ -1,11 +1,11 @@ <script> import { mapState } from 'vuex'; -import ProjectTree from './ide_project_tree.vue'; +import projectTree from './ide_project_tree.vue'; import icon from '../../vue_shared/components/icon.vue'; export default { components: { - ProjectTree, + projectTree, icon, }, data() { diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue index 54e05a4633c..a24abadd936 100644 --- a/app/assets/javascripts/ide/components/ide_status_bar.vue +++ b/app/assets/javascripts/ide/components/ide_status_bar.vue @@ -1,6 +1,6 @@ <script> import { mapState } from 'vuex'; -import Icon from '../../vue_shared/components/icon.vue'; +import icon from '../../vue_shared/components/icon.vue'; import tooltip from '../../vue_shared/directives/tooltip'; import timeAgoMixin from '../../vue_shared/mixins/timeago'; @@ -12,7 +12,7 @@ export default { }, }, components: { - Icon, + icon, }, directives: { tooltip, @@ -31,16 +31,14 @@ export default { <template> <div class="ide-status-bar"> - <div - class="col-sm-3"> + <div> <icon name="branch" :size="12"> </icon> {{ selectedFile.branchId }} </div> - <div - class="col-sm-4"> + <div> <div v-if="selectedFile.lastCommit && selectedFile.lastCommit.id"> Last commit: @@ -53,20 +51,20 @@ export default { </a> </div> </div> - <div - class="col-sm-2 text-right"> + <div + class="text-right"> {{ selectedFile.name }} </div> <div - class="col-sm-1 text-right"> - {{ selectedFile.EOL }} + class="text-right"> + {{ selectedFile.eol }} </div> <div - class="col-sm-1 text-right"> + class="text-right"> {{ file.editorRow }}:{{ file.editorColumn }} </div> <div - class="col-sm-1 text-right"> + class="text-right"> {{ selectedFile.fileLanguage }} </div> </div> diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue index cacaa728309..c70d01c2d04 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/index.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue @@ -20,7 +20,6 @@ }, parent: { type: Object, - required: false, }, }, components: { @@ -43,16 +42,11 @@ this.openModal = !this.openModal; }, }, - computed: { - ...mapState([ - 'trees', - ]), - }, }; </script> <template> - <div class="repo-new-btn"> + <div class="repo-new-btn pull-right"> <div class="dropdown"> <button type="button" @@ -81,8 +75,8 @@ </li> <li> <upload - :projectId="projectId" - :branchId="branch" + :project-id="projectId" + :branch-id="branch" :path="path" :parent="parent" /> @@ -101,8 +95,8 @@ <new-modal v-if="openModal" :type="modalType" - :projectId="projectId" - :branchId="branch" + :project-id="projectId" + :branch-id="branch" :path="path" :parent="parent" @toggle="toggleModalOpen" diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue index 1ae141e2518..2bb723c4b1d 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue @@ -15,7 +15,6 @@ }, parent: { type: Object, - required: false, }, type: { type: String, diff --git a/app/assets/javascripts/ide/components/new_dropdown/upload.vue b/app/assets/javascripts/ide/components/new_dropdown/upload.vue index 14a047781c5..54a0ff3573e 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/upload.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/upload.vue @@ -13,7 +13,6 @@ }, parent: { type: Object, - required: false, }, path: { type: String, @@ -24,9 +23,6 @@ ...mapState([ 'trees', ]), - fileId() { - return `file-upload-${encodeURIComponent(this.projectId)}-${this.branchId}-${encodeURIComponent(this.path)}`; - }, }, methods: { ...mapActions([ @@ -82,7 +78,6 @@ > {{ __('Upload file') }} <input - :id="fileId" type="file" class="hidden" ref="fileUpload" diff --git a/app/assets/javascripts/ide/components/repo.vue b/app/assets/javascripts/ide/components/repo.vue deleted file mode 100644 index 14cbe4c6cc2..00000000000 --- a/app/assets/javascripts/ide/components/repo.vue +++ /dev/null @@ -1,63 +0,0 @@ -<script> -import { mapState, mapGetters } from 'vuex'; -import RepoSidebar from './ide_side_bar.vue'; -import RepoCommitSection from './repo_commit_section.vue'; -import RepoTabs from './repo_tabs.vue'; -import RepoFileButtons from './repo_file_buttons.vue'; -import RepoPreview from './repo_preview.vue'; -import repoEditor from './repo_editor.vue'; - -export default { - computed: { - ...mapState([ - 'currentBlobView', - ]), - ...mapGetters([ - 'isCollapsed', - 'changedFiles', - ]), - }, - components: { - RepoSidebar, - RepoTabs, - RepoFileButtons, - repoEditor, - RepoCommitSection, - RepoPreview, - }, - mounted() { - const returnValue = 'Are you sure you want to lose unsaved changes?'; - window.onbeforeunload = (e) => { - if (!this.changedFiles.length) return undefined; - - Object.assign(e, { - returnValue, - }); - return returnValue; - }; - }, -}; -</script> - -<template> - <div - class="multi-file" - :class="{ - 'is-collapsed': isCollapsed - }" - > - <repo-sidebar/> - <div - v-if="isCollapsed" - class="multi-file-edit-pane" - > - <repo-tabs /> - <component - class="multi-file-edit-pane-content" - :is="currentBlobView" - /> - <repo-file-buttons /> - </div> - <repo-commit-section /> - </div> -</template> diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue index 2b282b5a26b..a18508c2f96 100644 --- a/app/assets/javascripts/ide/components/repo_commit_section.vue +++ b/app/assets/javascripts/ide/components/repo_commit_section.vue @@ -30,6 +30,7 @@ export default { }, computed: { ...mapState([ + 'currentProjectId', 'currentBranchId', ]), ...mapGetters([ @@ -72,7 +73,7 @@ export default { this.$store.dispatch('getTreeData', { projectId: this.currentProjectId, branch: this.currentBranchId, - endpoint: `/${this.currentProjectId}/tree/${this.currentBranchId}`, + endpoint: `/tree/${this.currentBranchId}`, }); }) .catch(() => { diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue index cafe5665ce6..081d09984ea 100644 --- a/app/assets/javascripts/ide/components/repo_editor.vue +++ b/app/assets/javascripts/ide/components/repo_editor.vue @@ -57,7 +57,6 @@ export default { // Handle Cursor Position this.editor.onPositionChange((instance, e) => { this.setEditorPosition({ - file: this.$store.state.selectedFile, editorRow: e.position.lineNumber, editorColumn: e.position.column, }); @@ -81,10 +80,10 @@ export default { fileLanguage: model.language, }); - // Get File EOL + // Get File eol this.setFileEOL({ file: this.$store.state.selectedFile, - EOL: model.EOL, + eol: model.eol, }); }, }, diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue index 1dacc1e2841..18a00cb7426 100644 --- a/app/assets/javascripts/ide/components/repo_file.vue +++ b/app/assets/javascripts/ide/components/repo_file.vue @@ -20,6 +20,10 @@ type: Boolean, default: true, }, + showExtraColumns: { + type: Boolean, + default: false, + }, }, computed: { isSubmodule() { @@ -28,16 +32,6 @@ isTree() { return this.file.type === 'tree'; }, - fileIcon() { - return { - 'fa-spinner fa-spin': this.file.loading, - [this.file.icon]: !this.file.loading, - 'fa-folder-open': !this.file.loading && this.file.opened, - }; - }, - fileUrl() { - return this.file.url; - }, levelIndentation() { return { marginLeft: `${this.file.level * 16}px`, @@ -53,10 +47,9 @@ return this.file.type === 'blob' && this.file.opened ? 'file-open' : ''; }, changedClass() { - const tabChangedObj = { + return { 'fa-circle unsaved-icon': this.file.changed || this.file.tempFile, }; - return tabChangedObj; }, }, methods: { @@ -91,7 +84,7 @@ </a> <new-dropdown v-if="isTree" - :projectId="file.projectId" + :project-id="file.projectId" :branch="file.branchId" :path="file.path" :parent="file"/> @@ -114,7 +107,7 @@ </template> </td> - <template v-if="false == true"> + <template v-if="showExtraColumns"> <td class="multi-file-table-col-commit-message hidden-sm hidden-xs"> <a v-if="file.lastCommit.message" diff --git a/app/assets/javascripts/ide/components/repo_file_buttons.vue b/app/assets/javascripts/ide/components/repo_file_buttons.vue index b33103cb47a..34f0d51819a 100644 --- a/app/assets/javascripts/ide/components/repo_file_buttons.vue +++ b/app/assets/javascripts/ide/components/repo_file_buttons.vue @@ -24,35 +24,33 @@ export default { v-if="showButtons" class="multi-file-editor-btn-group" > - <div class="col-sm-12"> + <a + :href="activeFile.rawPath" + target="_blank" + class="btn btn-default btn-sm raw" + rel="noopener noreferrer"> + {{ rawDownloadButtonLabel }} + </a> + + <div + class="btn-group" + role="group" + aria-label="File actions"> <a - :href="activeFile.rawPath" - target="_blank" - class="btn btn-default btn-sm raw" - rel="noopener noreferrer"> - {{ rawDownloadButtonLabel }} + :href="activeFile.blamePath" + class="btn btn-default btn-sm blame"> + Blame + </a> + <a + :href="activeFile.commitsPath" + class="btn btn-default btn-sm history"> + History + </a> + <a + :href="activeFile.permalink" + class="btn btn-default btn-sm permalink"> + Permalink </a> - - <div - class="btn-group" - role="group" - aria-label="File actions"> - <a - :href="activeFile.blamePath" - class="btn btn-default btn-sm blame"> - Blame - </a> - <a - :href="activeFile.commitsPath" - class="btn btn-default btn-sm history"> - History - </a> - <a - :href="activeFile.permalink" - class="btn btn-default btn-sm permalink"> - Permalink - </a> - </div> </div> </div> </template> diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js index 8aaa168c9da..a3e833bf02d 100644 --- a/app/assets/javascripts/ide/ide_router.js +++ b/app/assets/javascripts/ide/ide_router.js @@ -1,6 +1,10 @@ import Vue from 'vue'; import VueRouter from 'vue-router'; import store from './stores'; +import flash from '../flash'; +import { + getTreeEntry, +} from './stores/utils'; Vue.use(VueRouter); @@ -24,25 +28,27 @@ Vue.use(VueRouter); // Unfortunately Vue Router doesn't work without at least a fake component // If you do only data handling -const FooRouterComponent = { - template: '<div>foo</div>', +const EmptyRouterComponent = { + render(createElement) { + return createElement('div'); + }, }; const router = new VueRouter({ mode: 'history', - base: '/-/ide/', + base: `${gon.relative_url_root}/-/ide/`, routes: [ { path: '/project/:namespace/:project', - component: FooRouterComponent, + component: EmptyRouterComponent, children: [ { path: ':targetmode/:branch/*', - component: FooRouterComponent, + component: EmptyRouterComponent, }, { path: 'mr/:mrid', - component: FooRouterComponent, + component: EmptyRouterComponent, }, ], }, @@ -66,22 +72,24 @@ router.beforeEach((to, from, next) => { store.dispatch('getTreeData', { projectId: fullProjectId, branch: to.params.branch, - endpoint: `/${fullProjectId}/tree/${to.params.branch}`, + endpoint: `/tree/${to.params.branch}`, }) .then(() => { if (to.params[0]) { - const treeEntry = store.getters.getTreeEntry(`${to.params.namespace}/${to.params.project}/${to.params.branch}`, to.params[0]); + const treeEntry = getTreeEntry(store, `${to.params.namespace}/${to.params.project}/${to.params.branch}`, to.params[0]); if (treeEntry) { store.dispatch('handleTreeEntryAction', treeEntry); } } }) .catch((e) => { + flash('Error while loading the branch files. Please try again.'); throw e; }); } }) .catch((e) => { + flash('Error while loading the project data. Please try again.'); throw e; }); next(); diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js index 98954232036..74ee2f14599 100644 --- a/app/assets/javascripts/ide/index.js +++ b/app/assets/javascripts/ide/index.js @@ -104,7 +104,3 @@ initNewDropdown(newDropdownHolder); const contextualSidebar = new ContextualSidebar(); contextualSidebar.bindEvents(); - -/* global Sidebar */ -// eslint-disable-next-line no-new -new Sidebar(); diff --git a/app/assets/javascripts/ide/lib/common/model.js b/app/assets/javascripts/ide/lib/common/model.js index 46ff9beb4c7..71261189182 100644 --- a/app/assets/javascripts/ide/lib/common/model.js +++ b/app/assets/javascripts/ide/lib/common/model.js @@ -32,7 +32,7 @@ export default class Model { return this.model.getModeId(); } - get EOL() { + get eol() { return encodeURI(this.model.getEOL()) === '%0A' ? 'LF' : 'CRLF'; } diff --git a/app/assets/javascripts/ide/lib/editor.js b/app/assets/javascripts/ide/lib/editor.js index 3e762a5c4de..51e202b9348 100644 --- a/app/assets/javascripts/ide/lib/editor.js +++ b/app/assets/javascripts/ide/lib/editor.js @@ -23,10 +23,10 @@ export default class Editor { this.decorationsController = new DecorationsController(this), ); - const debouncedUpdate = _.debounce(() => { + this.debouncedUpdate = _.debounce(() => { this.updateDimensions(); }, 200); - window.addEventListener('resize', debouncedUpdate, false); + window.addEventListener('resize', this.debouncedUpdate, false); } createInstance(domElement) { @@ -78,7 +78,7 @@ export default class Editor { dispose() { this.disposable.dispose(); - window.removeEventListener('resize', this.updateDimensions.bind(this)); + window.removeEventListener('resize', this.debouncedUpdate); // dispose main monaco instance if (this.instance) { diff --git a/app/assets/javascripts/ide/stores/actions/branch.js b/app/assets/javascripts/ide/stores/actions/branch.js index f3e74fd7744..0b6968e0973 100644 --- a/app/assets/javascripts/ide/stores/actions/branch.js +++ b/app/assets/javascripts/ide/stores/actions/branch.js @@ -2,18 +2,18 @@ import service from '../../services'; import flash from '../../../flash'; import * as types from '../mutation_types'; -// eslint-disable-next-line import/prefer-default-export export const getBranchData = ( { commit, state, dispatch }, - { projectId, branchId, enforce = false } = {}, + { projectId, branchId, force = false } = {}, ) => new Promise((resolve, reject) => { if ((typeof state.projects[`${projectId}`] === 'undefined' || !state.projects[`${projectId}`].branches[branchId]) - || enforce) { + || force) { service.getBranchData(`${projectId}`, branchId) .then((data) => { + const { id } = data.commit; commit(types.SET_BRANCH, { projectPath: `${projectId}`, branchName: branchId, branch: data }); - dispatch('setBranchReference', { projectId, branchId }); + commit(types.SET_BRANCH_WORKING_REFERENCE, { projectId, branchId, reference: id }); resolve(data); }) .catch(() => { @@ -25,18 +25,6 @@ export const getBranchData = ( } }); -export const setBranchReference = ({ commit, state }, { projectId, branchId }) => - service.getBranchData( - projectId, - branchId, - ) - .then((data) => { - const { id } = data.commit; - commit(types.SET_BRANCH_WORKING_REFERENCE, { projectId, branchId, reference: id }); - }) - .catch(() => flash('Error checking branch data. Please try again.')); - -// eslint-disable-next-line import/prefer-default-export export const createNewBranch = ({ state, commit }, branch) => service.createBranch( state.project.id, { diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js index 7055c81edac..4d4e792a466 100644 --- a/app/assets/javascripts/ide/stores/actions/file.js +++ b/app/assets/javascripts/ide/stores/actions/file.js @@ -87,16 +87,19 @@ export const setFileLanguage = ({ commit }, { file, fileLanguage }) => { commit(types.SET_FILE_LANGUAGE, { file, fileLanguage }); }; -export const setFileEOL = ({ commit }, { file, EOL }) => { - commit(types.SET_FILE_EOL, { file, EOL }); +export const setFileEOL = ({ commit }, { file, eol }) => { + commit(types.SET_FILE_EOL, { file, eol }); }; -export const setEditorPosition = ({ commit }, { file, editorRow, editorColumn }) => { - commit(types.SET_FILE_POSITION, { file, editorRow, editorColumn }); +export const setEditorPosition = ({ state, commit }, { editorRow, editorColumn }) => { + commit(types.SET_FILE_POSITION, { file: state.selectedFile, editorRow, editorColumn }); }; export const createTempFile = ({ state, commit, dispatch }, { projectId, branchId, parent, name, content = '', base64 = '' }) => { const path = parent.path !== undefined ? parent.path : ''; + const selectedProject = state.projects[projectId]; + // We need to do the replacement otherwise the web_url + file.url duplicate + const newUrl = `${selectedProject.web_url}/blob/${branchId}/${path}${path ? '/' : ''}${name}`; const file = createTemp({ projectId, branchId, @@ -107,7 +110,7 @@ export const createTempFile = ({ state, commit, dispatch }, { projectId, branchI changed: true, content, base64, - url: `/${projectId}/blob/${branchId}/${path}${path ? '/' : ''}${name}`, + url: newUrl, }); if (findEntry(parent.tree, 'blob', file.name)) return flash(`The name "${file.name}" is already taken in this directory.`); diff --git a/app/assets/javascripts/ide/stores/actions/project.js b/app/assets/javascripts/ide/stores/actions/project.js index a0f092b4f97..3832f7729a2 100644 --- a/app/assets/javascripts/ide/stores/actions/project.js +++ b/app/assets/javascripts/ide/stores/actions/project.js @@ -2,12 +2,11 @@ import service from '../../services'; import flash from '../../../flash'; import * as types from '../mutation_types'; -// eslint-disable-next-line import/prefer-default-export export const getProjectData = ( { commit, state, dispatch }, - { namespace, projectId, enforce = false } = {}, + { namespace, projectId, force = false } = {}, ) => new Promise((resolve, reject) => { - if (!state.projects[`${namespace}/${projectId}`] || enforce) { + if (!state.projects[`${namespace}/${projectId}`] || force) { service.getProjectData(namespace, projectId) .then(res => res.data) .then((data) => { diff --git a/app/assets/javascripts/ide/stores/actions/tree.js b/app/assets/javascripts/ide/stores/actions/tree.js index 2cc0f25fe83..2f879fe00c0 100644 --- a/app/assets/javascripts/ide/stores/actions/tree.js +++ b/app/assets/javascripts/ide/stores/actions/tree.js @@ -12,14 +12,17 @@ import { export const getTreeData = ( { commit, state, dispatch }, - { endpoint = state.endpoints.rootEndpoint, tree = null, projectId, branch } = {}, + { endpoint, tree = null, projectId, branch } = {}, ) => new Promise((resolve, reject) => { // We already have the base tree so we resolve immediately if (!tree && state.trees[`${projectId}/${branch}`]) { resolve(); } else { if (tree) commit(types.TOGGLE_LOADING, tree); - service.getTreeData(endpoint) + const selectedProject = state.projects[projectId]; + // We need to do the replacement otherwise the web_url + file.url duplicate + const completeEndpoint = selectedProject.web_url + (endpoint).replace(projectId, ''); + service.getTreeData(completeEndpoint) .then((res) => { const pageTitle = decodeURI(normalizeHeaders(res.headers)['PAGE-TITLE']); diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js index 309e411a0c9..dc731f22428 100644 --- a/app/assets/javascripts/ide/stores/getters.js +++ b/app/assets/javascripts/ide/stores/getters.js @@ -18,13 +18,6 @@ export const treeList = state => (treeId) => { return []; }; -export const getTree = state => (namespace, projectId, branch) => state.trees[`${namespace}/${projectId}/${branch}`]; - -export const getTreeEntry = (state, getters) => (treeId, path) => { - const fileList = getters.treeList(treeId); - return fileList.find(file => file.path === path); -}; - export const changedFiles = state => state.openFiles.filter(file => file.changed); export const activeFile = state => state.openFiles.find(file => file.active) || null; diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js index fe8d157dfde..5f3655b0092 100644 --- a/app/assets/javascripts/ide/stores/mutations/file.js +++ b/app/assets/javascripts/ide/stores/mutations/file.js @@ -51,9 +51,9 @@ export default { fileLanguage, }); }, - [types.SET_FILE_EOL](state, { file, EOL }) { + [types.SET_FILE_EOL](state, { file, eol }) { Object.assign(file, { - EOL, + eol, }); }, [types.SET_FILE_POSITION](state, { file, editorRow, editorColumn }) { diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js index c66c6e927e8..feb90aaec70 100644 --- a/app/assets/javascripts/ide/stores/state.js +++ b/app/assets/javascripts/ide/stores/state.js @@ -18,4 +18,6 @@ export default () => ({ parentTreeUrl: '', trees: {}, projects: {}, + leftBarCollapsed: false, + rightBarCollapsed: false, }); diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js index 522a233b8e1..2e1bcb3a64a 100644 --- a/app/assets/javascripts/ide/stores/utils.js +++ b/app/assets/javascripts/ide/stores/utils.js @@ -38,7 +38,7 @@ export const dataStructure = () => ({ editorRow: 1, editorColumn: 1, fileLanguage: '', - EOL: '', + eol: '', }); export const decorateData = (entity) => { @@ -87,6 +87,13 @@ export const decorateData = (entity) => { }; }; +export const getTree = state => (namespace, projectId, branch) => state.trees[`${namespace}/${projectId}/${branch}`]; + +export const getTreeEntry = (store, treeId, path) => { + const fileList = store.getters.treeList(treeId); + return fileList.find(file => file.path === path); +}; + export const findEntry = (tree, type, name) => tree.find( f => f.type === type && f.name === name, ); diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index b84d6c140be..1d6c7a5c472 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -219,6 +219,7 @@ $gl-input-padding: 10px; $gl-vert-padding: 6px; $gl-padding-top: 10px; $gl-sidebar-padding: 22px; +$gl-bar-padding: 3px; /* * Misc diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 393eeecd082..f4b1186139f 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -25,7 +25,6 @@ .ide-view { display: flex; height: calc(100vh - #{$header-height}); - margin-top: $header-height; color: $almost-black; border-top: 1px solid $white-dark; border-bottom: 1px solid $white-dark; @@ -52,7 +51,7 @@ text-overflow: ellipsis; } - i.unsaved-icon { + .unsaved-icon { color: $indigo-700; float: right; font-size: smaller; @@ -98,6 +97,7 @@ } .multi-file-table-col-commit-message { + white-space: nowrap; width: 50%; } @@ -160,20 +160,34 @@ height: 0; } +.blob-editor-container { + flex: 1; + height: 0; + display: flex; + flex-direction: column; + justify-content: center; + + .vertical-center { + min-height: auto; + } +} + .multi-file-editor-holder { height: 100%; } .multi-file-editor-btn-group { - padding: 3px 0; + padding: $gl-bar-padding $gl-padding; border-top: 1px solid $white-dark; border-bottom: 1px solid $white-dark; background: $white-light; } .ide-status-bar { - padding-bottom: 3px; + padding: $gl-bar-padding $gl-padding; background: $white-light; + display: flex; + justify-content: space-between; svg { vertical-align: middle; @@ -185,10 +199,6 @@ height: 100%; overflow: scroll; - .blob-viewer { - height: calc(100vh - 140px); - } - .file-content.code { display: flex; @@ -231,7 +241,6 @@ flex-direction: column; overflow: auto; border-bottom: solid 1px $gray-normal; - margin-bottom: 1px; } &.is-collapsed { @@ -255,29 +264,30 @@ .branch-container { border-left: 4px solid $indigo-700; margin-bottom: 2px; + } - .branch-header { - background: $white-dark; - display: flex; - - .branch-header-title { - flex: 1; - padding: 8px 16px; - color: $indigo-700; - font-weight: $gl-font-weight-bold; - - svg { - vertical-align: middle; - } - } + .branch-header { + background: $white-dark; + display: flex; + } - .branch-header-btns { - padding: 6px 16px; - } + .branch-header-title { + flex: 1; + padding: 8px 16px; + color: $indigo-700; + font-weight: $gl-font-weight-bold; + + svg { + vertical-align: middle; } } + .branch-header-btns { + padding: $gl-vert-padding $gl-padding; + } + .left-collapse-btn { + display: none; background: $gray-light; text-align: left; border-top: 1px solid $white-dark; @@ -415,14 +425,20 @@ } .ide-loading { - padding-top: 200px; - text-align: center; - font-size: 140%; + display: flex; + height: 100vh; + align-items: center; + justify-content: center; } -.repo-new-btn { - float: right; +.ide-empty-state { + display: flex; + height: 100vh; + align-items: center; + justify-content: center; +} +.repo-new-btn { .dropdown-menu { left: auto; right: 0; @@ -434,3 +450,8 @@ } } } + +.ide-flash-container.flash-container { + margin-top: $header-height; + margin-bottom: 0; +} diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 94c4a92f71c..74ebc016870 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -42,7 +42,7 @@ module BlobHelper end def ide_edit_path(project = @project, ref = @ref, path = @path, options = {}) - ide_path + '/project' + edit_path(project, ref, path, options) + "#{ide_path}/project#{edit_path(project, ref, path, options)}" end def ide_blob_link(project = @project, ref = @ref, path = @path, options = {}) @@ -52,13 +52,13 @@ module BlobHelper return unless blob && blob.readable_text? - common_classes = "btn js-edit-blob #{options[:extra_class]}" + common_classes = "btn js-edit-ide #{options[:extra_class]}" if !on_top_of_branch?(project, ref) - button_tag 'Multi edit', class: "#{common_classes} disabled has-tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' } + button_tag 'Multi Edit <span class="label label-primary">Beta</span>', class: "#{common_classes} disabled has-tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' } # This condition applies to anonymous or users who can edit directly elsif !current_user || (current_user && can_modify_blob?(blob, project, ref)) - link_to 'Multi Edit <span class="lable">Beta</span>', ide_edit_path(project, ref, path, options), class: "#{common_classes} btn-sm" + link_to 'Multi Edit <span class="label label-primary">Beta</span>'.html_safe, ide_edit_path(project, ref, path, options), class: "#{common_classes} btn-sm" elsif current_user && can?(current_user, :fork_project, project) continue_params = { to: ide_edit_path(project, ref, path, options), @@ -67,8 +67,8 @@ module BlobHelper } fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params) - button_tag 'Multi Edit A', - class: "#{common_classes} js-edit-blob-link-fork-toggler", + button_tag 'Multi Edit <span class="label label-primary">Beta</span>', + class: "#{common_classes}", data: { action: 'edit', fork_path: fork_path } end end diff --git a/app/views/ide/index.html.haml b/app/views/ide/index.html.haml index e14e32db6b6..902518da2e5 100644 --- a/app/views/ide/index.html.haml +++ b/app/views/ide/index.html.haml @@ -4,12 +4,12 @@ - page_title 'IDE' - content_for :page_specific_javascripts do - = webpack_bundle_tag 'blob' = webpack_bundle_tag 'common_vue' = webpack_bundle_tag 'ide' -#ide - #content-body.content - .ide-loading.clgray - .text-center= icon('spinner spin', class: 'js-source-loading') - IDE Loading ... +.ide-flash-container.flash-container + +#ide.ide-loading + .text-center + = icon('spinner spin 2x', 'aria-hidden' => 'true', 'aria-label' => 'Loading content…') + %h2.clgray IDE Loading ... diff --git a/app/views/shared/repo/_repo.html.haml b/app/views/shared/repo/_repo.html.haml index 87e8c416194..2ab3ef902a9 100644 --- a/app/views/shared/repo/_repo.html.haml +++ b/app/views/shared/repo/_repo.html.haml @@ -1,5 +1,5 @@ - @no_container = true; -#repo{ data: { root: @path.empty?.to_s, +#repoa{ data: { root: @path.empty?.to_s, root_url: project_tree_path(project), url: content_url, current_branch: @ref, |