client/paging: improve endless paging

- Change page number indicator
- Remove most of the scrolling cruft as it has no chance of working
  anyway, thanks to our benevolent browsers
- Scroll to page header if the page is not the first one (I'm surprised
  this even works)
- Use promises rather than timers
- Simplify top page detection using .elementFromPoint rather than
  iterating over all pages. Coincidentally, this seems to work slightly
  better
This commit is contained in:
rr- 2016-05-09 11:17:04 +02:00
parent f39e58b1bc
commit 29b0fde9a7
3 changed files with 82 additions and 68 deletions

View file

@ -166,9 +166,20 @@ nav.text-nav
.page .page
position: relative position: relative
p p
position: absolute margin: 0.5em 0.5em 0.5em 0
right: 0 position: relative
top: 0 &:before
margin: 0 display: block
padding: 0.1em 0.5em content: ''
background: white position: absolute
left: 0
top: 50%
right: 0
height: 3px
background: $top-nav-color
z-index: 1
span
position: relative
background: white
padding: 0 1em
z-index: 2

View file

@ -1,4 +1,4 @@
<div class='page'> <div class='page'>
<p>{{this.page}}/{{this.totalPages}}</p> <p><span>Page {{this.page}} of {{this.totalPages}}</span></p>
<div class='page-content-holder'></div> <div class='page-content-holder'></div>
</div> </div>

View file

@ -18,6 +18,7 @@ class EndlessPageView {
views.listenToMessages(target); views.listenToMessages(target);
views.showView(target, source); views.showView(target, source);
this.active = true; this.active = true;
this.working = 0;
let headerRendererCtx = ctx; let headerRendererCtx = ctx;
headerRendererCtx.target = pageHeaderHolder; headerRendererCtx.target = pageHeaderHolder;
@ -25,47 +26,39 @@ class EndlessPageView {
const threshold = window.innerHeight / 3; const threshold = window.innerHeight / 3;
if (ctx.state && ctx.state.html) { this.minPageShown = null;
this.minPageShown = ctx.state.minPageShown; this.maxPageShown = null;
this.maxPageShown = ctx.state.maxPageShown; this.totalPages = null;
this.totalPages = ctx.state.totalPages; this.currentPage = null;
this.currentPage = ctx.state.currentPage;
} else {
this.minPageShown = null;
this.maxPageShown = null;
this.totalPages = null;
this.currentPage = null;
}
this.fetching = false;
this.updater = () => { this.updater = () => {
let topPage = null; if (this.working) {
let allPageNodes = return;
pagesHolder.querySelectorAll('.page');
for (let pageNode of allPageNodes) {
if (pageNode.getBoundingClientRect().bottom >= 0) {
topPage = parseInt(pageNode.getAttribute('data-page'));
break;
}
}
if (topPage !== this.currentPage) {
page.replace(
ctx.clientUrl.format({page: topPage}),
{
minPageShown: this.minPageShown,
maxPageShown: this.maxPageShown,
totalPages: this.totalPages,
currentPage: this.currentPage,
html: pagesHolder.innerHTML,
scrollX: window.scrollX,
scrollY: window.scrollY,
},
false,
false);
this.currentPage = topPage;
} }
if (this.fetching || this.totalPages === null) { let topPageNode = null;
var element = document.elementFromPoint(window.innerWidth / 2, 1);
while (element.parentNode !== null) {
if (element.classList.contains('page')) {
topPageNode = element;
break;
}
element = element.parentNode;
}
if (!topPageNode) {
return;
}
let topPageNumber = parseInt(topPageNode.getAttribute('data-page'));
if (topPageNumber !== this.currentPage) {
page.replace(
ctx.clientUrl.format({page: topPageNumber}),
null,
false,
false);
this.currentPage = topPageNumber;
}
if (this.totalPages === null) {
return; return;
} }
let scrollHeight = let scrollHeight =
@ -73,41 +66,42 @@ class EndlessPageView {
document.documentElement.clientHeight; document.documentElement.clientHeight;
if (this.minPageShown > 1 && window.scrollY - threshold < 0) { if (this.minPageShown > 1 && window.scrollY - threshold < 0) {
this.loadPage(pagesHolder, ctx, this.minPageShown - 1, false); this.loadPage(pagesHolder, ctx, this.minPageShown - 1, false)
.then(() => this.updater());
} else if (this.maxPageShown < this.totalPages && } else if (this.maxPageShown < this.totalPages &&
window.scrollY + threshold > scrollHeight) { window.scrollY + threshold > scrollHeight) {
this.loadPage(pagesHolder, ctx, this.maxPageShown + 1, true); this.loadPage(pagesHolder, ctx, this.maxPageShown + 1, true)
.then(() => this.updater());
} }
}; };
if (ctx.state && ctx.state.html) { this.loadPage(pagesHolder, ctx, ctx.searchQuery.page, true)
pagesHolder.innerHTML = ctx.state.html; .then(pageNode => {
window.scroll(ctx.state.scrollX, ctx.state.scrollY); if (ctx.searchQuery.page > 1) {
this.updater(); window.scroll(0, pageNode.getBoundingClientRect().top);
} else { }
this.loadPage(pagesHolder, ctx, ctx.searchQuery.page, true); this.updater();
} });
window.addEventListener('scroll', this.updater, true); window.addEventListener('scroll', this.updater, true);
window.addEventListener('unload', this.scrollToTop, true);
} }
unrender() { unrender() {
this.active = false; this.active = false;
window.removeEventListener('scroll', this.updater, true); window.removeEventListener('scroll', this.updater, true);
window.removeEventListener('unload', this.scrollToTop, true);
}
scrollToTop() {
window.scroll(0, 0);
} }
loadPage(pagesHolder, ctx, pageNumber, append) { loadPage(pagesHolder, ctx, pageNumber, append) {
this.fetching = true; this.working++;
return ctx.requestPage(pageNumber).then(response => {
if (pageNumber < this.minPageShown || this.minPageShown === null) {
this.minPageShown = pageNumber;
}
if (pageNumber > this.maxPageShown || this.maxPageShown === null) {
this.maxPageShown = pageNumber;
}
ctx.requestPage(pageNumber).then(response => {
if (!this.active) { if (!this.active) {
return; this.working--;
return Promise.reject();
} }
this.totalPages = Math.ceil(response.total / response.pageSize); this.totalPages = Math.ceil(response.total / response.pageSize);
if (response.total) { if (response.total) {
@ -122,6 +116,13 @@ class EndlessPageView {
'.page-content-holder'); '.page-content-holder');
ctx.pageRenderer.render(pageRendererCtx); ctx.pageRenderer.render(pageRendererCtx);
if (pageNumber < this.minPageShown || this.minPageShown === null) {
this.minPageShown = pageNumber;
}
if (pageNumber > this.maxPageShown || this.maxPageShown === null) {
this.maxPageShown = pageNumber;
}
if (append) { if (append) {
pagesHolder.appendChild(pageNode); pagesHolder.appendChild(pageNode);
} else { } else {
@ -134,16 +135,18 @@ class EndlessPageView {
window.scrollX, window.scrollX,
window.scrollY + pageNode.offsetHeight); window.scrollY + pageNode.offsetHeight);
} }
this.working--;
return Promise.resolve(pageNode);
} }
this.fetching = false;
window.setTimeout(() => { this.updater(); }, 10);
if (response.total <= (pageNumber - 1) * response.pageSize) { if (response.total <= (pageNumber - 1) * response.pageSize) {
events.notify(events.Info, 'No data to show'); events.notify(events.Info, 'No data to show');
} }
this.working--;
return Promise.reject();
}, response => { }, response => {
events.notify(events.Error, response.description); events.notify(events.Error, response.description);
this.working--;
return Promise.reject();
}); });
} }
} }