Commit 4119e92b authored by Phil Hughes's avatar Phil Hughes

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
parent 46e0a66a
<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>
......
<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);
dispatch('getLastCommitData', 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,
});
......
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,
});
};
<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', () => {
......
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();
});
});
});
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