summaryrefslogtreecommitdiffstats
path: root/client/components/lists/listBody.js
diff options
context:
space:
mode:
authorBenjamin Tissoires <benjamin.tissoires@redhat.com>2019-01-30 16:25:23 +0100
committerBenjamin Tissoires <benjamin.tissoires@redhat.com>2019-02-01 14:58:02 +0100
commit66bc1f28dd4395c1c2b4434520923edfa54d4eed (patch)
tree0db20b2fb43b1367a7d0d0f62ccb6d5f29774ea5 /client/components/lists/listBody.js
parent1b11123797a8da92e478ea257cd24ebadf084a24 (diff)
downloadwekan-66bc1f28dd4395c1c2b4434520923edfa54d4eed.tar.gz
wekan-66bc1f28dd4395c1c2b4434520923edfa54d4eed.tar.bz2
wekan-66bc1f28dd4395c1c2b4434520923edfa54d4eed.zip
Use infinite-scrolling on lists
This allows to reduce the loading time of a big board. Note that there is an infinite scroll implementation in the mixins, but this doesn't fit well as the cards in the list can have arbitrary height. The idea to rely on the visibility of a spinner is based on http://www.meteorpedia.com/read/Infinite_Scrolling
Diffstat (limited to 'client/components/lists/listBody.js')
-rw-r--r--client/components/lists/listBody.js81
1 files changed, 81 insertions, 0 deletions
diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js
index 1001f3bc..fdea3bae 100644
--- a/client/components/lists/listBody.js
+++ b/client/components/lists/listBody.js
@@ -1,6 +1,34 @@
const subManager = new SubsManager();
+const InfiniteScrollIter = 10;
BlazeComponent.extendComponent({
+ onCreated() {
+ // for infinite scrolling
+ this.cardlimit = new ReactiveVar(InfiniteScrollIter);
+ },
+
+ onRendered() {
+ const domElement = this.find('.js-perfect-scrollbar');
+
+ this.$(domElement).on('scroll', () => this.updateList(domElement));
+ $(window).on(`resize.${this.data().listId}`, () => this.updateList(domElement));
+
+ // we add a Mutation Observer to allow propagations of cardlimit
+ // when the spinner stays in the current view (infinite scrolling)
+ this.mutationObserver = new MutationObserver(() => this.updateList(domElement));
+
+ this.mutationObserver.observe(domElement, {
+ childList: true,
+ });
+
+ this.updateList(domElement);
+ },
+
+ onDestroyed() {
+ $(window).off(`resize.${this.data().listId}`);
+ this.mutationObserver.disconnect();
+ },
+
mixins() {
return [Mixins.PerfectScrollbar];
},
@@ -60,6 +88,13 @@ BlazeComponent.extendComponent({
type: 'cardType-card',
});
+ // if the displayed card count is less than the total cards in the list,
+ // we need to increment the displayed card count to prevent the spinner
+ // to appear
+ const cardCount = this.data().cards(this.idOrNull(swimlaneId)).count();
+ if (this.cardlimit.get() < cardCount) {
+ this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter);
+ }
// In case the filter is active we need to add the newly inserted card in
// the list of exceptions -- cards that are not filtered. Otherwise the
@@ -119,6 +154,52 @@ BlazeComponent.extendComponent({
return undefined;
},
+ cardsWithLimit(swimlaneId) {
+ const limit = this.cardlimit.get();
+ const selector = {
+ listId: this.currentData()._id,
+ archived: false,
+ };
+ if (swimlaneId)
+ selector.swimlaneId = swimlaneId;
+ return Cards.find(Filter.mongoSelector(selector), {
+ sort: ['sort'],
+ limit,
+ });
+ },
+
+ spinnerInView(container) {
+ const parentViewHeight = container.clientHeight;
+ const bottomViewPosition = container.scrollTop + parentViewHeight;
+
+ const spinner = this.find('.sk-spinner-list');
+
+ const threshold = spinner.offsetTop;
+
+ return bottomViewPosition > threshold;
+ },
+
+ showSpinner(swimlaneId) {
+ const list = Template.currentData();
+ return list.cards(swimlaneId).count() > this.cardlimit.get();
+ },
+
+ updateList(container) {
+ // first, if the spinner is not rendered, we have reached the end of
+ // the list of cards, so skip and disable firing the events
+ const target = this.find('.sk-spinner-list');
+ if (!target) {
+ this.$(container).off('scroll');
+ $(window).off(`resize.${this.data().listId}`);
+ return;
+ }
+
+ if (this.spinnerInView(container)) {
+ this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter);
+ Ps.update(container);
+ }
+ },
+
canSeeAddCard() {
return !this.reachedWipLimit() && Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
},