From fb163a24939e97756ac91361893c55ec760355fa Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 26 Mar 2019 15:13:35 +0100 Subject: list: simplify infinite scrolling Use IntersectionObserver instead of custom made one. This adds the benefit of not loading any extra cards if the list is not shown on screen --- client/components/lists/listBody.js | 61 +++++++++++-------------------------- 1 file changed, 17 insertions(+), 44 deletions(-) (limited to 'client/components/lists/listBody.js') diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 7d767011..006f8f0d 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -8,25 +8,25 @@ BlazeComponent.extendComponent({ }, 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, - }); + const spinner = this.find('.sk-spinner-list'); - this.updateList(domElement); - }, + if (spinner) { + const options = { + root: null, // we check if the spinner is on the current viewport + rootMargin: '0px', + threshold: 0.25, + }; + + const observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter); + } + }); + }, options); - onDestroyed() { - $(window).off(`resize.${this.data().listId}`); - this.mutationObserver.disconnect(); + observer.observe(spinner); + } }, mixins() { @@ -191,38 +191,11 @@ BlazeComponent.extendComponent({ }); }, - 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(); }, -- cgit v1.2.3-1-g7c22 From 00376b43f82b1b751974e827931373595c40c0f7 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 26 Mar 2019 15:54:53 +0100 Subject: list: make sure the spinner of infinite scrolling doesn't show on load When loading a board on a high resolution screen, there is a chance there is not enough cards displayed and the spinner is still there, spinning forever. Add an idle callback that checks if the spinner is still there, and while it is there, extend the number of cards to show. Fixes #2250 --- client/components/lists/listBody.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'client/components/lists/listBody.js') diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 006f8f0d..d6a62cc9 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -5,6 +5,7 @@ BlazeComponent.extendComponent({ onCreated() { // for infinite scrolling this.cardlimit = new ReactiveVar(InfiniteScrollIter); + this.spinnerShown = false; }, onRendered() { @@ -19,9 +20,8 @@ BlazeComponent.extendComponent({ const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { - if (entry.isIntersecting) { - this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter); - } + this.spinnerShown = entry.isIntersecting; + this.updateList(); }); }, options); @@ -29,6 +29,13 @@ BlazeComponent.extendComponent({ } }, + updateList() { + if (this.spinnerShown) { + this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter); + window.requestIdleCallback(() => this.updateList()); + } + }, + mixins() { return [Mixins.PerfectScrollbar]; }, -- cgit v1.2.3-1-g7c22 From cbb6c82113782c1ef235668ffb3c708431f6b400 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 26 Mar 2019 16:20:59 +0100 Subject: list: move the spinner into its own blaze component This way, when a list is at the maximum number of cards shown and adding a new card would make the spinner appear, the list would load the next N items. This can happen if user A and B are both looking at the same board, B adds a new cards, and A will see the spinner and will not be able to remove it. --- client/components/lists/listBody.js | 65 ++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 29 deletions(-) (limited to 'client/components/lists/listBody.js') diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index d6a62cc9..2e6591e2 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -5,35 +5,6 @@ BlazeComponent.extendComponent({ onCreated() { // for infinite scrolling this.cardlimit = new ReactiveVar(InfiniteScrollIter); - this.spinnerShown = false; - }, - - onRendered() { - const spinner = this.find('.sk-spinner-list'); - - if (spinner) { - const options = { - root: null, // we check if the spinner is on the current viewport - rootMargin: '0px', - threshold: 0.25, - }; - - const observer = new IntersectionObserver((entries) => { - entries.forEach((entry) => { - this.spinnerShown = entry.isIntersecting; - this.updateList(); - }); - }, options); - - observer.observe(spinner); - } - }, - - updateList() { - if (this.spinnerShown) { - this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter); - window.requestIdleCallback(() => this.updateList()); - } }, mixins() { @@ -641,3 +612,39 @@ BlazeComponent.extendComponent({ }]; }, }).register('searchElementPopup'); + +BlazeComponent.extendComponent({ + onCreated() { + this.spinnerShown = false; + this.cardlimit = this.parentComponent().cardlimit; + }, + + onRendered() { + const spinner = this.find('.sk-spinner-list'); + + if (spinner) { + const options = { + root: null, // we check if the spinner is on the current viewport + rootMargin: '0px', + threshold: 0.25, + }; + + const observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + this.spinnerShown = entry.isIntersecting; + this.updateList(); + }); + }, options); + + observer.observe(spinner); + } + }, + + updateList() { + if (this.spinnerShown) { + this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter); + window.requestIdleCallback(() => this.updateList()); + } + }, + +}).register('spinnerList'); -- cgit v1.2.3-1-g7c22 From e2d0faa539e287247ccd1208fe74705169749210 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 26 Mar 2019 16:22:55 +0100 Subject: list: disconnect infinite-scroll observer to prevent memory leak --- client/components/lists/listBody.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'client/components/lists/listBody.js') diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 2e6591e2..112b6379 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -629,17 +629,21 @@ BlazeComponent.extendComponent({ threshold: 0.25, }; - const observer = new IntersectionObserver((entries) => { + this.observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { this.spinnerShown = entry.isIntersecting; this.updateList(); }); }, options); - observer.observe(spinner); + this.observer.observe(spinner); } }, + onDestroyed() { + this.observer.disconnect(); + }, + updateList() { if (this.spinnerShown) { this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter); -- cgit v1.2.3-1-g7c22