Fixed insertion of files

Removed "insert" flag, inserting is by default for FileList.add().
Added "animate" flag to FileList.add().
Added logic to correctly detect when to insert/append elements whenever
the insertion point is visible or not.
Fixed "render next page" logic to work correctly when many pages of
files have been added.
This commit is contained in:
Vincent Petry 2014-04-04 16:11:31 +02:00
parent f6586f6bdf
commit 2883f231d0
4 changed files with 169 additions and 40 deletions

View File

@ -606,7 +606,7 @@ OC.Upload = {
{dir:$('#dir').val(), filename:name}, {dir:$('#dir').val(), filename:name},
function(result) { function(result) {
if (result.status === 'success') { if (result.status === 'success') {
FileList.add(result.data, {hidden: hidden, insert: true}); FileList.add(result.data, {hidden: hidden, animate: true});
} else { } else {
OC.dialogs.alert(result.data.message, t('core', 'Could not create file')); OC.dialogs.alert(result.data.message, t('core', 'Could not create file'));
} }
@ -619,7 +619,7 @@ OC.Upload = {
{dir:$('#dir').val(), foldername:name}, {dir:$('#dir').val(), foldername:name},
function(result) { function(result) {
if (result.status === 'success') { if (result.status === 'success') {
FileList.add(result.data, {hidden: hidden, insert: true}); FileList.add(result.data, {hidden: hidden, animate: true});
} else { } else {
OC.dialogs.alert(result.data.message, t('core', 'Could not create folder')); OC.dialogs.alert(result.data.message, t('core', 'Could not create folder'));
} }
@ -657,7 +657,7 @@ OC.Upload = {
var file = data; var file = data;
$('#uploadprogressbar').fadeOut(); $('#uploadprogressbar').fadeOut();
FileList.add(file, {hidden: hidden, insert: true}); FileList.add(file, {hidden: hidden, animate: true});
}); });
eventSource.listen('error',function(error) { eventSource.listen('error',function(error) {
$('#uploadprogressbar').fadeOut(); $('#uploadprogressbar').fadeOut();

View File

@ -26,8 +26,6 @@ window.FileList = {
// number of files per page // number of files per page
pageSize: 20, pageSize: 20,
// zero based page number
pageNumber: 0,
totalPages: 0, totalPages: 0,
/** /**
@ -227,9 +225,6 @@ window.FileList = {
}, },
_onScroll: function(e) { _onScroll: function(e) {
if (this.pageNumber + 1 >= this.totalPages) {
return;
}
if ($(window).scrollTop() + $(window).height() > $(document).height() - 500) { if ($(window).scrollTop() + $(window).height() > $(document).height() - 500) {
this._nextPage(true); this._nextPage(true);
} }
@ -324,16 +319,17 @@ window.FileList = {
* @param animate true to animate the new elements * @param animate true to animate the new elements
*/ */
_nextPage: function(animate) { _nextPage: function(animate) {
var tr, index, count = this.pageSize, var index = this.$fileList.children().length,
count = this.pageSize,
tr,
newTrs = [], newTrs = [],
selected = this.isAllSelected(); selected = this.isAllSelected();
if (this.pageNumber + 1 >= this.totalPages) { if (index >= this.files.length) {
return; return;
} }
this.pageNumber++; this.pageNumber++;
index = this.pageNumber * this.pageSize;
while (count > 0 && index < this.files.length) { while (count > 0 && index < this.files.length) {
tr = this._renderRow(this.files[index], {updateSummary: false}); tr = this._renderRow(this.files[index], {updateSummary: false});
@ -343,7 +339,7 @@ window.FileList = {
tr.find('input:checkbox').prop('checked', true); tr.find('input:checkbox').prop('checked', true);
} }
if (animate) { if (animate) {
tr.addClass('appear transparent'); // TODO tr.addClass('appear transparent');
newTrs.push(tr); newTrs.push(tr);
} }
index++; index++;
@ -365,7 +361,7 @@ window.FileList = {
* This operation will rerender the list and update the summary. * This operation will rerender the list and update the summary.
* @param filesArray array of file data (map) * @param filesArray array of file data (map)
*/ */
setFiles:function(filesArray) { setFiles: function(filesArray) {
// detach to make adding multiple rows faster // detach to make adding multiple rows faster
this.files = filesArray; this.files = filesArray;
this.pageNumber = -1; this.pageNumber = -1;
@ -516,34 +512,57 @@ window.FileList = {
/** /**
* Adds an entry to the files array and also into the DOM * Adds an entry to the files array and also into the DOM
* in a sorted manner.
* *
* @param fileData map of file attributes * @param fileData map of file attributes
* @param options map of attributes: * @param options map of attributes:
* - "insert" true to insert in a sorted manner, false to append (default)
* - "updateSummary" true to update the summary after adding (default), false otherwise * - "updateSummary" true to update the summary after adding (default), false otherwise
* @return new tr element (not appended to the table) * @return new tr element (not appended to the table)
*/ */
add: function(fileData, options) { add: function(fileData, options) {
var index = -1; var index = -1;
var $tr = this._renderRow(fileData, options); var $tr;
var $rows;
var $insertionPoint;
options = options || {}; options = options || {};
this.isEmpty = false; // there are three situations to cover:
// 1) insertion point is visible on the current page
// 2) insertion point is on a not visible page (visible after scrolling)
// 3) insertion point is at the end of the list
if (options.insert) { $rows = this.$fileList.children();
index = this._findInsertionIndex(fileData); index = this._findInsertionIndex(fileData);
if (index < this.files.length) { if (index > this.files.length) {
this.files.splice(index, 0, fileData); index = this.files.length;
this.$fileList.children().eq(index).before($tr); }
} else {
else { $insertionPoint = $rows.eq(index);
this.files.push(fileData); }
// is the insertion point visible ?
if ($insertionPoint.length) {
// only render if it will really be inserted
$tr = this._renderRow(fileData, options);
$insertionPoint.before($tr);
}
else {
// if insertion point is after the last visible
// entry, append
if (index === $rows.length) {
$tr = this._renderRow(fileData, options);
this.$fileList.append($tr); this.$fileList.append($tr);
} }
} }
else {
this.files.push(fileData); this.isEmpty = false;
this.$fileList.append($tr); this.files.splice(index, 0, fileData);
if ($tr && options.animate) {
$tr.addClass('appear transparent');
window.setTimeout(function() {
$tr.removeClass('transparent');
});
} }
// defaults to true if not defined // defaults to true if not defined
@ -880,7 +899,7 @@ window.FileList = {
// reinsert row // reinsert row
FileList.files.splice(tr.index(), 1); FileList.files.splice(tr.index(), 1);
tr.remove(); tr.remove();
FileList.add(fileInfo, {insert: true}); FileList.add(fileInfo);
} }
}); });
} }
@ -1351,7 +1370,7 @@ $(document).ready(function() {
FileList.remove(file.name); FileList.remove(file.name);
// create new file context // create new file context
data.context = FileList.add(file, {insert: true}); data.context = FileList.add(file, {animate: true});
} }
} }
}); });

View File

@ -30,6 +30,7 @@ describe('FileActions tests', function() {
$body.append('<input type="hidden" id="permissions" value="31"></input>'); $body.append('<input type="hidden" id="permissions" value="31"></input>');
// dummy files table // dummy files table
$filesTable = $body.append('<table id="filestable"></table>'); $filesTable = $body.append('<table id="filestable"></table>');
FileList.files = [];
}); });
afterEach(function() { afterEach(function() {
$('#dir, #permissions, #filestable').remove(); $('#dir, #permissions, #filestable').remove();

View File

@ -236,7 +236,7 @@ describe('FileList tests', function() {
var $tr; var $tr;
var fileData = { var fileData = {
type: 'file', type: 'file',
name: 'P comes after O.txt' name: 'ZZZ.txt'
}; };
FileList.setFiles(testFiles); FileList.setFiles(testFiles);
$tr = FileList.add(fileData); $tr = FileList.add(fileData);
@ -245,7 +245,7 @@ describe('FileList tests', function() {
it('inserts files in a sorted manner when insert option is enabled', function() { it('inserts files in a sorted manner when insert option is enabled', function() {
var $tr; var $tr;
for (var i = 0; i < testFiles.length; i++) { for (var i = 0; i < testFiles.length; i++) {
FileList.add(testFiles[i], {insert: true}); FileList.add(testFiles[i]);
} }
expect(FileList.files[0].name).toEqual('somedir'); expect(FileList.files[0].name).toEqual('somedir');
expect(FileList.files[1].name).toEqual('One.txt'); expect(FileList.files[1].name).toEqual('One.txt');
@ -259,9 +259,9 @@ describe('FileList tests', function() {
name: 'P comes after O.txt' name: 'P comes after O.txt'
}; };
for (var i = 0; i < testFiles.length; i++) { for (var i = 0; i < testFiles.length; i++) {
FileList.add(testFiles[i], {insert: true}); FileList.add(testFiles[i]);
} }
$tr = FileList.add(fileData, {insert: true}); $tr = FileList.add(fileData);
// after "One.txt" // after "One.txt"
expect($tr.index()).toEqual(2); expect($tr.index()).toEqual(2);
expect(FileList.files[2]).toEqual(fileData); expect(FileList.files[2]).toEqual(fileData);
@ -273,9 +273,9 @@ describe('FileList tests', function() {
name: 'somedir2 comes after somedir' name: 'somedir2 comes after somedir'
}; };
for (var i = 0; i < testFiles.length; i++) { for (var i = 0; i < testFiles.length; i++) {
FileList.add(testFiles[i], {insert: true}); FileList.add(testFiles[i]);
} }
$tr = FileList.add(fileData, {insert: true}); $tr = FileList.add(fileData);
expect($tr.index()).toEqual(1); expect($tr.index()).toEqual(1);
expect(FileList.files[1]).toEqual(fileData); expect(FileList.files[1]).toEqual(fileData);
}); });
@ -286,9 +286,9 @@ describe('FileList tests', function() {
name: 'zzz.txt' name: 'zzz.txt'
}; };
for (var i = 0; i < testFiles.length; i++) { for (var i = 0; i < testFiles.length; i++) {
FileList.add(testFiles[i], {insert: true}); FileList.add(testFiles[i]);
} }
$tr = FileList.add(fileData, {insert: true}); $tr = FileList.add(fileData);
expect($tr.index()).toEqual(4); expect($tr.index()).toEqual(4);
expect(FileList.files[4]).toEqual(fileData); expect(FileList.files[4]).toEqual(fileData);
}); });
@ -428,7 +428,7 @@ describe('FileList tests', function() {
var $input, request; var $input, request;
for (var i = 0; i < testFiles.length; i++) { for (var i = 0; i < testFiles.length; i++) {
FileList.add(testFiles[i], {insert: true}); FileList.add(testFiles[i]);
} }
// trigger rename prompt // trigger rename prompt
@ -516,14 +516,12 @@ describe('FileList tests', function() {
}); });
describe('List rendering', function() { describe('List rendering', function() {
it('renders a list of files using add()', function() { it('renders a list of files using add()', function() {
var addSpy = sinon.spy(FileList, 'add');
expect(FileList.files.length).toEqual(0); expect(FileList.files.length).toEqual(0);
expect(FileList.files).toEqual([]); expect(FileList.files).toEqual([]);
FileList.setFiles(testFiles); FileList.setFiles(testFiles);
expect($('#fileList tr').length).toEqual(4); expect($('#fileList tr').length).toEqual(4);
expect(FileList.files.length).toEqual(4); expect(FileList.files.length).toEqual(4);
expect(FileList.files).toEqual(testFiles); expect(FileList.files).toEqual(testFiles);
addSpy.restore();
}); });
it('updates summary using the file sizes', function() { it('updates summary using the file sizes', function() {
var $summary; var $summary;
@ -593,6 +591,117 @@ describe('FileList tests', function() {
expect($summary.find('.info').text()).toEqual('0 folders and 1 file'); expect($summary.find('.info').text()).toEqual('0 folders and 1 file');
}); });
}); });
describe('Rendering next page on scroll', function() {
function generateFiles(startIndex, endIndex) {
var files = [];
var name;
for (var i = startIndex; i <= endIndex; i++) {
name = 'File with index ';
if (i < 10) {
// do not rely on localeCompare here
// and make the sorting predictable
// cross-browser
name += '0';
}
name += i + '.txt';
files.push({
id: i,
type: 'file',
name: name,
mimetype: 'text/plain',
size: i * 2,
etag: 'abc'
});
}
return files;
}
beforeEach(function() {
FileList.setFiles(generateFiles(0, 64));
});
it('renders only the first page', function() {
expect(FileList.files.length).toEqual(65);
expect($('#fileList tr').length).toEqual(20);
});
it('renders the second page when scrolling down (trigger nextPage)', function() {
// TODO: can't simulate scrolling here, so calling nextPage directly
FileList._nextPage(true);
expect($('#fileList tr').length).toEqual(40);
FileList._nextPage(true);
expect($('#fileList tr').length).toEqual(60);
FileList._nextPage(true);
expect($('#fileList tr').length).toEqual(65);
FileList._nextPage(true);
// stays at 65
expect($('#fileList tr').length).toEqual(65);
});
it('inserts into the DOM if insertion point is in the visible page ', function() {
FileList.add({
id: 2000,
type: 'file',
name: 'File with index 15b.txt'
});
expect($('#fileList tr').length).toEqual(21);
expect(FileList.findFileEl('File with index 15b.txt').index()).toEqual(16);
});
it('does not inserts into the DOM if insertion point is not the visible page ', function() {
FileList.add({
id: 2000,
type: 'file',
name: 'File with index 28b.txt'
});
expect($('#fileList tr').length).toEqual(20);
expect(FileList.findFileEl('File with index 28b.txt').length).toEqual(0);
FileList._nextPage(true);
expect($('#fileList tr').length).toEqual(40);
expect(FileList.findFileEl('File with index 28b.txt').index()).toEqual(29);
});
it('appends into the DOM when inserting a file after the last visible element', function() {
FileList.add({
id: 2000,
type: 'file',
name: 'File with index 19b.txt'
});
expect($('#fileList tr').length).toEqual(21);
FileList._nextPage(true);
expect($('#fileList tr').length).toEqual(41);
});
it('appends into the DOM when inserting a file on the last page when visible', function() {
FileList._nextPage(true);
expect($('#fileList tr').length).toEqual(40);
FileList._nextPage(true);
expect($('#fileList tr').length).toEqual(60);
FileList._nextPage(true);
expect($('#fileList tr').length).toEqual(65);
FileList._nextPage(true);
FileList.add({
id: 2000,
type: 'file',
name: 'File with index 88.txt'
});
expect($('#fileList tr').length).toEqual(66);
FileList._nextPage(true);
expect($('#fileList tr').length).toEqual(66);
});
it('shows additional page when appending a page of files and scrolling down', function() {
var newFiles = generateFiles(66, 81);
for (var i = 0; i < newFiles.length; i++) {
FileList.add(newFiles[i]);
}
expect($('#fileList tr').length).toEqual(20);
FileList._nextPage(true);
expect($('#fileList tr').length).toEqual(40);
FileList._nextPage(true);
expect($('#fileList tr').length).toEqual(60);
FileList._nextPage(true);
expect($('#fileList tr').length).toEqual(80);
FileList._nextPage(true);
expect($('#fileList tr').length).toEqual(81);
FileList._nextPage(true);
expect($('#fileList tr').length).toEqual(81);
});
});
describe('file previews', function() { describe('file previews', function() {
var previewLoadStub; var previewLoadStub;