Catching up on Facebook Quickly–Try #1

I had removed the facebook app from my phone, because it was taking too much time.  I dreamed of writing my own app that would let me catch up on facebook quickly, and a week at a time.  This afternoon, I got some time to play.  

Three hours Later

image

Research:

  • I had to go to developer.facebook.com, register, and create an application, to get an application ID.  (well, maybe I didn’t, but I would eventually.)
  • I tried going down the path of Heroku, hosting an app, git, python, etc – but once I found the .Net client, I was done.  (nuget: Facebook
    • I ran into problems where heroku’s git client would not present the right ssh key, and I couldn’t find the ssh command line that it had installed to tell it the right key to use.
  • To bypass authentication, I went to https://developers.facebook.com/tools/explorer and generated an access token from there; it needed the read_stream priviledge.   The token expires after an hour.
    • the url to query is “/me/home” (thank you stack overflow) to get the JSON that represents your newsfeed (most recent items first, looks like)

Then, I created an MVC app to do the querying.  The controller side, to be replaced eventually with more fancier login stuff:

var accessToken = "AAACEdEose0cBABI5wFnNe209ddAmRFpWOB9T9O8x2sCaNlc91ZB1u6gqqrxHseMvBKhuDHtkS3KY6KAlIz6Xc8Ps24nvIWKRF... etc";
var client = new FacebookClient(accessToken);
dynamic homeraw = client.Get("me/home");

That’s great, I have a dynamic object, but I’d rather have intellisense.  Enter json2csharp (thank you!!!!), creates me some classes, a little refactoring / renaming, and some hard coding: 

foreach (var dataraw in homeraw.data)
{
  var homeItem = new HomeItem()
  {
    id = dataraw.id,
    from = new From()
    {
      id = dataraw.@from.id,
      name = dataraw.@from.name,
    },
    message = dataraw.message,
    caption = dataraw.caption,
    name = dataraw.name,
...

And sent everything off to my view, which spits it out into html (the Acronym stuff is not shown here, but I built “SG” out of “Sunjeev Gulati”):

@foreach (var item in Model.Data.data) { <div class="infobubble"> <div>

<strong title="@item.from.name">@Model.AuthorAcroymn[item.from.id]</strong>:

@(item.message ?? item.caption ?? item.name) </div> @if (!String.IsNullOrEmpty(item.picture)) { <div> <img class="pic" src="@item.picture" /> </div> } </div> }

Added a little styling:

.infobubble 
{
    font-size: 12px;
    border: 1px solid gray;
    float: left;
    display: inline-block;
    padding: 2px;
    margin: 2px;
    word-wrap: break-word;
}

And now for some fun.  Reformatting things in jQuery to be more blocky:

    $(window).load(function () {
        $(".infobubble").each(function () {
            var width = $(this).width();
            var height = $(this).height();

            // determine if there's a picture in here somewhere
            var pic = $(this).find(".pic");
            if (pic.length > 0) {
                var picwidth = $(pic[0]).width();
                $(this).width(picwidth);
                width = $(this).width();
                height = $(this).height();
            } else {
                // no picture.  just text.  assume its already very wide
                // bring the width down till its closer to square
                for (var i = 0; i < 10; i++) {
                    if (width > height * 2.0) {
                        $(this).width(width / 2.0);
                        width = $(this).width();
                        height = $(this).height();
                    } else if (height > width * 2.0) {
                        $(this).height(height / 2.0);
                        width = $(this).width();
                        height = $(this).height();
                    } else {
                        break;
                    }
                }
            }
            $(this).data("width", width);
            $(this).data("height", height);
        });
        $("#container").masonry({ itemSelector: '.infobubble' });
    });

This does a few things:

  • If it has a picture, size to the picture.  This usually works (though the “WAL-MART” one at the bottom of the screenshot is an interesting example, I’ll have to check for for overly long text)
  • Otherwise, try to size it till its kinda-square.
  • Then use jquery.Masonry to fill the space.

Where to go from here

  • Use some pre-defined sizes so that Masonry has an easier time filling a wall?
  • Use photo pictures, but really small, to identify who wrote a post?
  • Deal with odd-sized things, or find a better algorithm for blockifying things.  Probably try standard widths like 50 100 200 and 400 and then move on.
  • Decide what to do with comments / likes, etc; support for other kinds of facebook items (only time will reveal them to me)
  • Going further back in time, rather than just a single call.    Try to make it infinite scrolling?  Or, call the service over and over till I have the data I want, re-sort it, and then display it?  While being respectful of their service?
  • Actually go through the pain of logging the user in to facebook.
  • Host the app somewhere?  I’m not sure if it violates any facebook laws, like maybe there’s one that says “you cannot write an app that replaces the newsfeed”?

If this app actually gets written, some of the features that would make it useful:

  • Enter a date range to view over (defaulting to the last time you were in)
  • Inline see comments (if a reasonable number, damn you Takei)
  • Filter down by person or type of post
  • Sort by person, size, photo.
  • Keep track of items seen or not-seen; Star items to look at later; mark all as read.
  • Automatic mute / filters.
  • Jump to item in facebook proper to go do something more with it.

Of course, that’s pie in the sky.   If you want a crack at it, go for it.   What I probably could do on short order:

  • Allow the user to enter the token in a textbox, so anybody could use it (without having to tackle the facebook login hurdle)
  • Host it somewhere for people to have fun with
  • Put the source on github

That would be “a good milestone”.    Maybe next time.

Nevertheless:  that was fun.

Mvc concepts

I’m reaching the end of my first professional (== I got paid) MVC application.

I learned a lot. This is an attempted visualization of most of the topics that I learned.   You may need to click in to see the details.

In addition, as I found good sources of information on the ‘nets, I included links in my source code in comments.  Here is a compilation of those nuggets of goodness. If your post is mentioned on this list — thank you.

Link Goodness
 http://stackoverflow.com/questions/4367723/get-enum-from-description-attribute Embedding meta-data about things in enumerations
 http://ayende.com/blog/1595/nhibernate-mapping-creating-sanity-checks (NHibernate) Integration test to validate mapping completeness
http://www.codingoptimist.com/2010/11/nhibernate-hidden-gems-speed-up.html (NHibernate) Caching configurations (gain: 1 second, not worth it, dropped)
http://stackoverflow.com/questions/325156/calling-generic-method-with-a-type-argument-known-only-at-execution-time Having multiple types with shared logic was a wonderful spot for co-variance, but after about an hour, it was time to move on
http://www.nickriggs.com/posts/getting-the-id-and-name-attribute-generated-by-typed-html-helpers/ I had to drop down to a TextBox with an @class setting, and thus I couldn’t use TextBoxFor, so this came in very useful
http://www.codeproject.com/KB/cs/stringenum.aspx Used by a coworker somewhere in the guts of an automatic model-to-.Where()-clause LINQ query extension
http://www.mikesdotnetting.com/Article/125/ASP.NET-MVC-Uploading-and-Downloading-Files Made it oh-so-easy for me to save and upload intermediate files from this file processing system
http://stackoverflow.com/questions/1151987/can-i-set-an-unlimited-length-for-maxjsonlength-in-web-config/7207539#7207539 Before we got the Grid getting only 1 page at a time, we were getting everything – thus needing this
http://code.google.com/p/autofac/wiki/Mvc3Integration Autofac was just amazing. It made sense to me! Its what I would have done if i was writing an IoC container…
http://slynetblog.blogspot.com/2012/03/using-aspnet-mvc-4-webapi-with.html Using ASp.Net MVC4 with NHiberate and AutoFac .. pointing me in the right direction
http://forum.jquery.com/topic/span-text-new-text-not-working-in-ie  <span/> to <span></span> did the trick, then I could .text() it
http://stackoverflow.com/questions/10588587/jqgrid-does-not-render-correctly-in-chrome-chrome-frame An annoying bug that sent me searching for at least 15 minutes
http://stackoverflow.com/questions/5248183/html-partial-vs-html-renderpartial-html-action-vs-html-renderaction When the views became too large, I could refactor|extract view
http://stackoverflow.com/questions/2498187/system-web-extensions-trouble-deploying-net-framework-4-website-on-iis7 The target server to install on did not have MVC3; so I had to include it in the XCOPY deploy