0

I implemented infinite scrolling in my web app using firebase.

In my "Top" section, I show the highest upvoted posts of users.

The issue, is that in my current implementation, a few posts are skipped.

That is, if many posts have the same score, some will just not appear.

I understand why this is happening, I load my posts by lot of 12, and then select the next lot by doing this:

start = childDataSnapshot.child("inversedScore").val() + 1;

The issue with this approach is that if there are more than 12 elements with the same vote score, or if there are 2 elements with the same vote score, one being number 12 and the other number 13, then number 13 gets ignored and we directly go the next score level.

For example, here is my data on the server:

post1: score: 15
post2: score: 14
post3: score: 13
post4: score: 12
post5: score: 11
post6: score: 10
post7: score: 10
post8: score: 9
post9: score: 9
post10: score: 9
post11: score: 8
post12: score: 7

//posts 13 and 14 will be ignored
post13: score: 7
post14: score: 7

post15: score: 6
post16: score: 6

Here is what I will see on the client:

post1: score: 15
post2: score: 14
post3: score: 13
post4: score: 12
post5: score: 11
post6: score: 10
post7: score: 10
post8: score: 9
post9: score: 9
post10: score: 9
post11: score: 8
post12: score: 7
post15: score: 6
post16: score: 6

What other approach is there to loading the top posts with firebase so that top posts with the same score are never "skipped" ?


client:

var started = false;

app.controller('ctrl', function ($scope, $firebaseArray, $timeout) {

    if(started == false) {
        started = true;

        $scope.bricks = [];
        var _n = 12;

        if ( $(window).width() < 992) {
          _n = 2;
        } 

        var firstElementsLoaded = false;
        var _start = -1000000000;
        var position = 0;

        $scope.getDataset = function() {  
            var sendObject = {
                start: _start,
                section: section,
                n: _n,
            } 
            $.ajax({
                type: "POST",
                url: "images/top",
                data: sendObject,
            }).done(function(result) {
                for (var i = 0; i < result.array.length; i++) {
                    console.log("result: "+result.array[i]);
                    $scope.bricks.push(result.array[i]);
                    $scope.$apply();
                }

                _start = result.start;
                firstElementsLoaded = true;
            });
        };


        $scope.getDataset();

        var screenHeight;
        var existingScreenHeight

        if ( $(window).width() < 992) {
            screenHeight = (_n + 1) * 350;
            existingScreenHeight = -350;
        } 
        else {
            screenHeight = 1 * Math.ceil($(window).height()); 
            existingScreenHeight = -screenHeight;
        }

        window.addEventListener('scroll', function() {

            if (firstElementsLoaded == true) {
               if (window.scrollY >= ((screenHeight) + existingScreenHeight)) {
                  existingScreenHeight += (screenHeight);
                  $scope.$apply($scope.getDataset());
                  firstElementsLoaded = false;
               }
            }
        });
    }
});

angular.bootstrap(document.body, ['app']);

server:

router.post('/images/top', function(req, res, next) {
    var start = parseInt(req.body.start);
    var section = req.body.section;
    var n = parseInt(req.body.n);
    var returnArray = [];
    var screenshotRef = admin.database().ref("posts/"+section);
    screenshotRef.orderByChild("inversedScore").startAt(start).limitToFirst(n).once("value", function(dataSnapshot) {

        dataSnapshot.forEach(function(childDataSnapshot) {

            start = childDataSnapshot.child("inversedScore").val() + 1;

            var post = childDataSnapshot.val();

            var file = bucket.file(post.image);

            var config = {
              action: 'read',
              expires: Date.now() + global.expiryTime,
            };

            file.getSignedUrl(config, function(err, url) {
                if (err) {
                    console.error(err);
                    return;
                }
                post.image = url;
                returnArray.push(post);
                if (returnArray.length == n) {
                    var returnObject = {
                        start: start,
                        array: returnArray
                    }
                    res.send(returnObject);
                }
            });
        });  
    });
});
TheProgrammer
  • 1,409
  • 4
  • 24
  • 53

1 Answers1

2

You can easily skip the extra item (typically referred to as the anchor item) in the client. You cannot skip it in the query. If you pass in the anchor item's key as a second parameter, you can ensure there is only a single overlapping item.

See these previous questions:

Let me know if you have questions about this, because it's definitely not the most intuitive part of the API.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807