Commit 45b9d60b authored by Phil Hughes's avatar Phil Hughes

template improvements

[ci skip]
parent 3e2d0e1a
......@@ -49,7 +49,7 @@
<button
type="button"
class="btn btn-blank multi-file-discard-btn"
@click="discardFileChanges(file)"
@click="discardFileChanges(file.path)"
>
Discard
</button>
......
......@@ -69,6 +69,7 @@
/>
<repo-editor
class="multi-file-edit-pane-content"
:file="activeFile"
/>
<repo-file-buttons
:file="activeFile"
......
<script>
import { mapState } from 'vuex';
import { mapGetters, mapState } from 'vuex';
import icon from '~/vue_shared/components/icon.vue';
import panelResizer from '~/vue_shared/components/panel_resizer.vue';
import repoCommitSection from './repo_commit_section.vue';
......@@ -27,9 +27,9 @@
'changedFiles',
'rightPanelCollapsed',
]),
currentIcon() {
return this.rightPanelCollapsed ? 'angle-double-left' : 'angle-double-right';
},
...mapGetters([
'currentIcon',
]),
},
};
</script>
......
......@@ -40,8 +40,10 @@ export default {
/>
</div>
</div>
<div>
<repo-tree :tree-id="branch.treeId" />
</div>
<template v-if="branch.tree">
<repo-tree
:tree="branch.tree"
/>
</template>
</div>
</template>
<script>
import { mapState, mapGetters } from 'vuex';
import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
import repoFile from './repo_file.vue';
import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
import RepoFile from './repo_file.vue';
export default {
components: {
repoFile,
skeletonLoadingContainer,
RepoFile,
SkeletonLoadingContainer,
},
props: {
treeId: {
type: String,
tree: {
type: Object,
required: true,
},
},
computed: {
...mapState([
'trees',
]),
...mapGetters([
'treeList',
]),
selctedTree() {
return this.trees[this.treeId].tree;
},
showLoading() {
return !this.trees[this.treeId] || this.trees[this.treeId].loading;
},
},
};
</script>
<template>
<div
class="ide-file-list"
v-if="treeId"
>
<template v-if="showLoading">
<template v-if="tree.loading">
<div
class="multi-file-loading-container"
v-for="n in 3"
......@@ -46,7 +30,7 @@ export default {
</div>
</template>
<repo-file
v-for="file in treeList"
v-for="file in tree.tree"
:key="file.key"
:file="file"
/>
......
<script>
import { mapState } from 'vuex';
import { mapState, mapGetters } from 'vuex';
import icon from '~/vue_shared/components/icon.vue';
import panelResizer from '~/vue_shared/components/panel_resizer.vue';
import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
......@@ -17,7 +17,9 @@
computed: {
...mapState([
'loading',
'projects',
]),
...mapGetters([
'projectsWithTrees',
]),
},
};
......@@ -40,7 +42,7 @@
</div>
</template>
<project-tree
v-for="project in projects"
v-for="project in projectsWithTrees"
:key="project.id"
:project="project"
/>
......
......@@ -6,22 +6,24 @@ import monacoLoader from '../monaco_loader';
import Editor from '../lib/editor';
export default {
props: {
file: {
type: Object,
required: true,
},
},
computed: {
...mapGetters([
'activeFile',
]),
...mapState([
'leftPanelCollapsed',
'rightPanelCollapsed',
'panelResizing',
]),
shouldHideEditor() {
return this.activeFile && this.activeFile.binary && !this.activeFile.raw;
return this.file && this.file.binary && !this.file.raw;
},
},
watch: {
activeFile(oldVal, newVal) {
if (newVal.path !== this.activeFile.path) {
file(oldVal, newVal) {
if (newVal.path !== this.file.path) {
this.initMonaco();
}
},
......@@ -31,11 +33,6 @@ export default {
rightPanelCollapsed() {
this.editor.updateDimensions();
},
panelResizing(isResizing) {
if (isResizing === false) {
this.editor.updateDimensions();
}
},
},
beforeDestroy() {
this.editor.dispose();
......@@ -64,7 +61,7 @@ export default {
this.editor.clearEditor();
this.getRawFileData(this.activeFile)
this.getRawFileData(this.file)
.then(() => {
this.editor.createInstance(this.$refs.editor);
})
......@@ -75,9 +72,9 @@ export default {
});
},
setupEditor() {
if (!this.activeFile) return;
if (!this.file) return;
this.model = this.editor.createModel(this.activeFile);
this.model = this.editor.createModel(this.file);
this.editor.attachModel(this.model);
......@@ -101,8 +98,8 @@ export default {
});
this.editor.setPosition({
lineNumber: this.activeFile.editorRow,
column: this.activeFile.editorColumn,
lineNumber: this.file.editorRow,
column: this.file.editorColumn,
});
// Handle File Language
......
......@@ -3,9 +3,8 @@
import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
import fileIcon from '~/vue_shared/components/file_icon.vue';
import newDropdown from './new_dropdown/index.vue';
import fileStatusIcon from 'ee/ide/components/repo_file_status_icon.vue'; // eslint-disable-line import/first
import changedFileIcon from 'ee/ide/components/changed_file_icon.vue'; // eslint-disable-line import/first
import fileStatusIcon from './repo_file_status_icon.vue';
import changedFileIcon from './changed_file_icon.vue';
export default {
name: 'RepoFile',
......@@ -24,39 +23,25 @@
type: Object,
required: true,
},
showExtraColumns: {
type: Boolean,
default: false,
},
},
computed: {
isSubmodule() {
return this.file.type === 'submodule';
},
isTree() {
return this.file.type === 'tree';
},
levelIndentation() {
if (this.file.level > 0) {
return {
marginLeft: `${this.file.level * 16}px`,
};
}
return {};
isBlob() {
return this.file.type === 'blob';
},
shortId() {
return this.file.id.substr(0, 8);
levelIndentation() {
return {
marginLeft: `${this.file.level * 16}px`,
};
},
fileClass() {
if (this.file.type === 'blob') {
if (this.file.active) {
return 'file-open file-active';
}
return this.file.opened ? 'file-open' : '';
} else if (this.file.type === 'tree') {
return 'folder';
}
return '';
return {
'file-open': this.isBlob && this.file.opened,
'file-active': this.isBlob && this.file.active,
'folder': this.isTree,
};
},
},
updated() {
......@@ -88,21 +73,24 @@
<div
class="file-name"
@click="clickFile(file)"
role="button"
>
<a
<span
class="ide-file-name str-truncated"
:style="levelIndentation"
>
<file-icon
:file-name="file.name"
:loading="file.loading"
:folder="file.type === 'tree'"
:folder="isTree"
:opened="file.opened"
:style="levelIndentation"
:size="16"
/>
{{ file.name }}
<file-status-icon :file="file" />
</a>
<file-status-icon
:file="file"
/>
</span>
<new-dropdown
v-if="isTree"
:project-id="file.projectId"
......@@ -115,17 +103,6 @@
v-if="file.changed || file.tempFile"
class="prepend-top-5"
/>
<template v-if="isSubmodule && file.id">
@
<span class="commit-sha">
<a
@click.stop
:href="file.tree_url"
>
{{ shortId }}
</a>
</span>
</template>
</div>
</div>
<template v-if="file.opened">
......
......@@ -9,10 +9,10 @@ export const setInitialData = ({ commit }, data) =>
export const discardAllChanges = ({ state, commit, dispatch }) => {
state.changedFiles.forEach((file) => {
commit(types.DISCARD_FILE_CHANGES, file);
commit(types.DISCARD_FILE_CHANGES, file.path);
if (file.tempFile) {
dispatch('closeFile', file);
dispatch('closeFile', file.path);
}
});
......@@ -74,4 +74,3 @@ export const scrollToTab = () => {
export * from './actions/tree';
export * from './actions/file';
export * from './actions/project';
export * from './actions/branch';
import flash from '~/flash';
import service from '../../services';
import * as types from '../mutation_types';
export const getBranchData = (
{ commit, state, dispatch },
{ 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(() => {
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 createNewBranch = ({ state, commit }, branch) => service.createBranch(
state.currentProjectId,
{
branch,
ref: state.currentBranchId,
},
)
.then(res => res.json())
.then((data) => {
const branchName = data.name;
const url = location.href.replace(state.currentBranchId, branchName);
if (this.$router) this.$router.push(url);
commit(types.SET_CURRENT_BRANCH, branchName);
});
......@@ -42,9 +42,6 @@ export const setFileActive = ({ commit, state, getters, dispatch }, path) => {
commit(types.SET_FILE_ACTIVE, { path, active: true });
dispatch('scrollToTab');
// reset hash for line highlighting
location.hash = '';
commit(types.SET_CURRENT_PROJECT, file.projectId);
commit(types.SET_CURRENT_BRANCH, file.branchId);
};
......@@ -141,11 +138,13 @@ export const createTempFile = ({ state, commit, dispatch }, { projectId, branchI
return Promise.resolve(file);
};
export const discardFileChanges = ({ commit }, file) => {
commit(types.DISCARD_FILE_CHANGES, file);
commit(types.REMOVE_FILE_FROM_CHANGED, file);
export const discardFileChanges = ({ state, commit }, path) => {
const file = state.entries[path];
commit(types.DISCARD_FILE_CHANGES, path);
commit(types.REMOVE_FILE_FROM_CHANGED, path);
if (file.tempFile && file.opened) {
commit(types.TOGGLE_FILE_OPEN, file.path);
commit(types.TOGGLE_FILE_OPEN, path);
}
};
......@@ -2,7 +2,6 @@ import flash from '~/flash';
import service from '../../services';
import * as types from '../mutation_types';
// eslint-disable-next-line import/prefer-default-export
export const getProjectData = (
{ commit, state, dispatch },
{ namespace, projectId, force = false } = {},
......@@ -25,3 +24,26 @@ export const getProjectData = (
resolve(state.projects[`${namespace}/${projectId}`]);
}
});
export const getBranchData = (
{ commit, state, dispatch },
{ 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(() => {
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]);
}
});
......@@ -10,16 +10,13 @@ import {
} from '../utils';
import FilesDecoratorWorker from '../workers/files_decorator_worker';
export const toggleTreeOpen = ({ commit, dispatch }, { tree }) => {
commit(types.TOGGLE_TREE_OPEN, tree);
export const toggleTreeOpen = ({ commit, dispatch }, path) => {
commit(types.TOGGLE_TREE_OPEN, path);
};
export const handleTreeEntryAction = ({ commit, dispatch }, row) => {
if (row.type === 'tree') {
dispatch('toggleTreeOpen', {
endpoint: row.url,
tree: row,
});
dispatch('toggleTreeOpen', row.path);
} else if (row.type === 'submodule') {
commit(types.TOGGLE_LOADING, { entry: row });
visitUrl(row.url);
......@@ -115,7 +112,7 @@ export const getFiles = (
const selectedTree = state.trees[`${projectId}/${branchId}`];
commit(types.SET_ENTRIES, entries);
commit(types.SET_DIRECTORY_DATA, { tree: selectedTree, data: treeList });
commit(types.SET_DIRECTORY_DATA, { treePath: `${projectId}/${branchId}`, data: treeList });
commit(types.TOGGLE_LOADING, { entry: selectedTree, forceValue: false });
worker.terminate();
......
......@@ -11,10 +11,21 @@ export const addedFiles = state => state.changedFiles.filter(f => f.tempFile);
export const modifiedFiles = state => state.changedFiles.filter(f => !f.tempFile);
export const treeList = (state) => {
const tree = state.trees[`${state.currentProjectId}/master`];
export const projectsWithTrees = state => Object.keys(state.projects).map((projectId) => {
const project = state.projects[projectId];
if (!tree) return [];
return {
...project,
branches: Object.keys(project.branches).map((branchId) => {
const branch = project.branches[branchId];
return tree.tree;
};
return {
...branch,
tree: state.trees[branch.treeId],
};
}),
};
});
export const currentIcon = state =>
(state.rightPanelCollapsed ? 'angle-double-left' : 'angle-double-right');
......@@ -7,16 +7,14 @@ export default {
});
},
[types.SET_BRANCH](state, { projectPath, branchName, branch }) {
// Add client side properties
Object.assign(branch, {
treeId: `${projectPath}/${branchName}`,
active: true,
workingReference: '',
});
Object.assign(state.projects[projectPath], {
branches: {
[branchName]: branch,
[branchName]: {
...branch,
treeId: `${projectPath}/${branchName}`,
active: true,
workingReference: '',
},
},
});
},
......
......@@ -59,9 +59,9 @@ export default {
editorColumn,
});
},
[types.DISCARD_FILE_CHANGES](state, file) {
Object.assign(state.entries[file.path], {
content: state.entries[file.path].raw,
[types.DISCARD_FILE_CHANGES](state, path) {
Object.assign(state.entries[path], {
content: state.entries[path].raw,
changed: false,
});
},
......
import * as types from '../mutation_types';
export default {
[types.TOGGLE_TREE_OPEN](state, tree) {
Object.assign(state.entries[tree.path], {
opened: !state.entries[tree.path].opened,
[types.TOGGLE_TREE_OPEN](state, path) {
Object.assign(state.entries[path], {
opened: !state.entries[path].opened,
});
},
[types.CREATE_TREE](state, { treePath }) {
......@@ -16,9 +16,13 @@ export default {
}),
});
},
[types.SET_DIRECTORY_DATA](state, { data, tree }) {
Object.assign(tree, {
tree: data,
[types.SET_DIRECTORY_DATA](state, { data, treePath }) {
Object.assign(state, {
trees: Object.assign(state.trees, {
[treePath]: {
tree: data,
},
}),
});
},
[types.SET_LAST_COMMIT_URL](state, { tree = state, url }) {
......
......@@ -27,6 +27,7 @@ self.addEventListener('message', (e) => {
url: `/${projectId}/tree/${branchId}/${folderPath}`,
level: parentFolder ? parentFolder.level + 1 : folderLevel,
type: 'tree',
parentTreeUrl: parentFolder ? parentFolder.url : `/${projectId}/tree/${branchId}/`,
});
Object.assign(acc, {
......@@ -58,6 +59,7 @@ self.addEventListener('message', (e) => {
url: `/${projectId}/blob/${branchId}/${path}`,
level: fileFolder ? fileFolder.level + 1 : 0,
type: 'blob',
parentTreeUrl: fileFolder ? fileFolder.url : `/${projectId}/blob/${branchId}`,
});
Object.assign(acc, {
......
import store from 'ee/ide/stores';
import service from 'ee/ide/services';
import { resetStore } from '../../helpers';
describe('Multi-file store branch actions', () => {
afterEach(() => {
resetStore(store);
});
describe('createNewBranch', () => {
beforeEach(() => {
spyOn(service, 'createBranch').and.returnValue(Promise.resolve({
json: () => ({
name: 'testing',
}),
}));
spyOn(history, 'pushState');
store.state.currentProjectId = 'abcproject';
store.state.currentBranchId = 'testing';
store.state.projects.abcproject = {
branches: {
master: {
workingReference: '1',
},
},
};
});
it('creates new branch', (done) => {
store.dispatch('createNewBranch', 'master')
.then(() => {
expect(store.state.currentBranchId).toBe('testing');
expect(service.createBranch).toHaveBeenCalledWith('abcproject', {
branch: 'master',
ref: 'testing',
});
done();
})
.catch(done.fail);
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment