Refactor page handling

This commit is contained in:
gconsidine
2018-03-01 09:15:43 -05:00
committed by Jake McDermott
parent df84f822f6
commit b16d9a89e3
3 changed files with 238 additions and 160 deletions

View File

@@ -16,11 +16,9 @@ let $q;
const record = {}; const record = {};
let parent = null; let parent = null;
let cache = [];
let buffer = [];
const SCROLL_BUFFER = 250; const SCROLL_THRESHOLD = 0.1;
const SCROLL_LOAD_DELAY = 50; const SCROLL_DELAY = 1000;
const EVENT_START_TASK = 'playbook_on_task_start'; const EVENT_START_TASK = 'playbook_on_task_start';
const EVENT_START_PLAY = 'playbook_on_play_start'; const EVENT_START_PLAY = 'playbook_on_play_start';
const EVENT_STATS_PLAY = 'playbook_on_stats'; const EVENT_STATS_PLAY = 'playbook_on_stats';
@@ -78,6 +76,8 @@ function JobsIndexController (
isLocked: false, isLocked: false,
showBackToTop: false, showBackToTop: false,
isActive: false, isActive: false,
position: 0,
time: 0,
home: scrollHome, home: scrollHome,
end: scrollEnd, end: scrollEnd,
down: scrollPageDown, down: scrollPageDown,
@@ -111,10 +111,6 @@ function JobsIndexController (
}); });
} }
// TODO: Determine how to manage buffered events (store in page cache vs. separate)
// Leaning towards keeping separate (same as they come in over WS). On resume of scroll,
// Clear/reset cache, append buffered events, then back to normal render cycle
function processWebSocketEvents (scope, data) { function processWebSocketEvents (scope, data) {
let done; let done;
@@ -173,74 +169,47 @@ function render (events) {
} }
function devClear () { function devClear () {
cache = [];
page.init(resource); page.init(resource);
clear(); clear();
} }
function next () { function next () {
const config = { return page.next()
related: resource.related, .then(events => {
page: vm.scroll.lastPage + 1, if (!events) {
params: { return;
order_by: 'start_line'
}
};
// console.log('[2] getting next page', config.page, cache);
return model.goToPage(config)
.then(data => {
if (!data || !data.results) {
return $q.resolve();
} }
cache.push({ page: data.page, events: [] });
vm.scroll.lastPage = data.page;
return shift() return shift()
.then(() => append(data.results)); .then(() => append(events));
}); })
} }
function prev () { function previous () {
const container = $(ELEMENT_CONTAINER)[0]; const container = $(ELEMENT_CONTAINER)[0];
const config = { let previousHeight;
related: resource.related,
page: vm.scroll.firstPage - 1,
params: {
order_by: 'start_line'
}
};
console.log(cache); return page.previous()
// console.log('[2] getting previous page', config.page, cache); .then(events => {
return model.goToPage(config) if (!events) {
.then(data => { return;
if (!data || !data.results) {
return $q.resolve();
} }
cache.unshift({ page: data.page, events: [] });
vm.scroll.firstPage = data.page;
const previousHeight = container.scrollHeight;
console.log(cache);
return pop() return pop()
.then(() => prepend(data.results)) .then(() => {
.then(lines => { previousHeight = container.scrollHeight;
const currentHeight = container.scrollHeight;
return prepend(events);
})
.then(() => {
const currentHeight = container.scrollHeight;
container.scrollTop = currentHeight - previousHeight; container.scrollTop = currentHeight - previousHeight;
}); });
}); });
} }
function append (events) { function append (events) {
// console.log('[4] appending next page');
return $q(resolve => { return $q(resolve => {
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
const parsed = parseEvents(events); const parsed = parseEvents(events);
@@ -258,62 +227,52 @@ function append (events) {
} }
function prepend (events) { function prepend (events) {
// console.log('[4] prepending next page');
return $q(resolve => { return $q(resolve => {
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
const parsed = parseEvents(events); const parsed = parseEvents(events);
const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parsed.html))); const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parsed.html)));
const table = $(ELEMENT_TBODY); const table = $(ELEMENT_TBODY);
cache[0].lines = parsed.lines; page.updateLineCount('current', parsed.lines);
table.prepend(rows); table.prepend(rows);
$compile(rows.contents())($scope); $compile(rows.contents())($scope);
return resolve(parsed.lines); $scope.$apply(() => {
return resolve(parsed.lines);
});
}); });
}); });
} }
function pop () { function pop () {
// console.log('[3] popping old page');
return $q(resolve => { return $q(resolve => {
if (cache.length <= resource.page.pageLimit) { if (!page.isOverCapacity()) {
// console.log('[3.1] nothing to pop');
return resolve(); return resolve();
} }
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
const ejected = cache.pop(); const lines = page.trim('right');
// console.log('[3.1] popping', ejected); const rows = $(ELEMENT_TBODY).children().slice(-lines);
const rows = $(ELEMENT_TBODY).children().slice(-ejected.lines);
vm.scroll.firstPage = cache[0].page;
rows.empty(); rows.empty();
rows.remove(); rows.remove();
return resolve(ejected); return resolve();
}); });
}); });
} }
function shift () { function shift () {
// console.log('[3] shifting old page', cache.length);
return $q(resolve => { return $q(resolve => {
if (!page.isOverCapacity()) { if (!page.isOverCapacity()) {
// console.log('[3.1] nothing to shift');
return resolve(); return resolve();
} }
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
const lines = page.trim(); const lines = page.trim('left');
//console.log('[3.1] shifting', lines);
const rows = $(ELEMENT_TBODY).children().slice(0, lines); const rows = $(ELEMENT_TBODY).children().slice(0, lines);
vm.scroll.firstPage = page.getPageNumber('first');
rows.empty(); rows.empty();
rows.remove(); rows.remove();
@@ -323,7 +282,6 @@ function shift () {
} }
function clear () { function clear () {
// console.log('[3] clearing pages');
return $q(resolve => { return $q(resolve => {
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
const rows = $(ELEMENT_TBODY).children(); const rows = $(ELEMENT_TBODY).children();
@@ -574,65 +532,71 @@ function onScroll () {
return; return;
} }
if (vm.scroll.register) {
$timeout.cancel(vm.scroll.register);
}
vm.scroll.register = $timeout(registerScrollEvent, SCROLL_DELAY);
}
function registerScrollEvent () {
vm.scroll.isActive = true; vm.scroll.isActive = true;
$timeout(() => { const position = container[0].scrollTop;
const top = container[0].scrollTop; const height = container[0].offsetHeight;
const bottom = top + SCROLL_BUFFER + container[0].offsetHeight; const downward = position > vm.scroll.position;
if (top <= SCROLL_BUFFER) { let promise;
// console.log('[1] scroll to top');
vm.scroll.showBackToTop = false;
prev() if (position !== 0 ) {
.then(() => { vm.scroll.showBackToTop = true;
// console.log('[5] scroll reset'); } else {
vm.scroll.isActive = false; vm.scroll.showBackToTop = false;
}); }
return;
} else {
vm.scroll.showBackToTop = true;
if (bottom >= container[0].scrollHeight) { console.log('downward', downward);
// console.log('[1] scroll to bottom'); if (downward) {
if (((height - position) / height) < SCROLL_THRESHOLD) {
next() promise = next;
.then(() => {
// console.log('[5] scroll reset');
vm.scroll.isActive = false;
});
} else {
vm.scroll.isActive = false;
}
} }
}, SCROLL_LOAD_DELAY); } else {
if ((position / height) < SCROLL_THRESHOLD) {
console.log('previous');
promise = previous;
}
}
vm.scroll.position = position;
if (!promise) {
vm.scroll.isActive = false;
return $q.resolve();
}
return promise()
.then(() => {
console.log('done');
vm.scroll.isActive = false;
/*
*$timeout(() => {
* vm.scroll.isActive = false;
*}, SCROLL_DELAY);
*/
});
} }
function scrollHome () { function scrollHome () {
const config = { return page.first()
related: resource.related, .then(events => {
page: 'first', if (!events) {
params: { return;
order_by: 'start_line'
}
};
vm.scroll.isActive = true;
// console.log('[2] getting first page', config.page, cache);
return model.goToPage(config)
.then(data => {
if (!data || !data.results) {
return $q.resolve();
} }
cache = [{
page: data.page
}]
return clear() return clear()
.then(() => prepend(data.results)) .then(() => prepend(events))
.then(() => { .then(() => {
vm.scroll.isActive = false; vm.scroll.isActive = false;
}); });
@@ -641,45 +605,31 @@ function scrollHome () {
function scrollEnd () { function scrollEnd () {
if (vm.scroll.isLocked) { if (vm.scroll.isLocked) {
// Make note of current page when unlocked -- keep buffered events for that page for page.bookmark();
// continuity
vm.scroll.firstPage = cache[0].page;
vm.scroll.lastPage = cache[cache.length - 1].page;
vm.scroll.isLocked = false; vm.scroll.isLocked = false;
vm.scroll.isActive = false; vm.scroll.isActive = false;
return; return;
} else if (!vm.scroll.isLocked && vm.stream.isActive) { } else if (!vm.scroll.isLocked && vm.stream.isActive) {
page.bookmark();
vm.scroll.isActive = true; vm.scroll.isActive = true;
vm.scroll.isLocked = true; vm.scroll.isLocked = true;
return; return;
} }
const config = {
related: resource.related,
page: 'last',
params: {
order_by: 'start_line'
}
};
vm.scroll.isActive = true; vm.scroll.isActive = true;
// console.log('[2] getting last page', config.page, cache); return page.last()
return model.goToPage(config) .then(events => {
.then(data => { if (!events) {
if (!data || !data.results) { return;
return $q.resolve();
} }
cache = [{
page: data.page
}]
return clear() return clear()
.then(() => append(data.results)) .then(() => append(events))
.then(() => { .then(() => {
const container = $(ELEMENT_CONTAINER)[0]; const container = $(ELEMENT_CONTAINER)[0];

View File

@@ -1,4 +1,4 @@
function JobPageService () { function JobPageService ($q) {
this.page = null; this.page = null;
this.resource = null; this.resource = null;
this.result = null; this.result = null;
@@ -13,7 +13,14 @@ function JobPageService () {
size: resource.page.size, size: resource.page.size,
current: 0, current: 0,
index: -1, index: -1,
count: 0 count: 0,
first: 0,
last: 0,
bookmark: {
first: 0,
last: 0,
current: 0
}
}; };
this.result = { this.result = {
@@ -28,14 +35,25 @@ function JobPageService () {
this.cache = []; this.cache = [];
}; };
this.add = (page, position) => { this.add = (page, position, bookmark) => {
page.events = page.events || []; page.events = page.events || [];
page.lines = page.lines || 0; page.lines = page.lines || 0;
if (!position) { if (position === 'first') {
this.cache.unshift(page);
this.page.first = page.number;
this.page.last = this.cache[this.cache.length -1].number;
} else {
this.cache.push(page); this.cache.push(page);
this.page.last = page.number;
this.page.first = this.cache[0].number;
} }
if (bookmark) {
this.page.bookmark.current = page.number;
}
this.page.current = page.number;
this.page.count++; this.page.count++;
}; };
@@ -88,19 +106,33 @@ function JobPageService () {
return data; return data;
}; };
this.emptyCache = () => {
this.page.first = this.page.current;
this.page.last = this.page.current;
this.cache = [];
};
this.isOverCapacity = () => { this.isOverCapacity = () => {
return (this.cache.length - this.page.limit) > 0; return (this.cache.length - this.page.limit) > 0;
}; };
this.trim = () => { this.trim = side => {
const count = this.cache.length - this.page.limit; const count = this.cache.length - this.page.limit;
const ejected = this.cache.splice(0, count);
const linesRemoved = ejected.reduce((total, page) => total + page.lines, 0);
return linesRemoved; let ejected;
if (side === 'left') {
ejected = this.cache.splice(0, count);
this.page.first = this.cache[0].number;
} else {
ejected = this.cache.splice(-count);
this.page.last = this.cache[this.cache.length - 1].number;
}
return ejected.reduce((total, page) => total + page.lines, 0);
}; };
this.getPageNumber = (page) => { this.getPageNumber = page => {
let index; let index;
if (page === 'first') { if (page === 'first') {
@@ -114,22 +146,118 @@ function JobPageService () {
let index; let index;
if (page === 'current') { if (page === 'current') {
index = this.cache.length - 1; index = this.cache.findIndex(item => item.number === this.page.current);
} }
if (this.cache[index].lines) { this.cache[index].lines += lines;
this.cache[index].lines += lines;
} else {
this.cache[index].lines = lines;
}
} }
this.next = () => { this.bookmark = () => {
console.log('b,current', this.page.current);
if (!this.page.bookmark.active) {
this.page.bookmark.first = this.page.first;
this.page.bookmark.last = this.page.last;
this.page.bookmark.current = this.page.current;
console.log('b,bookmark', this.page.bookmark.current);
this.page.bookmark.active = true;
} else {
this.page.bookmark.active = false;
}
}; };
this.prev = () => { this.next = () => {
let page;
let bookmark;
if (this.page.bookmark.active) {
page = this.page.bookmark.current + 1;
bookmark = true;
} else {
page = this.page.last + 1;
}
const config = this.buildRequestConfig(page);
return this.resource.model.goToPage(config)
.then(data => {
if (!data || !data.results) {
return $q.resolve();
}
this.add({ number: data.page, events: [], lines: 0 }, 'last', bookmark);
return data.results;
});
};
this.previous = () => {
let page;
let bookmark;
if (this.page.bookmark.active) {
page = this.page.bookmark.current - 1;
bookmark = true;
} else {
page = this.page.first - 1;
}
const config = this.buildRequestConfig(page);
return this.resource.model.goToPage(config)
.then(data => {
if (!data || !data.results) {
return $q.resolve();
}
this.add({ number: data.page, events: [], lines: 0 }, 'first', bookmark);
return data.results;
});
};
this.last = () => {
const config = this.buildRequestConfig('last');
this.emptyCache();
return this.resource.model.goToPage(config)
.then(data => {
if (!data || !data.results) {
return $q.resolve();
}
this.add({ number: data.page, events: [], lines: 0 }, 'last');
return data.results;
});
};
this.first = () => {
const config = this.buildRequestConfig('first');
this.emptyCache();
return this.resource.model.goToPage(config)
.then(data => {
if (!data || !data.results) {
return $q.resolve();
}
this.add({ number: data.page, events: [], lines: 0 }, 'first');
return data.results;
});
};
this.buildRequestConfig = (page) => {
return {
page,
related: this.resource.related,
params: {
order_by: 'start_line'
}
};
}; };
this.current = () => { this.current = () => {
@@ -137,4 +265,6 @@ function JobPageService () {
}; };
} }
JobPageService.$inject = ['$q'];
export default JobPageService; export default JobPageService;

View File

@@ -466,9 +466,7 @@ function goToPage (config) {
if (pagesInCache.length > this.page.limit) { if (pagesInCache.length > this.page.limit) {
const pageToDelete = pagesInCache.shift(); const pageToDelete = pagesInCache.shift();
console.log(pageCache);
delete pageCache[pageToDelete]; delete pageCache[pageToDelete];
console.log(this.page.cache);
} }
} }