Category Archives: Dev

How (not) to do Tagging in Angular.js

I’ve been working on getting tagging working in Orbital Feed Reader. Tagging feeds was really brilliant in Google Reader and I want that in the feed reader I use. So I’m building it.

The Setup

You can store tagging in your database a few different ways, but here’s the way I chose:

                         +----------+
      +----------+       | feed_tag |
      | feeds    |       |----------|
      |----------|       |  tag_id  +---+
      |  id      +-------+  feed_id |   |   +--------+
      |  name    |       +----------+   |   | tags   |
      |  url     |                      |   |--------|
      |  unread  |                      +---+  id    |
      +----------+                          |  name  |
                                            |        |
                                            +--------+

You’ve got feeds and tags, those have an ID number. You’ve got a table linking those by ID number. Easy.

The Goal

I wanted to be able to toggle between either a simple list of feeds or a list of tags with feeds underneath.

<script type="text/ng-template"  id='feedline.html'>
  <div class="feed" id="feed-{{feed.feed_id}}" 
    ng-class="{'is-editable': editable, 'is-selected': feed.isSelected}" 
    ng-click="select(feed)"  >
      {{feed.feed_name}} 
      <span class="feedcounter">{{feed.unread_count}}</span>
      <a ng-show="editable" ng-click="editFeed(feed)">âš™</a>
  </div>
</script>
<ul id='feeds' ng-hide="showByTags" >
  <li ng-repeat="feed in feeds" ng-include="'feedline.html'" ></li>
</ul>
<ul id='tags' ng-show="showByTags">
  <li class="tag" ng-repeat="(tag, feeds) in tags" >
    <div id="{{tag}}" ng-click="select(tag)" 
      ng-class="{'is-selected':tag.isSelected}" >
      #{{tag}} 
      <span class="feedcounter">{{tagUnreadCount(tag)}}</span> 
    </div>
    <ul>
      <li ng-repeat="feed in feeds" ng-include="'feedline.html'"></li>
    </ul>
  </li>
</ul>

My first stab at doing this was very wrong and I wanted to share that so you don’t go down that dumb path on your project.

The Wrong way to do tagging

I decided I would pull the feeds in two different ways. I would pull feeds from the database as a normal list, but then I would also pull a list of tags with feeds underneath.
This was my first mistake. Hitting the DB twice for the same data means I’m doing lots of duplication and sending lots of information over the internet (slooooow) twice. It also means keeping lists in sync which is also a code smell that means you are probably not keeping things DRY.

So I pull feeds from the db once like this:

SELECT
  feeds.id AS feed_id,
  COALESCE(u_feeds.feed_name,feeds.feed_name ) AS feed_name,
  feeds.feed_url,
  COALESCE(u_feeds.icon_url, feeds.icon_url ) AS icon_url,
  COALESCE(u_feeds.site_url, feeds.site_url ) AS site_url,
  feeds.lASt_updated,
  feeds.lASt_error,
  u_feeds.private,
  SUM(IF(COALESCE(ue.isRead,1)=0,1,0)) AS unread_count
FROM user_feeds AS u_feeds
INNER join feeds AS feeds
  ON u_feeds.feed_id = feeds.id
LEFT OUTER JOIN user_entries AS ue
  ON ue.feed_id=feeds.id
  AND u_feeds.owner = current_user_ID
GROUP BY feeds.id,
  feeds.feed_url,
  feeds.feed_name,
  feeds.icon_url,
  feeds.site_url,
  feeds.lASt_updated,
  feeds.lASt_error,
  u_feeds.private

And then pull feeds by tag like this:

SELECT
COALESCE(tags.name,'Untagged') AS tag,
COALESCE(tags.id, null) AS tag_id,
feeds.id AS feed_id,
COALESCE(u_feeds.feed_name,feeds.feed_name ) AS feed_name,
feeds.feed_url,
COALESCE(u_feeds.icon_url, feeds.icon_url ) AS icon_url,
COALESCE(u_feeds.site_url, feeds.site_url ) AS site_url,
feeds.last_updated,
feeds.last_error,
u_feeds.private,
SUM(IF(COALESCE(ue.isRead,1)=0,1,0)) AS unread_count
FROM user_feed_tags AS uft
INNER JOIN tags AS tags
  ON tags.id = uft.tag_id
INNER JOIN user_feeds AS u_feeds
  ON uft.user_feed_id = u_feeds.id
INNER JOIN feeds AS feeds
  ON u_feeds.feed_id = feeds.id
LEFT OUTER JOIN user_entries AS ue
  ON ue.feed_id=feeds.id
WHERE u_feeds.owner = current_user_ID
GROUP BY
feeds.id,
feeds.feed_url,
feeds.feed_name,
feeds.icon_url,
feeds.site_url,
feeds.last_updated,
feeds.last_error,
u_feeds.private,
tags.name

UNION
SELECT
'Untagged' AS tag,
null AS tag_id,
feeds.id AS feed_id,
COALESCE(u_feeds.feed_name,feeds.feed_name ) AS feed_name,
feeds.feed_url,
COALESCE(u_feeds.icon_url, feeds.icon_url ) AS icon_url,
COALESCE(u_feeds.site_url, feeds.site_url ) AS site_url,
feeds.last_updated,
feeds.last_error,
u_feeds.private,
SUM(if(COALESCE(ue.isRead,1)=0,1,0)) AS unread_count

FROM user_feeds AS u_feeds
LEFT OUTER JOIN user_feed_tags AS uft
  ON uft.user_feed_id = u_feeds.id
INNER JOIN feeds AS feeds
  ON u_feeds.feed_id = feeds.id
LEFT OUTER JOIN user_entries AS ue
  ON ue.feed_id=feeds.id
WHERE u_feeds.owner = current_user_ID
  AND ISNULL(uft.user_feed_id)
GROUP BY
feeds.id,
feeds.feed_url,
feeds.feed_name,
feeds.icon_url,
feeds.site_url,
feeds.last_updated,
feeds.last_error,
u_feeds.private

For feeds by tag I then would use underscore.js’s excellent _.groupBy() method. It’s easy to see tags in angular using groupBy and I thought it was good enough.

It worked! Sure, it was inefficient, but I’m pragmatic – I just wanted to get this done and out the door so I could use it. No point in prematurely optmizing, right?

Next step was making sure I could show the unread count per tag. And keep it all in sync between the tags and the feeds themselves. This is when I smelled my stinky code smells. Every time I marked an entry in a feed as read I would have to search it out multiple times on multiple lists. This means this information was stored in multiple places – and it’s always good to follow the DRY principle. Don’t Repeat Yourself.

The Right(er) way to do tagging

Let’s regroup and try this again. I want to have ONE place where I pull info from my DB about a feed, and I want to only update information in one place but display it in multiple ways.

I changed it so I pull a straight list of the feeds, but use the group_concat SQL function to also pull in all the tags associated with a feed.

SELECT 
  u_feeds.id AS feed_id,
  COALESCE(u_feeds.feed_name,feeds.feed_name ) AS feed_name,
  feeds.feed_url, 
  COALESCE(u_feeds.icon_url, feeds.icon_url ) AS icon_url,
  COALESCE(u_feeds.site_url, feeds.site_url ) AS site_url,
  feeds.last_updated,
  feeds.last_error,
  u_feeds.private,
  SUM(IF(COALESCE(ue.isRead,1)=0,1,0)) AS unread_count,
  GROUP_CONCAT(DISTINCT COALESCE(tags.name,'Untagged')) as tags
FROM $user_feeds AS u_feeds
INNER JOIN $feeds AS feeds
  ON u_feeds.feed_id = feeds.id
  AND u_feeds.owner =  $current_user->ID.
LEFT OUTER JOIN $user_entries AS ue
  ON ue.feed_id=feeds.id
LEFT OUTER JOIN $user_feed_tags uft
  ON uft.user_feed_id = u_feeds.id
LEFT OUTER JOIN $tags tags
  ON uft.tag_id = tags.id
GROUP BY 
  feed_id,
  feed_url,
  feed_name,
  icon_url,
  site_url,
  last_updated,
  last_error,
  private

When I want to show a list of feeds I can just go with the same solution I had before.
But when I want to group by tags I can apply a couple of interesting tricks to solve this for me.

    refresh : function refresh(callback){
  _isLoading = true;
  $http.get(opts.ajaxurl + '?action=orbital_get_feeds')
  .success( function( data ){
    //Here is our simple feed list
    _feeds= data;

    //Now lets get a list of all the unique tags in those feeds
    var taga = _.unique(_.pluck(_feeds, 'tags').join().split(","));

    //For each tag, lets build up a list of the feeds that have that tag
    _.each(taga, function(tag){
      _tags[tag] = _.filter(_feeds,function(feed){
                      return _.contains(feed.tags.split(","),tag);
                    });
    })
    _isLoading = false;
    //Should we do some extra work?
    if(callback){
      callback(_feeds);
    }
  });
},

What’s going on there?
I want to get an array of all my unique tags, so first thing I do, I use _.pluck() to give me an array of the tags element of each feed.
This gives me:

[
  ["art,gifs"],
  ["art"],
  ["Untagged"],
  ["gifs"],
  ["Untagged"],
  ["Untagged"],
]

Then I join() those together (the default join is with a comma):

"art,gifs,art,Untagged,gifs,Untagged,Untagged"

I split(“,”) that on commas:

["art","gifs","art","Untagged","gifs","Untagged","Untagged"]

And finally we just grab the _.unique() values:

["art","gifs","Untagged"]

So now we have a list of the tags for our feeds – how exciting! Let’s build a list of the feeds for each of these tags.
So, for _.each() tag in our list of tags we go back to our list of feeds and _.filter() it – we want only a feed where the feed.tags _.contains() the tag.

Why’s this so much better? Well – if we update the `unread_count` for a feed or change the name of a feed – it updates the underlying object. That means your change percolates up automatically through angular databinding to every part of your UI. No more keeping lots of lists in sync.

And now I’ve bored even myself to death with this so I guess that’s it.

Orbital Feed Reader 0.1.5 Codenamed “CHICKEN HAT” is out!

It’s been a busy couple of months for me. I’ve had my first user feedback on Orbital Feed Reader – a fella who’s trying to use it on GoDaddy’s Awful Hosting. I haven’t fixed all of his issues but I’m getting closer. Lil Peanut has been smiling, grabbing things, laughing, and peeing on me.

http://instagram.com/p/fYqTrGjpqD/

What’s in 0.1.5? Some bug fixes, some UI fixes and a way to change the SORT ORDER!

Feed Names

When you put in a feed to autodiscover it will show you the actual feed names instead of just the feed URL. This is important because it lets you choose among many feeds in a smarter way.
Feed Names on Orbital

Sort Order

Henry Vitoski pointed out that most people will want to use Orbital differently than I do. I’m a completist, I like to start at the bottom and read EVERY SINGLE THING. The rest of you apparently are more interested in seeing new things first and then delving into the past. Now there’s a way to change the sorting. It will remember whatever you choose.
See more details at the changelog page.

Orbital Reader is live

After a lot of subway rides and wednesday nights, you can now install the best feed reader for WordPress right from your wordpress admin panel.

Once you are logged into your wordpress admin screen:

  1. Click Plugins
  2. Click Add New
  3. Search the Codex for “Orbital”
  4. Install Plugin
  5. Activate Plugin

The plugin should show you where it is, tell you how to use it, and away you go!

THE WORLD IS yOURS

It’s early days on this thing, so please tell me what’s difficult, what’s wrong, what could be improved.

SVG icons in Orbital

Screen Shot 2013-08-06 at 5.51.25 PM

In Orbital Feed Reader I’m trying to make sure that I’m doing things in smart ways – I don’t have much time to work on it. One thing that people do when they create software is make icons – lots of them for different sizes. This has always seemed strange to me – most icons aren’t photos, so I don’t get the point.  There’s a graphics format called SVG – Scalable Vector Graphics  – that automatically resizes itself at any resolution.  It’s smooth and very small – and all modern browsers support SVG.

So I’m trying to use SVG for any icons that aren’t available as unicode symbols.

Those two icons are free icons for Feeds and OPML and they are both SVG.  This was such an easy choice that I don’t know why everyone doesn’t do it. The only thing that was a little off is that the OPML icon had a little bit more blank space than I wanted, so I edited it in the free, open source SVG editor Inkscape. Here’s the new version I made.


Creative Commons License BY-SA 3.0
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

The folks over at liquidorb made this icon available under a CC-BY-SA license so I’m giving them credit and making my derivative work available under the same license.

Unicode Icons in Orbital

Font icons are kind of fashionable in web development these days. What’s awesome about them is that you get all the clever wonderful benefits of how good browsers are at rendering webfonts. In a way, a web font used for font icons is like a png sprite for SVGs – you get one server round trip and then a bunch of reusable images.

It’s a good idea if you need to show some custom icons. Me, I’m too cheap for that. For Orbital I’m trying to make sure that there is a very small footprint. Rather than using a custom web font I’m just getting creative with the unicode tables.

Screen Shot 2013-08-06 at 5.40.43 PM

As far as I can tell, unicode has some pretty easy to understand symbols for add (+), refresh(⟳) and edit(✎). So I get decent, scalable icons for zero server round trips!

Wordprss is now Orbital Feed Reader

When I talked with Aaron about the name of the feed reader for WordPress I’m working on, he pointed out that it seems awfully close to the actual name WordPress. It also is impossible to say. I asked lots of folks for new name ideas, this is the one that stuck for me.

Orbital, because:

  • You can see a lot from orbit
  • I really like the band

  • It is easy to say and there isn’t a feed reader already named Orbital

In other news, I’m working with folks to install it on their servers and show them how to use it. Be my Alpha Tester, please!