Creating an unread count for a static site

Lately I've noticed some sites will include a little "unread" icon for their blog, showing you how many new articles they have since your last visit. As an example, here is how the Apache Cordova displays it in their header:

Unread blog entries

I've seen this on a few other sites (like Ionic) and I assume it is a simple plugin, but I thought it would be kind of fun to build a prototype of this myself. What follows is a simple example of the feature that can be used for static sites. There's probably many different ways to add this feature (and I'll discuss some options at the end) and I'd love to hear from my readers how they've accomplished this if they have it on their site. As a reminder, everything I share here is free for you to make use of on your own site. All I ask is that you show your appreciation with a quick visit to my Amazon Wishlist if you can.

Ok - so before we get into the code, let's discuss how we can handle this. When comes to the site, we can see if they've been to the site before. There are a variety of ways to persist data on the client, but the simplest is localStorage.

Given that a user first hit the site on February 1, 2016, we can then ask the blog for a list of articles. Luckily there's a simple way to do that - RSS. By parsing the blog's RSS feed, we can iterate over every entry and see when it was published. If it was published after February 1, we can increment a counter of unread articles.

So far so good, but then we have a few questions. What if the user has never been to the site before? Should we show an unread count of 10? (10 being the "typical" number of items in an RSS feed.) In my opinion, no. It feels a bit pushy.

How and when do we update the value? In theory you could update the date value immediately. I may choose to go the blog or not, but since you've already told me that the site has X unread articles, there isn't necessarily a need to keep telling me that. Or - I could only update the date when you visit the blog (either the home page or any particular entry).

To keep things a bit simpler, we'll say that when you visit the blog home page, we'll automatically update the "last visited" value so you no longer see an unread count. Alright, let's write some code!


$(document).ready(function() {
    
    if(!onBlog()) {
        getUnReadCount();   
    }
    
});

For our first iteration, we've got a bit of simple code that says - well - you can probably read that out loud and figure out exactly what we're doing. If we aren't on the blog, get the unread count. Now let's flesh out those methods.

First - onBlog:


function onBlog() {
    return window.location.pathname.indexOf("blog") >= 0;
}

A bit lame, but all we do here is see if 'blog' exists in the current path. Obviously your site could use 'news' for the path so you may need to modify that logic to match your particular site.

Now let's look at getUnReadCount:


function getUnReadCount(cb) { 

    //have we been here?
    var lastvisit = localStorage['lastVisit'];
    if(!lastvisit) return cb(0);
        
    //ok, get the RSS
    var yql = "https://query.yahooapis.com/v1/public/yql?q=select%20*from%20rss%20where%20url%3D%22http%3A%2F%2Ffeeds.feedburner.com%2Fraymondcamdensblog%3Fformat%3Dxml%22&format=json&diagnostics=true&callback=";
    
    var unread = 0;
    $.getJSON(yql, function(res) {
        var items = res.query.results.item;
        console.log(items); 
        items.forEach(function(item) {
            var articleDate = new Date(item.pubDate).getTime();
            if(articleDate > lastvisit) unread++;
        });
        cb(unread);
    }, "jsonp");
        
}

So notice that we're now expecting an argument to the function that will be a callback to fire when we're done with our work. If the user has never been to the site, we shortcircuit out by returning 0.

We then hit the RSS feed for our site. I talked about parsing RSS on my blog a few months ago and at the time I mentioned YQL as an excellent replacement for the Google Feed API. In our case, we get the items from the RSS feed in a nice array we can loop over.

Note the array iterator. For each article get the time in milliseconds and compare it to our lastvisit value. We increment every time the article is newer. Now is probably a good time to go back and show the complete code.


$(document).ready(function() {
    
    if(!onBlog()) {
        getUnReadCount(function(count) {
            console.log('result is '+count);
            if(count > 0) {
                $('.badge').text(count);
            }
        }); 
    } else {
        localStorage['lastVisit'] = new Date().getTime(); 
    }   
});

function onBlog() {
    return window.location.pathname.indexOf("blog") >= 0;
}

function getUnReadCount(cb) { 

    //have we been here?
    var lastvisit = localStorage['lastVisit'];
    if(!lastvisit) return cb(0);
        
    //ok, get the RSS
    var yql = "https://query.yahooapis.com/v1/public/yql?q=select%20*from%20rss%20where%20url%3D%22http%3A%2F%2Ffeeds.feedburner.com%2Fraymondcamdensblog%3Fformat%3Dxml%22&format=json&diagnostics=true&callback=";
    
    var unread = 0;
    $.getJSON(yql, function(res) {
        var items = res.query.results.item;
        items.forEach(function(item) {
            var articleDate = new Date(item.pubDate).getTime();
            if(articleDate > lastvisit) unread++;
        });
        cb(unread);
    }, "jsonp");
        
}

You can see now that we're handling the display update as well as storing your last visit when you are on the blog. The DOM selector is just running against a super simple Bootstrap template I whipped up just for this example.

Here is a screen shot of in action:

Blog updated

I went ahead and put up the demo here: https://static.raymondcamden.com/demos/2016/02/18/test.html But obviously it will be a bit weird since the RSS is on my site, not the demo and, but, you can get the complete HTML templates from there if you want.

Alternatives

Right off the bat, I can think of one quick way to simplify this a bit. Don't forget that most static site generators let you have dynamic files of all sorts - not just HTML. Imagine if my JavaScript code was dynamic as well. I could dynamically generate an array of the last ten date values. Then my code could skip going to YQL. Heck, it wouldn't even need to be async anymore. It would add a tiny bit of weight to the download of the file, but the network speed optimization should make up for that I think.

You could replace the use of localStorage with a cookie and - in general - get slightly more support - but that seems like overkill to me. (However, I would consider adding a simple localStorage check in my code before doing any of the checks.)

What do you think?

Like This?

If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can also subscribe to the email feed to get notified of new posts.