summaryrefslogtreecommitdiff
path: root/client/js/views/endless_page_view.js
diff options
context:
space:
mode:
Diffstat (limited to 'client/js/views/endless_page_view.js')
-rw-r--r--client/js/views/endless_page_view.js111
1 files changed, 88 insertions, 23 deletions
diff --git a/client/js/views/endless_page_view.js b/client/js/views/endless_page_view.js
index f94c371..a4cb420 100644
--- a/client/js/views/endless_page_view.js
+++ b/client/js/views/endless_page_view.js
@@ -38,18 +38,26 @@ class EndlessPageView {
this.defaultLimit = parseInt(ctx.parameters.limit || ctx.defaultLimit);
const initialOffset = parseInt(ctx.parameters.offset || 0);
- this._loadPage(ctx, initialOffset, this.defaultLimit, true).then(
- (pageNode) => {
- if (initialOffset !== 0) {
- pageNode.scrollIntoView();
+ if (this._isCacheValid(ctx)) {
+ this._loadCachedPages(ctx);
+ } else {
+ this._clearCache(ctx);
+ this._loadPage(ctx, initialOffset, this.defaultLimit, true).then(
+ (pageNode) => {
+ if (initialOffset !== 0) {
+ pageNode.scrollIntoView();
+ }
}
- }
- );
+ );
+ }
this._timeout = window.setInterval(() => {
window.requestAnimationFrame(() => {
this._probePageLoad(ctx);
this._syncUrl(ctx);
+ if (this._shouldUseCache(ctx)) {
+ ctx.browserState.scrollY = window.scrollY;
+ }
});
}, 250);
@@ -72,12 +80,7 @@ class EndlessPageView {
return this._hostNode.querySelector(".pages-holder");
}
- _destroy() {
- window.clearInterval(this._timeout);
- this._active = false;
- }
-
- _syncUrl(ctx) {
+ get _topPageNode() {
let topPageNode = null;
let element = document.elementFromPoint(
window.innerWidth / 2,
@@ -90,18 +93,35 @@ class EndlessPageView {
}
element = element.parentNode;
}
+ return topPageNode;
+ }
+
+ _destroy() {
+ window.clearInterval(this._timeout);
+ this._active = false;
+ }
+
+ _syncUrl(ctx) {
+ const topPageNode = this._topPageNode;
if (!topPageNode) {
return;
}
let topOffset = parseInt(topPageNode.getAttribute("data-offset"));
let topLimit = parseInt(topPageNode.getAttribute("data-limit"));
if (topOffset !== this.currentOffset) {
+ const path = ctx.getClientUrlForPage(
+ topOffset,
+ topLimit === ctx.defaultLimit ? null : topLimit
+ );
+ if (this._shouldUseCache(ctx)) {
+ // We only scrolled, so we should continue using the same cache entry;
+ // Update the cache path so it's not invalidated:
+ ctx.browserState.pageCache.path = "/" + path;
+ }
router.replace(
- ctx.getClientUrlForPage(
- topOffset,
- topLimit === ctx.defaultLimit ? null : topLimit
- ),
- ctx.state,
+ path,
+ // ctx here is not "real" context, it's the object from _syncPageController()
+ ctx.browserState,
false
);
this.currentOffset = topOffset;
@@ -116,11 +136,9 @@ class EndlessPageView {
if (this.totalRecords === null) {
return;
}
+ const scrollThreshold = this._topPageNode.scrollHeight * 0.2;
- if (
- this.minOffsetShown > 0 &&
- isScrolledIntoView(this.topPageGuardNode)
- ) {
+ if (this.minOffsetShown > 0 && window.scrollY < scrollThreshold) {
this._loadPage(
ctx,
this.minOffsetShown - this.defaultLimit,
@@ -129,14 +147,51 @@ class EndlessPageView {
);
}
+ const pageBottom = this._pagesHolderNode.getBoundingClientRect().bottom;
if (
this.maxOffsetShown < this.totalRecords &&
- isScrolledIntoView(this.bottomPageGuardNode)
+ pageBottom < window.innerHeight + scrollThreshold
) {
this._loadPage(ctx, this.maxOffsetShown, this.defaultLimit, true);
}
}
+ _shouldUseCache(ctx) {
+ return ctx.browserState !== undefined && ctx.browserState != null &&
+ ctx.readPageFromCache !== undefined;
+ }
+
+ _isCacheValid(ctx) {
+ if (!this._shouldUseCache(ctx)) return false;
+ const cache = ctx.browserState.pageCache;
+ return cache !== null && cache !== undefined &&
+ cache.path == history.state.path &&
+ cache.pages !== undefined && cache.pages !== null;
+ }
+
+ _clearCache(ctx) {
+ if (!this._shouldUseCache(ctx)) return;
+ ctx.browserState.pageCache = {path: history.state.path, pages: {}};
+ }
+
+ _loadCachedPages(ctx) {
+ if (!this._shouldUseCache(ctx)) return;
+ // k-v map of page offset to raw response
+ const pages = ctx.browserState.pageCache.pages || {};
+ window.requestAnimationFrame(() => {
+ for (const [offset, data] of Object.entries(pages)) {
+ const response = {
+ offset: data.offset,
+ limit: data.limit,
+ total: data.total,
+ results: ctx.readPageFromCache(data.raw_data)
+ };
+ this._renderPage(ctx, true, response);
+ }
+ window.scroll(0, ctx.browserState.scrollY || 0);
+ });
+ }
+
_loadPage(ctx, offset, limit, append) {
this._runningRequests++;
return new Promise((resolve, reject) => {
@@ -146,6 +201,16 @@ class EndlessPageView {
this._runningRequests--;
return Promise.reject();
}
+ if (this._shouldUseCache(ctx)) {
+ // Need to extract raw_data, otherwise it can't be stored in history
+ const pages = ctx.browserState.pageCache.pages || {};
+ pages[offset] = {
+ offset: response.offset,
+ limit: response.limit,
+ total: response.total,
+ raw_data: response.results.raw_data,
+ };
+ }
window.requestAnimationFrame(() => {
let pageNode = this._renderPage(ctx, append, response);
this._runningRequests--;
@@ -190,7 +255,7 @@ class EndlessPageView {
}
if (
response.offset + response.results.length >
- this.maxOffsetShown ||
+ this.maxOffsetShown ||
this.maxOffsetShown === null
) {
this.maxOffsetShown =