views/paging: add endless pager
This commit is contained in:
parent
7874614be3
commit
b7a67fc01c
9 changed files with 150 additions and 4 deletions
4
client/html/endless_pager.hbs
Normal file
4
client/html/endless_pager.hbs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<div class='pager'>
|
||||||
|
<div class='pages-holder'></div>
|
||||||
|
<div class='messages'></div>
|
||||||
|
</div>
|
4
client/html/endless_pager_page.hbs
Normal file
4
client/html/endless_pager_page.hbs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<div class='page'>
|
||||||
|
<p>Page {{this.page}} of {{this.totalPages}}</p>
|
||||||
|
<div class='page-content-holder'></div>
|
||||||
|
</div>
|
|
@ -1,16 +1,33 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const api = require('../api.js');
|
const events = require('../events.js');
|
||||||
|
const settingsController = require('./settings_controller.js');
|
||||||
|
const EndlessPageView = require('../views/endless_page_view.js');
|
||||||
const ManualPageView = require('../views/manual_page_view.js');
|
const ManualPageView = require('../views/manual_page_view.js');
|
||||||
|
|
||||||
class PageController {
|
class PageController {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
events.listen(events.SettingsChange, () => {
|
||||||
|
this.update();
|
||||||
|
});
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
if (settingsController.getSettings().endlessScroll) {
|
||||||
|
this.pageView = new EndlessPageView();
|
||||||
|
} else {
|
||||||
this.pageView = new ManualPageView();
|
this.pageView = new ManualPageView();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
run(ctx) {
|
run(ctx) {
|
||||||
this.pageView.render(ctx);
|
this.pageView.render(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
this.pageView.unrender();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new PageController();
|
module.exports = new PageController();
|
||||||
|
|
|
@ -25,6 +25,7 @@ class SettingsController {
|
||||||
saveSettings(browsingSettings) {
|
saveSettings(browsingSettings) {
|
||||||
localStorage.setItem('settings', JSON.stringify(browsingSettings));
|
localStorage.setItem('settings', JSON.stringify(browsingSettings));
|
||||||
events.notify(events.Success, 'Settings saved');
|
events.notify(events.Success, 'Settings saved');
|
||||||
|
events.notify(events.SettingsChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSettings(settings) {
|
getSettings(settings) {
|
||||||
|
|
|
@ -39,7 +39,14 @@ class UsersController {
|
||||||
'/user/:name/delete',
|
'/user/:name/delete',
|
||||||
(ctx, next) => { this.loadUserRoute(ctx, next); },
|
(ctx, next) => { this.loadUserRoute(ctx, next); },
|
||||||
(ctx, next) => { this.deleteUserRoute(ctx, next); });
|
(ctx, next) => { this.deleteUserRoute(ctx, next); });
|
||||||
page.exit('/user/', (ctx, next) => { this.user = null; });
|
page.exit(/\/users\/.*/, (ctx, next) => {
|
||||||
|
pageController.stop();
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
page.exit(/\/user\/.*/, (ctx, next) => {
|
||||||
|
this.user = null;
|
||||||
|
next();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
listUsersRoute(ctx, next) {
|
listUsersRoute(ctx, next) {
|
||||||
|
|
|
@ -27,6 +27,7 @@ module.exports = {
|
||||||
Error: 2,
|
Error: 2,
|
||||||
Info: 3,
|
Info: 3,
|
||||||
Authentication: 4,
|
Authentication: 4,
|
||||||
|
SettingsChange: 5,
|
||||||
|
|
||||||
notify: notify,
|
notify: notify,
|
||||||
listen: listen,
|
listen: listen,
|
||||||
|
|
|
@ -29,6 +29,14 @@ if (!Object.entries) {
|
||||||
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
|
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
|
||||||
|
|
||||||
// non standard
|
// non standard
|
||||||
|
Node.prototype.prependChild = function(child) {
|
||||||
|
if (this.firstChild) {
|
||||||
|
this.insertBefore(child, this.firstChild);
|
||||||
|
} else {
|
||||||
|
this.appendChild(child);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Promise.prototype.always = function(onResolveOrReject) {
|
Promise.prototype.always = function(onResolveOrReject) {
|
||||||
return this.then(
|
return this.then(
|
||||||
onResolveOrReject,
|
onResolveOrReject,
|
||||||
|
|
101
client/js/views/endless_page_view.js
Normal file
101
client/js/views/endless_page_view.js
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const events = require('../events.js');
|
||||||
|
const misc = require('../util/misc.js');
|
||||||
|
const views = require('../util/views.js');
|
||||||
|
|
||||||
|
function removeConsecutiveDuplicates(a) {
|
||||||
|
return a.filter((item, pos, ary) => {
|
||||||
|
return !pos || item != ary[pos - 1];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class EndlessPageView {
|
||||||
|
constructor() {
|
||||||
|
this.holderTemplate = views.getTemplate('endless-pager');
|
||||||
|
this.pageTemplate = views.getTemplate('endless-pager-page');
|
||||||
|
}
|
||||||
|
|
||||||
|
render(ctx) {
|
||||||
|
const target = document.getElementById('content-holder');
|
||||||
|
const source = this.holderTemplate();
|
||||||
|
const pagesHolder = source.querySelector('.pages-holder');
|
||||||
|
views.listenToMessages(target);
|
||||||
|
views.showView(target, source);
|
||||||
|
|
||||||
|
const threshold = window.innerHeight / 3;
|
||||||
|
|
||||||
|
this.minPageShown = null;
|
||||||
|
this.maxPageShown = null;
|
||||||
|
this.totalPages = null;
|
||||||
|
this.fetching = false;
|
||||||
|
|
||||||
|
this.updater = () => {
|
||||||
|
if (this.fetching || this.totalPages === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let scrollHeight =
|
||||||
|
document.documentElement.scrollHeight -
|
||||||
|
document.documentElement.clientHeight;
|
||||||
|
|
||||||
|
if (this.minPageShown > 1 && window.scrollY - threshold < 0) {
|
||||||
|
this.loadPage(pagesHolder, ctx, this.minPageShown - 1, false);
|
||||||
|
} else if (this.maxPageShown < this.totalPages &&
|
||||||
|
window.scrollY + threshold > scrollHeight) {
|
||||||
|
this.loadPage(pagesHolder, ctx, this.maxPageShown + 1, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.loadPage(pagesHolder, ctx, ctx.initialPage, true);
|
||||||
|
window.addEventListener('scroll', this.updater, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
unrender() {
|
||||||
|
window.removeEventListener('scroll', this.updater, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPage(pagesHolder, ctx, currentPage, append) {
|
||||||
|
this.fetching = true;
|
||||||
|
|
||||||
|
if (currentPage < this.minPageShown || this.minPageShown === null) {
|
||||||
|
this.minPageShown = currentPage;
|
||||||
|
}
|
||||||
|
if (currentPage > this.maxPageShown || this.maxPageShown === null) {
|
||||||
|
this.maxPageShown = currentPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.requestPage(currentPage).then(response => {
|
||||||
|
this.totalPages = Math.ceil(response.total / response.pageSize);
|
||||||
|
if (response.total) {
|
||||||
|
const page = this.pageTemplate({
|
||||||
|
page: currentPage,
|
||||||
|
totalPages: this.totalPages,
|
||||||
|
});
|
||||||
|
|
||||||
|
let pageRendererCtx = response;
|
||||||
|
pageRendererCtx.target = page.querySelector(
|
||||||
|
'.page-content-holder');
|
||||||
|
ctx.pageRenderer.render(pageRendererCtx);
|
||||||
|
|
||||||
|
if (append) {
|
||||||
|
pagesHolder.appendChild(page);
|
||||||
|
} else {
|
||||||
|
pagesHolder.prependChild(page);
|
||||||
|
window.scroll(
|
||||||
|
window.scrollX, window.scrollY + page.offsetHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fetching = false;
|
||||||
|
this.updater();
|
||||||
|
|
||||||
|
if (response.total <= (currentPage - 1) * response.pageSize) {
|
||||||
|
events.notify(events.Info, 'No data to show');
|
||||||
|
}
|
||||||
|
}, response => {
|
||||||
|
events.notify(events.Error, response.description);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = EndlessPageView;
|
|
@ -75,7 +75,7 @@ class ManualPageView {
|
||||||
|
|
||||||
views.listenToMessages(target);
|
views.listenToMessages(target);
|
||||||
views.showView(target, source);
|
views.showView(target, source);
|
||||||
if (response.total < (currentPage - 1) * response.pageSize) {
|
if (response.total <= (currentPage - 1) * response.pageSize) {
|
||||||
events.notify(events.Info, 'No data to show');
|
events.notify(events.Info, 'No data to show');
|
||||||
}
|
}
|
||||||
}, response => {
|
}, response => {
|
||||||
|
@ -84,6 +84,9 @@ class ManualPageView {
|
||||||
events.notify(events.Error, response.description);
|
events.notify(events.Error, response.description);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unrender() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ManualPageView;
|
module.exports = ManualPageView;
|
||||||
|
|
Loading…
Reference in a new issue