diff --git a/client/html/endless_pager.tpl b/client/html/endless_pager.tpl index 6870f9a5..4812f96d 100644 --- a/client/html/endless_pager.tpl +++ b/client/html/endless_pager.tpl @@ -1,5 +1,7 @@
+
+
diff --git a/client/js/views/endless_page_view.js b/client/js/views/endless_page_view.js index 9ed82fe4..93fb5200 100644 --- a/client/js/views/endless_page_view.js +++ b/client/js/views/endless_page_view.js @@ -6,6 +6,13 @@ const views = require('../util/views.js'); const holderTemplate = views.getTemplate('endless-pager'); const pageTemplate = views.getTemplate('endless-pager-page'); +function isScrolledIntoView(el) { + const elemTop = el.getBoundingClientRect().top; + const elemBottom = el.getBoundingClientRect().bottom; + const isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight); + return isVisible; +} + class EndlessPageView { constructor(ctx) { this._hostNode = document.getElementById('content-holder'); @@ -13,28 +20,35 @@ class EndlessPageView { } run(ctx) { + this._destroy(); + this._active = true; - this._working = 0; - this._init = false; + this._runningRequests = 0; + this._initialPageLoad = true; this.clearMessages(); views.emptyContent(this._pagesHolderNode); - this.threshold = window.innerHeight / 3; this.minOffsetShown = null; this.maxOffsetShown = null; this.totalRecords = null; this.currentOffset = 0; + this.defaultLimit = parseInt(ctx.parameters.limit || ctx.defaultLimit); - const offset = parseInt(ctx.parameters.offset || 0); - const limit = parseInt(ctx.parameters.limit || ctx.defaultLimit); - this._loadPage(ctx, offset, limit, true) + const initialOffset = parseInt(ctx.parameters.offset || 0); + this._loadPage(ctx, initialOffset, this.defaultLimit, true) .then(pageNode => { - if (offset !== 0) { + if (initialOffset !== 0) { pageNode.scrollIntoView(); } }); - this._probePageLoad(ctx); + + this._timeout = window.setInterval(() => { + window.requestAnimationFrame(() => { + this._probePageLoad(ctx); + this._syncUrl(ctx); + }); + }, 250); views.monitorNodeRemoval(this._pagesHolderNode, () => this._destroy()); } @@ -43,27 +57,24 @@ class EndlessPageView { return this._hostNode.querySelector('.page-header-holder'); } + get topPageGuardNode() { + return this._hostNode.querySelector('.page-guard.top'); + } + + get bottomPageGuardNode() { + return this._hostNode.querySelector('.page-guard.bottom'); + } + get _pagesHolderNode() { return this._hostNode.querySelector('.pages-holder'); } _destroy() { + window.clearInterval(this._timeout); this._active = false; } - _probePageLoad(ctx) { - if (this._active) { - window.setTimeout(() => { - window.requestAnimationFrame(() => { - this._probePageLoad(ctx); - }); - }, 250); - } - - if (this._working) { - return; - } - + _syncUrl(ctx) { let topPageNode = null; let element = document.elementFromPoint( window.innerWidth / 2, @@ -89,6 +100,12 @@ class EndlessPageView { false); this.currentOffset = topOffset; } + } + + _probePageLoad(ctx) { + if (!this._active || this._runningRequests) { + return; + } if (this.totalRecords === null) { return; @@ -97,32 +114,41 @@ class EndlessPageView { document.documentElement.scrollHeight - document.documentElement.clientHeight; - if (this.minOffsetShown > 0 && window.scrollY < this.threshold) { + if (this.minOffsetShown > 0 && + isScrolledIntoView(this.topPageGuardNode)) { this._loadPage( - ctx, this.minOffsetShown - topLimit, topLimit, false); - } else if (this.maxOffsetShown < this.totalRecords && - window.scrollY + this.threshold > scrollHeight) { + ctx, + this.minOffsetShown - this.defaultLimit, + this.defaultLimit, + false); + } + + if (this.maxOffsetShown < this.totalRecords && + isScrolledIntoView(this.bottomPageGuardNode)) { this._loadPage( - ctx, this.maxOffsetShown, topLimit, true); + ctx, + this.maxOffsetShown, + this.defaultLimit, + true); } } _loadPage(ctx, offset, limit, append) { - this._working++; + this._runningRequests++; return new Promise((resolve, reject) => { ctx.requestPage(offset, limit).then(response => { if (!this._active) { - this._working--; + this._runningRequests--; return Promise.reject(); } window.requestAnimationFrame(() => { let pageNode = this._renderPage(ctx, append, response); - this._working--; + this._runningRequests--; resolve(pageNode); }); }, error => { this.showError(error.message); - this._working--; + this._runningRequests--; reject(); }); }); @@ -165,7 +191,7 @@ class EndlessPageView { if (append) { this._pagesHolderNode.appendChild(pageNode); - if (!this._init && response.offset > 0) { + if (this._initialPageLoad && response.offset > 0) { window.scroll(0, pageNode.getBoundingClientRect().top); } } else { @@ -179,7 +205,7 @@ class EndlessPageView { this.showInfo('No data to show'); } - this._init = true; + this._initialPageLoad = false; return pageNode; }