summaryrefslogtreecommitdiffstats
path: root/client/components/lists
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
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')
-rw-r--r--client/components/lists/list.styl3
-rw-r--r--client/components/lists/listBody.jade12
-rw-r--r--client/components/lists/listBody.js81
3 files changed, 95 insertions, 1 deletions
diff --git a/client/components/lists/list.styl b/client/components/lists/list.styl
index 51ade73c..70502083 100644
--- a/client/components/lists/list.styl
+++ b/client/components/lists/list.styl
@@ -211,6 +211,9 @@
max-height: 250px
overflow: hidden
+.sk-spinner-list
+ margin-top: unset !important
+
list-header-color(background, color...)
border-bottom: 6px solid background
diff --git a/client/components/lists/listBody.jade b/client/components/lists/listBody.jade
index c6c9b204..f030833b 100644
--- a/client/components/lists/listBody.jade
+++ b/client/components/lists/listBody.jade
@@ -4,7 +4,7 @@ template(name="listBody")
if cards.count
+inlinedForm(autoclose=false position="top")
+addCardForm(listId=_id position="top")
- each (cards (idOrNull ../../_id))
+ each (cardsWithLimit (idOrNull ../../_id))
a.minicard-wrapper.js-minicard(href=absoluteUrl
class="{{#if cardIsSelected}}is-selected{{/if}}"
class="{{#if MultiSelection.isSelected _id}}is-checked{{/if}}")
@@ -12,6 +12,16 @@ template(name="listBody")
.materialCheckBox.multi-selection-checkbox.js-toggle-multi-selection(
class="{{#if MultiSelection.isSelected _id}}is-checked{{/if}}")
+minicard(this)
+ if (showSpinner (idOrNull ../../_id))
+ .sk-spinner.sk-spinner-wave.sk-spinner-list(
+ class=currentBoard.colorClass
+ id="showMoreResults")
+ .sk-rect1
+ .sk-rect2
+ .sk-rect3
+ .sk-rect4
+ .sk-rect5
+
if canSeeAddCard
+inlinedForm(autoclose=false position="bottom")
+addCardForm(listId=_id position="bottom")
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();
},