Add promise-based event processesing for consistency

This commit is contained in:
gconsidine
2018-03-06 17:47:22 -05:00
committed by Jake McDermott
parent 3705169de0
commit 60a19246ae
3 changed files with 244 additions and 188 deletions

View File

@@ -10,6 +10,8 @@ let render;
let scroll; let scroll;
let resource; let resource;
let chain;
function JobsIndexController ( function JobsIndexController (
_resource_, _resource_,
_page_, _page_,
@@ -56,6 +58,7 @@ function JobsIndexController (
const stream = false; // TODO: Set in route const stream = false; // TODO: Set in route
chain = $q.resolve();
render.requestAnimationFrame(() => init()); render.requestAnimationFrame(() => init());
} }
@@ -81,37 +84,39 @@ function init (stream) {
} }
function process (scope, data) { function process (scope, data) {
if (data.event === JOB_START) { chain = chain.then(() => {
vm.stream.active = true; if (data.event === JOB_START) {
scroll.lock(); vm.stream.active = true;
} else if (data.event === JOB_END) { scroll.lock();
vm.stream.active = false; } else if (data.event === JOB_END) {
} vm.stream.active = false;
}
const pageAdded = page.addToBuffer(data); const pageAdded = page.addToBuffer(data);
if (pageAdded && !scroll.isLocked()) { if (pageAdded && !scroll.isLocked()) {
vm.stream.paused = true; vm.stream.paused = true;
} }
if (vm.stream.paused && scroll.isLocked()) { if (vm.stream.paused && scroll.isLocked()) {
vm.stream.paused = false; vm.stream.paused = false;
} }
if (vm.stream.rendering || vm.stream.paused) { if (vm.stream.rendering || vm.stream.paused) {
return; return;
} }
const events = page.emptyBuffer(); const events = page.emptyBuffer();
return renderStream(events); return renderStream(events);
})
} }
function renderStream (events) { function renderStream (events) {
vm.stream.rendering = true; vm.stream.rendering = true;
return shift() return shift()
.then(() => append(events)) .then(() => append(events, true))
.then(() => { .then(() => {
if (scroll.isLocked()) { if (scroll.isLocked()) {
scroll.setScrollPosition(scroll.getScrollHeight()); scroll.setScrollPosition(scroll.getScrollHeight());
@@ -135,6 +140,12 @@ function renderStream (events) {
function devClear () { function devClear () {
init(true); init(true);
render.clear(); render.clear();
vm.stream = {
active: false,
rendering: false,
paused: false
};
} }
function next () { function next () {
@@ -167,23 +178,22 @@ function previous () {
}) })
.then(() => { .then(() => {
const currentHeight = scroll.getScrollHeight(); const currentHeight = scroll.getScrollHeight();
scroll.setScrollPosition(currentHeight - postPopHeight + initialPosition); scroll.setScrollPosition(currentHeight - postPopHeight + initialPosition);
}); });
}); });
} }
function append (events) { function append (events, stream) {
return render.append(events) return render.append(events)
.then(count => { .then(count => {
page.updateLineCount('current', count); page.updateLineCount(count, stream);
}); });
} }
function prepend (events) { function prepend (events) {
return render.prepend(events) return render.prepend(events)
.then(count => { .then(count => {
page.updateLineCount('current', count); page.updateLineCount(count);
}); });
} }
@@ -192,7 +202,7 @@ function pop () {
return $q.resolve(); return $q.resolve();
} }
const lines = page.trim('right'); const lines = page.trim();
return render.pop(lines); return render.pop(lines);
} }
@@ -202,11 +212,71 @@ function shift () {
return $q.resolve(); return $q.resolve();
} }
const lines = page.trim('left'); const lines = page.trim(true);
return render.shift(lines); return render.shift(lines);
} }
function scrollHome () {
scroll.pause();
return page.first()
.then(events => {
if (!events) {
return;
}
return render.clear()
.then(() => prepend(events))
.then(() => {
scroll.resetScrollPosition();
scroll.resume();
});
});
}
function scrollEnd () {
if (scroll.isLocked()) {
page.setBookmark();
scroll.unlock();
return;
} else if (!scroll.isLocked() && vm.stream.active) {
page.removeBookmark();
scroll.lock();
return;
}
scroll.pause();
return page.last()
.then(events => {
if (!events) {
return;
}
return render.clear()
.then(() => append(events))
.then(() => {
scroll.setScrollPosition(scroll.getScrollHeight());
scroll.resume();
});
});
}
function scrollPageUp () {
scroll.pageUp();
}
function scrollPageDown () {
scroll.pageDown();
}
function scrollIsAtRest (isAtRest) {
vm.scroll.showBackToTop = !isAtRest;
}
function expand () { function expand () {
vm.toggle(parent, true); vm.toggle(parent, true);
} }
@@ -250,66 +320,6 @@ function toggle (uuid, menu) {
} }
} }
function scrollHome () {
scroll.pause();
return page.first()
.then(events => {
if (!events) {
return;
}
return render.clear()
.then(() => render.prepend(events))
.then(() => {
scroll.setScrollPosition(0);
scroll.resume();
});
});
}
function scrollEnd () {
if (scroll.isLocked()) {
page.bookmark();
scroll.unlock();
return;
} else if (!scroll.isLocked() && vm.stream.active) {
page.bookmark();
scroll.lock();
return;
}
scroll.pause();
return page.last()
.then(events => {
if (!events) {
return;
}
return render.clear()
.then(() => render.append(events))
.then(() => {
scroll.setScrollPosition(scroll.getScrollHeight());
scroll.resume();
});
});
}
function scrollPageUp () {
scroll.pageUp();
}
function scrollPageDown () {
scroll.pageDown();
}
function scrollIsAtRest (isAtRest) {
vm.scroll.showBackToTop = !isAtRest;
}
JobsIndexController.$inject = [ JobsIndexController.$inject = [
'resource', 'resource',
'JobPageService', 'JobPageService',

View File

@@ -5,12 +5,21 @@ function JobPageService ($q) {
this.page = { this.page = {
limit: this.resource.page.pageLimit, limit: this.resource.page.pageLimit,
size: this.resource.page.size, size: this.resource.page.size,
current: 0, cache: [],
index: -1, state: {
count: 0, count: 0,
first: 0, current: 0,
last: 0, first: 0,
bookmark: { last: 0
}
};
this.bookmark = {
pending: false,
set: false,
cache: [],
state: {
count: 0,
first: 0, first: 0,
last: 0, last: 0,
current: 0 current: 0
@@ -25,43 +34,45 @@ function JobPageService ($q) {
this.buffer = { this.buffer = {
count: 0 count: 0
}; };
this.cache = [];
}; };
this.add = (page, position, bookmark) => { this.addPage = (number, events, push, reference) => {
page.events = page.events || []; const page = { number, events, lines: 0 };
page.lines = page.lines || 0; reference = reference || this.getActiveReference();
if (position === 'first') { if (push) {
this.cache.unshift(page); reference.cache.push(page);
this.page.first = page.number; reference.state.last = page.number;
this.page.last = this.cache[this.cache.length -1].number; reference.state.first = reference.cache[0].number;
} else { } else {
this.cache.push(page); reference.cache.unshift(page);
this.page.last = page.number; reference.state.first = page.number;
this.page.first = this.cache[0].number; reference.state.last = reference.cache[reference.cache.length -1].number;
} }
if (bookmark) { reference.state.current = page.number;
this.page.bookmark.current = page.number; reference.state.count++;
} };
this.page.current = page.number; this.addToPageCache = (index, event, reference) => {
this.page.count++; reference.cache[index].events.push(event);
}; };
this.addToBuffer = event => { this.addToBuffer = event => {
const reference = this.getReference();
let pageAdded = false; let pageAdded = false;
if (this.result.count % this.page.size === 0) { if (this.result.count % this.page.size === 0) {
pageAdded = true; this.addPage(reference.state.current + 1, [event], true, reference);
this.add({ number: this.page.count + 1, events: [event] }); if (this.isBookmarkPending()) {
this.setBookmark();
}
this.trimBuffer(); this.trimBuffer();
pageAdded = true;
} else { } else {
this.cache[this.cache.length - 1].events.push(event); this.addToPageCache(reference.cache.length - 1, event, reference);
} }
this.buffer.count++; this.buffer.count++;
@@ -71,104 +82,127 @@ function JobPageService ($q) {
}; };
this.trimBuffer = () => { this.trimBuffer = () => {
const diff = this.cache.length - this.page.limit; const reference = this.getReference();
const diff = reference.cache.length - this.page.limit;
if (diff <= 0) { if (diff <= 0) {
return; return;
} }
for (let i = 0; i < diff; i++) { for (let i = 0; i < diff; i++) {
if (this.cache[i].events) { if (reference.cache[i].events) {
this.buffer.count -= this.cache[i].events.length; this.buffer.count -= reference.cache[i].events.length;
this.cache[i].events = []; reference.cache[i].events.splice(0, reference.cache[i].events.length);
} }
} }
}; };
this.emptyBuffer = () => { this.emptyBuffer = () => {
const reference = this.getReference();
let data = []; let data = [];
for (let i = 0; i < this.cache.length; i++) { for (let i = 0; i < reference.cache.length; i++) {
const events = this.cache[i].events; const count = reference.cache[i].events.length;
if (events.length > 0) { if (count > 0) {
this.buffer.count -= events.length; this.buffer.count -= count;
data = data.concat(this.cache[i].events.splice(0, events.length)); data = data.concat(reference.cache[i].events.splice(0, count));
} }
} }
return data; return data;
}; };
this.emptyCache = () => { this.emptyCache = number => {
this.page.first = this.page.current; const reference = this.getActiveReference();
this.page.last = this.page.current;
this.cache = []; number = number || reference.state.current;
reference.state.first = number;
reference.state.last = number;
reference.state.current = number;
reference.cache.splice(0, reference.cache.length);
}; };
this.isOverCapacity = () => { this.isOverCapacity = () => {
return (this.cache.length - this.page.limit) > 0; const reference = this.getActiveReference();
return (reference.cache.length - this.page.limit) > 0;
}; };
this.trim = side => { this.trim = left => {
const count = this.cache.length - this.page.limit; let reference = this.getActiveReference();
let excess = reference.cache.length - this.page.limit;
let ejected; let ejected;
if (side === 'left') { if (left) {
ejected = this.cache.splice(0, count); ejected = reference.cache.splice(0, excess);
this.page.first = this.cache[0].number; reference.state.first = reference.cache[0].number;
} else { } else {
ejected = this.cache.splice(-count); ejected = reference.cache.splice(-excess);
this.page.last = this.cache[this.cache.length - 1].number; reference.state.last = reference.cache[reference.cache.length - 1].number;
} }
return ejected.reduce((total, page) => total + page.lines, 0); return ejected.reduce((total, page) => total + page.lines, 0);
}; };
this.getPageNumber = page => { this.isPageBookmarked = number => {
let index; return number >= this.page.bookmark.first && number <= this.page.bookmark.last;
if (page === 'first') {
index = 0;
}
return this.cache[index].number;
}; };
this.updateLineCount = (page, lines) => { this.updateLineCount = (lines, stream) => {
let index; let reference;
if (page === 'current') { if (stream) {
index = this.cache.findIndex(item => item.number === this.page.current); reference = this.getReference();
} else {
reference = this.getActiveReference();
} }
this.cache[index].lines += lines; const index = reference.cache.findIndex(item => item.number === reference.state.current);
reference.cache[index].lines += lines;
} }
this.bookmark = () => { this.isBookmarkPending = () => {
if (!this.page.bookmark.active) { return this.bookmark.pending;
this.page.bookmark.first = this.page.first; };
this.page.bookmark.last = this.page.last;
this.page.bookmark.current = this.page.current; this.isBookmarkSet = () => {
this.page.bookmark.active = true; return this.bookmark.set;
} else { };
this.page.bookmark.active = false;
this.setBookmark = () => {
if (this.isBookmarkSet()) {
return;
} }
if (!this.isBookmarkPending()) {
this.bookmark.pending = true;
return;
}
this.bookmark.state.first = this.page.state.first;
this.bookmark.state.last = this.page.state.last;
this.bookmark.state.current = this.page.state.current;
this.bookmark.cache = JSON.parse(JSON.stringify(this.page.cache));
this.bookmark.set = true;
this.bookmark.pending = false;
};
this.removeBookmark = () => {
this.bookmark.set = false;
this.bookmark.pending = false;
this.bookmark.cache.splice(0, this.bookmark.cache.length);
this.bookmark.state.first = 0;
this.bookmark.state.last = 0;
this.bookmark.state.current = 0;
}; };
this.next = () => { this.next = () => {
let page; const reference = this.getActiveReference();
let bookmark; const config = this.buildRequestConfig(reference.state.last + 1);
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) return this.resource.model.goToPage(config)
.then(data => { .then(data => {
@@ -176,24 +210,15 @@ function JobPageService ($q) {
return $q.resolve(); return $q.resolve();
} }
this.add({ number: data.page, events: [], lines: 0 }, 'last', bookmark); this.addPage(data.page, [], true);
return data.results; return data.results;
}); });
}; };
this.previous = () => { this.previous = () => {
let page; const reference = this.getActiveReference();
let bookmark; const config = this.buildRequestConfig(reference.state.first - 1);
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) return this.resource.model.goToPage(config)
.then(data => { .then(data => {
@@ -201,7 +226,7 @@ function JobPageService ($q) {
return $q.resolve(); return $q.resolve();
} }
this.add({ number: data.page, events: [], lines: 0 }, 'first', bookmark); this.addPage(data.page, [], false);
return data.results; return data.results;
}); });
@@ -210,15 +235,14 @@ function JobPageService ($q) {
this.last = () => { this.last = () => {
const config = this.buildRequestConfig('last'); const config = this.buildRequestConfig('last');
this.emptyCache();
return this.resource.model.goToPage(config) return this.resource.model.goToPage(config)
.then(data => { .then(data => {
if (!data || !data.results) { if (!data || !data.results) {
return $q.resolve(); return $q.resolve();
} }
this.add({ number: data.page, events: [], lines: 0 }, 'last'); this.emptyCache(data.page);
this.addPage(data.page, [], true);
return data.results; return data.results;
}); });
@@ -227,23 +251,22 @@ function JobPageService ($q) {
this.first = () => { this.first = () => {
const config = this.buildRequestConfig('first'); const config = this.buildRequestConfig('first');
this.emptyCache();
return this.resource.model.goToPage(config) return this.resource.model.goToPage(config)
.then(data => { .then(data => {
if (!data || !data.results) { if (!data || !data.results) {
return $q.resolve(); return $q.resolve();
} }
this.add({ number: data.page, events: [], lines: 0 }, 'first'); this.emptyCache(data.page);
this.addPage(data.page, [], false);
return data.results; return data.results;
}); });
}; };
this.buildRequestConfig = (page) => { this.buildRequestConfig = number => {
return { return {
page, page: number,
related: this.resource.related, related: this.resource.related,
params: { params: {
order_by: 'start_line' order_by: 'start_line'
@@ -251,8 +274,24 @@ function JobPageService ($q) {
}; };
}; };
this.current = () => { this.getActiveReference = () => {
return this.resource.model.get(`related.${this.resource.related}.results`); return this.isBookmarkSet() ? this.getReference(true) : this.getReference();
};
this.getReference = (bookmark) => {
if (bookmark) {
return {
bookmark: true,
cache: this.bookmark.cache,
state: this.bookmark.state
};
}
return {
bookmark: false,
cache: this.page.cache,
state: this.page.state
};
}; };
} }

View File

@@ -130,6 +130,13 @@ function JobScrollService ($q, $timeout) {
this.isAtRest(); this.isAtRest();
}; };
this.resetScrollPosition = () => {
this.position.previous = 0;
this.position.current = 0;
this.el[0].scrollTop = 0;
this.isAtRest();
};
this.isAtRest = () => { this.isAtRest = () => {
if (this.position.current === 0 && !this.state.top) { if (this.position.current === 0 && !this.state.top) {
this.state.top = true; this.state.top = true;