correctly updates commit data after committing changes
also merges data in after a fetch to save overriting all data. changed the render keys to save a full re-render when any data changes
This commit is contained in:
parent
46e0a66aed
commit
4119e92be5
|
@ -1,11 +1,15 @@
|
|||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import timeAgoMixin from '../../vue_shared/mixins/timeago';
|
||||
import skeletonLoadingContainer from '../../vue_shared/components/skeleton_loading_container.vue';
|
||||
|
||||
export default {
|
||||
mixins: [
|
||||
timeAgoMixin,
|
||||
],
|
||||
components: {
|
||||
skeletonLoadingContainer,
|
||||
},
|
||||
props: {
|
||||
file: {
|
||||
type: Object,
|
||||
|
@ -87,17 +91,10 @@
|
|||
>
|
||||
{{ file.lastCommit.message }}
|
||||
</a>
|
||||
<div
|
||||
<skeleton-loading-container
|
||||
v-else
|
||||
class="animation-container animation-container-small"
|
||||
>
|
||||
<div
|
||||
v-for="n in 6"
|
||||
:key="n"
|
||||
:class="'skeleton-line-' + n"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
:small="true"
|
||||
/>
|
||||
</td>
|
||||
|
||||
<td class="commit-update hidden-xs text-right">
|
||||
|
@ -107,17 +104,11 @@
|
|||
>
|
||||
{{ timeFormated(file.lastCommit.updatedAt) }}
|
||||
</span>
|
||||
<div
|
||||
<skeleton-loading-container
|
||||
v-else
|
||||
class="animation-container animation-container-small animation-container-right"
|
||||
>
|
||||
<div
|
||||
v-for="n in 6"
|
||||
:key="n"
|
||||
:class="'skeleton-line-' + n"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
class="animation-container-right"
|
||||
:small="true"
|
||||
/>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import skeletonLoadingContainer from '../../vue_shared/components/skeleton_loading_container.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
skeletonLoadingContainer,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'isCollapsed',
|
||||
]),
|
||||
},
|
||||
methods: {
|
||||
lineOfCode(n) {
|
||||
return `skeleton-line-${n}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -21,36 +20,24 @@
|
|||
aria-label="Loading files"
|
||||
>
|
||||
<td>
|
||||
<div
|
||||
class="animation-container animation-container-small">
|
||||
<div
|
||||
v-for="n in 6"
|
||||
:key="n"
|
||||
:class="lineOfCode(n)">
|
||||
</div>
|
||||
</div>
|
||||
<skeleton-loading-container
|
||||
:small="true"
|
||||
/>
|
||||
</td>
|
||||
<template v-if="!isCollapsed">
|
||||
<td
|
||||
class="hidden-sm hidden-xs">
|
||||
<div class="animation-container animation-container-small">
|
||||
<div
|
||||
v-for="n in 6"
|
||||
:key="n"
|
||||
:class="lineOfCode(n)">
|
||||
</div>
|
||||
</div>
|
||||
<skeleton-loading-container
|
||||
:small="true"
|
||||
/>
|
||||
</td>
|
||||
|
||||
<td
|
||||
class="hidden-xs">
|
||||
<div class="animation-container animation-container-small animation-container-right">
|
||||
<div
|
||||
v-for="n in 6"
|
||||
:key="n"
|
||||
:class="lineOfCode(n)">
|
||||
</div>
|
||||
</div>
|
||||
<skeleton-loading-container
|
||||
class="animation-container-right"
|
||||
:small="true"
|
||||
/>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
|
|
|
@ -80,7 +80,7 @@ export default {
|
|||
/>
|
||||
<repo-file
|
||||
v-for="(file, index) in treeList"
|
||||
:key="index"
|
||||
:key="file.name + file.type"
|
||||
:file="file"
|
||||
/>
|
||||
</tbody>
|
||||
|
|
|
@ -64,7 +64,7 @@ export const checkCommitStatus = ({ state }) => service.getBranchData(
|
|||
})
|
||||
.catch(() => flash('Error checking branch data. Please try again.'));
|
||||
|
||||
export const commitChanges = ({ commit, state, dispatch }, { payload, newMr }) =>
|
||||
export const commitChanges = ({ commit, state, dispatch, getters }, { payload, newMr }) =>
|
||||
service.commit(state.project.id, payload)
|
||||
.then((data) => {
|
||||
const { branch } = payload;
|
||||
|
@ -73,12 +73,28 @@ export const commitChanges = ({ commit, state, dispatch }, { payload, newMr }) =
|
|||
return;
|
||||
}
|
||||
|
||||
const lastCommit = {
|
||||
commit_path: `${state.project.url}/commit/${data.id}`,
|
||||
commit: {
|
||||
message: data.message,
|
||||
authored_date: data.committed_date,
|
||||
},
|
||||
};
|
||||
|
||||
flash(`Your changes have been committed. Commit ${data.short_id} with ${data.stats.additions} additions, ${data.stats.deletions} deletions.`, 'notice');
|
||||
|
||||
if (newMr) {
|
||||
redirectToUrl(`${state.endpoints.newMergeRequestUrl}${branch}`);
|
||||
} else {
|
||||
commit(types.SET_COMMIT_REF, data.id);
|
||||
|
||||
getters.changedFiles.forEach((entry) => {
|
||||
commit(types.SET_LAST_COMMIT_DATA, {
|
||||
entry,
|
||||
lastCommit,
|
||||
});
|
||||
});
|
||||
|
||||
dispatch('discardAllChanges');
|
||||
dispatch('closeAllFiles');
|
||||
dispatch('toggleEditMode');
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
setPageTitle,
|
||||
findEntry,
|
||||
createTemp,
|
||||
createOrMergeEntry,
|
||||
} from '../utils';
|
||||
|
||||
export const getTreeData = (
|
||||
|
@ -24,15 +25,19 @@ export const getTreeData = (
|
|||
return res.json();
|
||||
})
|
||||
.then((data) => {
|
||||
const prevLastCommitPath = tree.lastCommitPath;
|
||||
if (!state.isInitialRoot) {
|
||||
commit(types.SET_ROOT, data.path === '/');
|
||||
}
|
||||
|
||||
commit(types.SET_DIRECTORY_DATA, { data, tree });
|
||||
dispatch('updateDirectoryData', { data, tree });
|
||||
commit(types.SET_PARENT_TREE_URL, data.parent_tree_url);
|
||||
commit(types.SET_LAST_COMMIT_URL, { tree, url: data.last_commit_path });
|
||||
commit(types.TOGGLE_LOADING, tree);
|
||||
|
||||
if (prevLastCommitPath !== null) {
|
||||
dispatch('getLastCommitData', tree);
|
||||
}
|
||||
|
||||
pushState(endpoint);
|
||||
})
|
||||
|
@ -50,7 +55,7 @@ export const toggleTreeOpen = ({ commit, dispatch }, { endpoint, tree }) => {
|
|||
pushState(tree.parentTreeUrl);
|
||||
|
||||
commit(types.SET_PREVIOUS_URL, tree.parentTreeUrl);
|
||||
commit(types.SET_DIRECTORY_DATA, { data, tree });
|
||||
dispatch('updateDirectoryData', { data, tree });
|
||||
} else {
|
||||
commit(types.SET_PREVIOUS_URL, endpoint);
|
||||
dispatch('getTreeData', { endpoint, tree });
|
||||
|
@ -112,11 +117,11 @@ export const createTempTree = ({ state, commit, dispatch }, name) => {
|
|||
};
|
||||
|
||||
export const getLastCommitData = ({ state, commit, dispatch, getters }, tree = state) => {
|
||||
if (tree.lastCommitPath === '' || getters.isCollapsed) return;
|
||||
if (tree.lastCommitPath === null || getters.isCollapsed) return;
|
||||
|
||||
service.getTreeLastCommit(tree.lastCommitPath)
|
||||
.then((res) => {
|
||||
const lastCommitPath = normalizeHeaders(res.headers)['LOG-URL'];
|
||||
const lastCommitPath = normalizeHeaders(res.headers)['MORE-LOGS-URL'] || null;
|
||||
|
||||
commit(types.SET_LAST_COMMIT_URL, { tree, url: lastCommitPath });
|
||||
|
||||
|
@ -135,3 +140,23 @@ export const getLastCommitData = ({ state, commit, dispatch, getters }, tree = s
|
|||
})
|
||||
.catch(() => flash('Error fetching log data.'));
|
||||
};
|
||||
|
||||
export const updateDirectoryData = ({ commit, state }, { data, tree }) => {
|
||||
const level = tree.level !== undefined ? tree.level + 1 : 0;
|
||||
const parentTreeUrl = data.parent_tree_url ? `${data.parent_tree_url}${data.path}` : state.endpoints.rootUrl;
|
||||
const createEntry = (entry, type) => createOrMergeEntry({
|
||||
tree,
|
||||
entry,
|
||||
level,
|
||||
type,
|
||||
parentTreeUrl,
|
||||
});
|
||||
|
||||
const formattedData = [
|
||||
...data.trees.map(t => createEntry(t, 'tree')),
|
||||
...data.submodules.map(m => createEntry(m, 'submodule')),
|
||||
...data.blobs.map(b => createEntry(b, 'blob')),
|
||||
];
|
||||
|
||||
commit(types.SET_DIRECTORY_DATA, { tree, data: formattedData });
|
||||
};
|
||||
|
|
|
@ -50,7 +50,7 @@ export default {
|
|||
},
|
||||
[types.SET_LAST_COMMIT_DATA](state, { entry, lastCommit }) {
|
||||
Object.assign(entry.lastCommit, {
|
||||
url: `${state.project.url}/commit/${lastCommit.commit.id}`,
|
||||
url: lastCommit.commit_path,
|
||||
message: lastCommit.commit.message,
|
||||
updatedAt: lastCommit.commit.authored_date,
|
||||
});
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import * as types from '../mutation_types';
|
||||
import * as utils from '../utils';
|
||||
|
||||
export default {
|
||||
[types.TOGGLE_TREE_OPEN](state, tree) {
|
||||
|
@ -8,30 +7,8 @@ export default {
|
|||
});
|
||||
},
|
||||
[types.SET_DIRECTORY_DATA](state, { data, tree }) {
|
||||
const level = tree.level !== undefined ? tree.level + 1 : 0;
|
||||
const parentTreeUrl = data.parent_tree_url ? `${data.parent_tree_url}${data.path}` : state.endpoints.rootUrl;
|
||||
|
||||
Object.assign(tree, {
|
||||
tree: [
|
||||
...data.trees.map(t => utils.decorateData({
|
||||
...t,
|
||||
type: 'tree',
|
||||
parentTreeUrl,
|
||||
level,
|
||||
})),
|
||||
...data.submodules.map(m => utils.decorateData({
|
||||
...m,
|
||||
type: 'submodule',
|
||||
parentTreeUrl,
|
||||
level,
|
||||
})),
|
||||
...data.blobs.map(b => utils.decorateData({
|
||||
...b,
|
||||
type: 'blob',
|
||||
parentTreeUrl,
|
||||
level,
|
||||
})),
|
||||
],
|
||||
tree: data,
|
||||
});
|
||||
},
|
||||
[types.SET_PARENT_TREE_URL](state, url) {
|
||||
|
|
|
@ -104,3 +104,22 @@ export const createTemp = ({ name, path, type, level, changed, content, base64 }
|
|||
renderError: base64,
|
||||
});
|
||||
};
|
||||
|
||||
export const createOrMergeEntry = ({ tree, entry, type, parentTreeUrl, level }) => {
|
||||
const found = findEntry(tree, type, entry.name);
|
||||
|
||||
if (found) {
|
||||
return Object.assign({}, found, {
|
||||
id: entry.id,
|
||||
url: entry.url,
|
||||
tempFile: false,
|
||||
});
|
||||
}
|
||||
|
||||
return decorateData({
|
||||
...entry,
|
||||
type,
|
||||
parentTreeUrl,
|
||||
level,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<script>
|
||||
export default {
|
||||
props: {
|
||||
small: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
lines: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 6,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="animation-container"
|
||||
:class="{
|
||||
'animation-container-small': small,
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-for="line in lines"
|
||||
:key="line"
|
||||
:class="'skeleton-line-' + line"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -59,7 +59,8 @@ class Projects::RefsController < Projects::ApplicationController
|
|||
{
|
||||
file_name: content.name,
|
||||
commit: last_commit,
|
||||
type: content.type
|
||||
type: content.type,
|
||||
commit_path: project_commit_path(@project, last_commit)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -72,7 +73,7 @@ class Projects::RefsController < Projects::ApplicationController
|
|||
respond_to do |format|
|
||||
format.html { render_404 }
|
||||
format.json do
|
||||
response.headers["Log-Url"] = @more_log_url
|
||||
response.headers["More-Logs-Url"] = @more_log_url
|
||||
|
||||
render json: @logs
|
||||
end
|
||||
|
|
|
@ -20,7 +20,7 @@ describe('RepoFile', () => {
|
|||
resetStore(vm.$store);
|
||||
});
|
||||
|
||||
it('renders link, icon, name and last commit details', () => {
|
||||
it('renders link, icon and name', () => {
|
||||
const RepoFile = Vue.extend(repoFile);
|
||||
vm = new RepoFile({
|
||||
store,
|
||||
|
@ -37,10 +37,9 @@ describe('RepoFile', () => {
|
|||
expect(vm.$el.querySelector(`.${vm.file.icon}`).style.marginLeft).toEqual('0px');
|
||||
expect(name.href).toMatch(`/${vm.file.url}`);
|
||||
expect(name.textContent.trim()).toEqual(vm.file.name);
|
||||
expect(vm.$el.querySelector('.commit-message').textContent.trim()).toBe(vm.file.lastCommit.message);
|
||||
expect(vm.$el.querySelector('.commit-update').textContent.trim()).toBe(updated);
|
||||
expect(fileIcon.classList.contains(vm.file.icon)).toBeTruthy();
|
||||
expect(fileIcon.style.marginLeft).toEqual(`${vm.file.level * 10}px`);
|
||||
expect(vm.$el.querySelectorAll('.animation-container').length).toBe(2);
|
||||
});
|
||||
|
||||
it('does render if hasFiles is true and is loading tree', () => {
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
import Vue from 'vue';
|
||||
import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
|
||||
import mountComponent from '../../helpers/vue_mount_component_helper';
|
||||
|
||||
describe('Skeleton loading container', () => {
|
||||
let vm;
|
||||
|
||||
beforeEach(() => {
|
||||
const component = Vue.extend(skeletonLoadingContainer);
|
||||
vm = mountComponent(component);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
});
|
||||
|
||||
it('renders 6 skeleton lines by default', () => {
|
||||
expect(vm.$el.querySelector('.skeleton-line-6')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('renders in full mode by default', () => {
|
||||
expect(vm.$el.classList.contains('animation-container-small')).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('small', () => {
|
||||
beforeEach((done) => {
|
||||
vm.small = true;
|
||||
|
||||
Vue.nextTick(done);
|
||||
});
|
||||
|
||||
it('renders in small mode', () => {
|
||||
expect(vm.$el.classList.contains('animation-container-small')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('lines', () => {
|
||||
beforeEach((done) => {
|
||||
vm.lines = 5;
|
||||
|
||||
Vue.nextTick(done);
|
||||
});
|
||||
|
||||
it('renders 5 lines', () => {
|
||||
expect(vm.$el.querySelector('.skeleton-line-5')).not.toBeNull();
|
||||
expect(vm.$el.querySelector('.skeleton-line-6')).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue