Commit 43a6b985 authored by Simon Knox's avatar Simon Knox

Merge branch '343000-lazy-load-first-batch' into 'master'

Request visible batch of commit data first when lazy loading

See merge request gitlab-org/gitlab!75609
parents 4ae8feda 6f5f68e2
......@@ -52,14 +52,9 @@ export const loadCommits = async (projectPath, path, ref, offset) => {
}
// We fetch in batches of 25, so this ensures we don't refetch
Array.from(Array(COMMIT_BATCH_SIZE)).forEach((_, i) => {
addRequestedOffset(offset - i);
addRequestedOffset(offset + i);
});
Array.from(Array(COMMIT_BATCH_SIZE)).forEach((_, i) => addRequestedOffset(offset + i));
// Since a user could scroll either up or down, we want to support lazy loading in both directions
const commitsBatchUp = await fetchData(projectPath, path, ref, offset - COMMIT_BATCH_SIZE);
const commitsBatchDown = await fetchData(projectPath, path, ref, offset);
const commits = await fetchData(projectPath, path, ref, offset);
return commitsBatchUp.concat(commitsBatchDown);
return commits;
};
......@@ -13,7 +13,7 @@ import {
import { escapeRegExp } from 'lodash';
import paginatedTreeQuery from 'shared_queries/repository/paginated_tree.query.graphql';
import { escapeFileUrl } from '~/lib/utils/url_utility';
import { TREE_PAGE_SIZE } from '~/repository/constants';
import { TREE_PAGE_SIZE, ROW_APPEAR_DELAY } from '~/repository/constants';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
......@@ -128,6 +128,7 @@ export default {
return {
commit: null,
hasRowAppeared: false,
delayedRowAppear: null,
};
},
computed: {
......@@ -202,14 +203,19 @@ export default {
rowAppeared() {
this.hasRowAppeared = true;
if (this.commitInfo) {
return;
}
if (this.glFeatures.lazyLoadCommits) {
this.$emit('row-appear', {
rowNumber: this.rowNumber,
hasCommit: Boolean(this.commitInfo),
});
this.delayedRowAppear = setTimeout(
() => this.$emit('row-appear', this.rowNumber),
ROW_APPEAR_DELAY,
);
}
},
rowDisappeared() {
clearTimeout(this.delayedRowAppear);
this.hasRowAppeared = false;
},
},
......
......@@ -3,7 +3,12 @@ import paginatedTreeQuery from 'shared_queries/repository/paginated_tree.query.g
import createFlash from '~/flash';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { __ } from '../../locale';
import { TREE_PAGE_SIZE, TREE_INITIAL_FETCH_COUNT, TREE_PAGE_LIMIT } from '../constants';
import {
TREE_PAGE_SIZE,
TREE_INITIAL_FETCH_COUNT,
TREE_PAGE_LIMIT,
COMMIT_BATCH_SIZE,
} from '../constants';
import getRefMixin from '../mixins/get_ref';
import projectPathQuery from '../queries/project_path.query.graphql';
import { readmeFile } from '../utils/readme';
......@@ -151,11 +156,19 @@ export default {
.concat(data.trees.pageInfo, data.submodules.pageInfo, data.blobs.pageInfo)
.find(({ hasNextPage }) => hasNextPage);
},
loadCommitData({ rowNumber = 0, hasCommit } = {}) {
if (!this.glFeatures.lazyLoadCommits || hasCommit || isRequested(rowNumber)) {
handleRowAppear(rowNumber) {
if (!this.glFeatures.lazyLoadCommits || isRequested(rowNumber)) {
return;
}
// Since a user could scroll either up or down, we want to support lazy loading in both directions
this.loadCommitData(rowNumber);
if (rowNumber - COMMIT_BATCH_SIZE >= 0) {
this.loadCommitData(rowNumber - COMMIT_BATCH_SIZE);
}
},
loadCommitData(rowNumber) {
loadCommits(this.projectPath, this.path, this.ref, rowNumber)
.then(this.setCommitData)
.catch(() => {});
......@@ -182,7 +195,7 @@ export default {
:has-more="hasShowMore"
:commits="commits"
@showMore="handleShowMore"
@row-appear="loadCommitData"
@row-appear="handleRowAppear"
/>
<file-preview v-if="readme" :blob="readme" />
</div>
......
......@@ -23,3 +23,5 @@ export const I18N_COMMIT_DATA_FETCH_ERROR = __('An error occurred while fetching
export const PDF_MAX_FILE_SIZE = 10000000; // 10 MB
export const PDF_MAX_PAGE_LIMIT = 50;
export const ROW_APPEAR_DELAY = 150;
......@@ -52,13 +52,6 @@ describe('commits service', () => {
expect(axios.get.mock.calls.length).toEqual(1);
});
it('calls axios get twice if an offset is larger than 25', async () => {
await requestCommits(100);
expect(axios.get.mock.calls[0][1]).toEqual({ params: { format: 'json', offset: 75 } });
expect(axios.get.mock.calls[1][1]).toEqual({ params: { format: 'json', offset: 100 } });
});
it('updates the list of requested offsets', async () => {
await requestCommits(200);
......
......@@ -4,6 +4,7 @@ import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import TableRow from '~/repository/components/table/row.vue';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import { FILE_SYMLINK_MODE } from '~/vue_shared/constants';
import { ROW_APPEAR_DELAY } from '~/repository/constants';
const COMMIT_MOCK = { lockLabel: 'Locked by Root', committedDate: '2019-01-01' };
......@@ -17,12 +18,12 @@ function factory(propsData = {}) {
vm = shallowMount(TableRow, {
propsData: {
commitInfo: COMMIT_MOCK,
...propsData,
name: propsData.path,
projectPath: 'gitlab-org/gitlab-ce',
url: `https://test.com`,
totalEntries: 10,
commitInfo: COMMIT_MOCK,
rowNumber: 123,
},
directives: {
......@@ -251,6 +252,8 @@ describe('Repository table row component', () => {
});
describe('row visibility', () => {
beforeAll(() => jest.useFakeTimers());
beforeEach(() => {
factory({
id: '1',
......@@ -258,18 +261,20 @@ describe('Repository table row component', () => {
path: 'test',
type: 'tree',
currentPath: '/',
commitInfo: null,
});
});
it('emits a `row-appear` event', () => {
afterAll(() => jest.useRealTimers());
it('emits a `row-appear` event', async () => {
findIntersectionObserver().vm.$emit('appear');
expect(vm.emitted('row-appear')).toEqual([
[
{
hasCommit: true,
rowNumber: 123,
},
],
]);
jest.runAllTimers();
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), ROW_APPEAR_DELAY);
expect(vm.emitted('row-appear')).toEqual([[123]]);
});
});
});
......@@ -190,14 +190,28 @@ describe('Repository table component', () => {
});
});
it('loads commit data when row-appear event is emitted', () => {
describe('commit data', () => {
const path = 'some/path';
const rowNumber = 1;
it('loads commit data for both top and bottom batches when row-appear event is emitted', () => {
const rowNumber = 50;
factory(path);
findFileTable().vm.$emit('row-appear', { hasCommit: false, rowNumber });
findFileTable().vm.$emit('row-appear', rowNumber);
expect(isRequested).toHaveBeenCalledWith(rowNumber);
expect(loadCommits).toHaveBeenCalledWith('', path, '', rowNumber);
expect(loadCommits.mock.calls).toEqual([
['', path, '', rowNumber],
['', path, '', rowNumber - 25],
]);
});
it('loads commit data once if rowNumber is zero', () => {
factory(path);
findFileTable().vm.$emit('row-appear', 0);
expect(loadCommits.mock.calls).toEqual([['', path, '', 0]]);
});
});
});
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