Commit c8461327 authored by Sean Roberts's avatar Sean Roberts

Add invisible threads list to allow render time saving for out of viewport threads

parent 9d396d57
......@@ -33,6 +33,16 @@ function getThreadHeight(id) {
return elementHeight + marginHeight;
}
var virtualThreadOptions = {
// identify the thread types that need to be rendered
// but not actually visible to the user
invisibleThreadFilter: function(thread){
// new highlights should always get rendered so we don't
// miss saving them via the render-save process
return thread.annotation.$highlight && metadata.isNew(thread.annotation);
},
};
// @ngInject
function ThreadListController($scope, VirtualThreadList) {
// `visibleThreads` keeps track of the subset of all threads matching the
......@@ -40,10 +50,11 @@ function ThreadListController($scope, VirtualThreadList) {
// only those threads, using placeholders above and below the visible threads
// to reserve space for threads which are not actually rendered.
var self = this;
var visibleThreads = new VirtualThreadList($scope, window, this.thread);
var visibleThreads = new VirtualThreadList($scope, window, this.thread, virtualThreadOptions);
visibleThreads.on('changed', function (state) {
self.virtualThreadList = {
visibleThreads: state.visibleThreads,
invisibleThreads: state.invisibleThreads,
offscreenUpperHeight: state.offscreenUpperHeight + 'px',
offscreenLowerHeight: state.offscreenLowerHeight + 'px',
};
......
......@@ -14,6 +14,11 @@
on-force-visible="vm.onForceVisible({thread: thread})">
</annotation-thread>
</li>
<li id="{{child.id}}"
ng-show="false"
ng-repeat="child in vm.virtualThreadList.invisibleThreads track by child.id">
<annotation-thread thread="child" />
</li>
<li class="thread-list__spacer"
ng-style="{height: vm.virtualThreadList.offscreenLowerHeight}"></li>
</ul>
......@@ -14,6 +14,9 @@ var unroll = util.unroll;
describe('VirtualThreadList', function () {
var lastState;
var threadList;
var threadOptions = {
invisibleThreadFilter: null,
};
var fakeScope;
var fakeWindow;
......@@ -62,8 +65,10 @@ describe('VirtualThreadList', function () {
pageYOffset: 0,
};
threadOptions.invisibleThreadFilter = sinon.stub().returns(false);
var rootThread = {annotation: undefined, children: []};
threadList = new VirtualThreadList(fakeScope, fakeWindow, rootThread);
threadList = new VirtualThreadList(fakeScope, fakeWindow, rootThread, threadOptions);
threadList.on('changed', function (state) {
lastState = state;
});
......@@ -75,10 +80,16 @@ describe('VirtualThreadList', function () {
fakeWindow.pageYOffset = testCase.scrollOffset;
fakeWindow.innerHeight = testCase.windowHeight;
// make sure for everything that is not being presented in the
// visible viewport, we pass it to this function.
threadOptions.invisibleThreadFilter.returns(true);
threadList.setRootThread(thread);
var visibleIDs = threadIDs(lastState.visibleThreads);
var invisibleIDs = threadIDs(lastState.invisibleThreads);
assert.deepEqual(visibleIDs, testCase.expectedVisibleThreads);
assert.equal(invisibleIDs.length, testCase.threads - testCase.expectedVisibleThreads.length);
assert.equal(lastState.offscreenUpperHeight, testCase.expectedHeightAbove);
assert.equal(lastState.offscreenLowerHeight, testCase.expectedHeightBelow);
},[{
......
......@@ -20,12 +20,18 @@ var inherits = require('inherits');
* @param {Window} container - The Window displaying the list of annotation threads.
* @param {Thread} rootThread - The initial Thread object for the top-level
* threads.
* @param {Object} options - The render-time options to help make final adjustments
* to what is and is not rendered.
* options.invisibleThreadFilter allows integrator to tell us what should be
* rerendered but not visible to the user yet.
*/
function VirtualThreadList($scope, window_, rootThread) {
function VirtualThreadList($scope, window_, rootThread, options) {
var self = this;
this._rootThread = rootThread;
this._options = Object.assign({}, options);
// Cache of thread ID -> last-seen height
this._heights = {};
......@@ -133,6 +139,12 @@ VirtualThreadList.prototype._updateVisibleThreads = function () {
// actually be created.
var visibleThreads = [];
// List of annotations which are required to be rendered but we do not
// want them visible. This is to ensure that we allow items to be rendered
// and initialized (for saving purposes) without having them be presented
// in out of context scenarios (i.e. in wrong order for sort)
var invisibleThreads = [];
var allThreads = this._rootThread.children;
var visibleHeight = this.window.innerHeight;
var usedHeight = 0;
......@@ -141,6 +153,7 @@ VirtualThreadList.prototype._updateVisibleThreads = function () {
for (var i = 0; i < allThreads.length; i++) {
thread = allThreads[i];
var threadHeight = this._height(thread.id);
var added = false;
if (usedHeight + threadHeight < this.window.pageYOffset - MARGIN_ABOVE) {
// Thread is above viewport
......@@ -148,12 +161,23 @@ VirtualThreadList.prototype._updateVisibleThreads = function () {
} else if (usedHeight <
this.window.pageYOffset + visibleHeight + MARGIN_BELOW) {
// Thread is either in or close to the viewport
visibleThreads.push(allThreads[i]);
visibleThreads.push(thread);
added = true;
} else {
// Thread is below viewport
offscreenLowerHeight += threadHeight;
}
// any thread that is not going to go through the render process
// because it is already outside of the viewport should be checked
// to see if it needs to be added as an invisible render. So it will
// be available to go through rendering but not visible to the user
if(!added &&
this._options.invisibleThreadFilter &&
this._options.invisibleThreadFilter(thread)){
invisibleThreads.push(thread);
}
usedHeight += threadHeight;
}
......@@ -161,6 +185,7 @@ VirtualThreadList.prototype._updateVisibleThreads = function () {
offscreenLowerHeight: offscreenLowerHeight,
offscreenUpperHeight: offscreenUpperHeight,
visibleThreads: visibleThreads,
invisibleThreads: invisibleThreads,
});
};
......
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