Update: It’s come to my attention that the formatting looks a bit funky on mobile for this article. For the best viewing experience, read it on a desktop :) thanks!

Adam launching his product with just 2 hours of development time.

Adam, like myself, is a software engineer who suffers from “dormant code syndrome”, where projects are built to about 80% of their finished state, abandoned and left to collect dust.

Having spoken to a number of both professional and hobbyist software engineers; this seemed like a trend.

A few days later Product Hunt retweeted his project and it caught my eye. The product, a black cap with a Bitcoin symbol on, was nothing to write home about, but the fact he shipped a project in 2 hours and began generating revenue was definitely interesting.

I briefly took a look through my private Github repositories and realised I have about 100 dead projects. Projects that I started excitedly then left to die in an unmarked grave after pumping weeks of my free time into them.

This had to stop.

I set myself a public challenge that ended up getting a fairly large amount of attention; the pressure’s on.

I wanted to take a simple idea that I’d had, write and ship it within a week.

I sat down and began to work, got stuck on a simple problem and sat on my bed for an hour browsing Reddit on my phone. Project over.

A few days later I opened Twitter back up and someone had favourited my self-challenge tweet, and I realised I had 2 days left to complete it. I decided I’d give it one last shot, mainly because I was bored out my mind and Overwatch was annoying me.

Swift beginnings

That night I was browsing /r/Documentaries on Reddit for some interesting stuff to watch while I wrote a university assignment. I found about 6 hour long documentaries to watch, and put the links into a notepad file on my desktop for later viewing.

Oh damn, that’s a good idea. Let’s queue these tabs up to watch so I don’t have to spend 3 seconds of my procrastination time manually copying and pasting the links into Chrome.

Now, at this point, any normal person would have found a pre-existing solution that solved this problem, and never looked back. But I’m a software engineer. I need to do this myself, for literally no reason whatsoever.

Enter, stage left — Tab Queue.

Tab Queue is a simple, humble Chrome extension that comprises of about 50 lines of code (including styling) that sequentially queues tabs up for later viewing. It syncs to your Chrome account too, cool!

Live tweeting the development

Let’s goooooooooo

My girlfriend works at a bar and was closing up that evening. At just past 9pm, I decided to get the ball rolling, and keep myself occupied with both frequent Git commits and tweets regarding the development process.

12 minutes in and I’m already at a roadblock. Excellent.

I needed some logos for the browser action and context menu items, so I grabbed a few royalty free icons and downloaded GIMP.

5 minutes later and my crappy shared internet connection managed to download 130mb of data. Incredible.

I paid the $5 fee for the Chrome store and carried on going.

Notice the time difference? It took me 3 minutes to change an SVG to the correct sizes for a Chrome extension, and 40 minutes to mess around with bad code that implemented Chrome’s APIs. For no reason.

Here’s what the backend currently looks like:

Good stuff. What you didn’t see here was the 30 minutes of writing 50 lines of code that achieved the same thing as chrome.contextMenus...

So far we’re creating a context menu item with the title “Add Tab To Queue” and enabling it. This means we have a quick right click -> add option whenever we’re on a webpage that we want to save!

Web extensions have a a great featured called LocalStorage. This is an isolated key:value storage facility for data you want to share in your extension, which seemed like the logical place to saved tab data to.

I hadn’t used this before so it took about 30 minutes to get everything working nicely.

I decided at this point that the UI should eventually have a nice table of saved tabs that could be opened specifically; alongside a button that would open the next tab at index 0.

Here we’re achieving a very simple thing: saving a tab to storage.

There are a few bits going on here, so I’ll walk us through it step by step.

Here we’re capturing a click event on the context menu item. This item returns two objects in it’s callback function, info and tab. We then pass the tab object as a parameter to ParseTab so we can work with it.

We now have tab as an object in our ParseTab function, so let’s get cracking.

First we open up the Chrome Sync storage, which allows us to save data to our LocalStorage tied to our Chrome account. This returns us an object called items.

Items is the key:value store for all data in our LocalStorage, and we need a specific key: data.

Data is an array of objects that we can reference by index whenever and wherever we want, but it doesn’t exist by default. In fact, if we haven’t used our slice of LocalStorage in Tab Queue yet, items is actually an empty object without a data property, so we have to account for that when working with a new installation of the extension.

Next we use the Object.keys API to grab all of the keys from our items object, which returns a handy array of all thekeys that items has.

We next check to see if the array of keys has more than 1 item, and that items.data exists.

If items.data exists, we simply push a new object to it. If it doesn’t, we create the array data and push the object to it. Simple!

Now we need to replace the original items object that our LocalStorage returned with the updated, newly expanded version. We’re doing this with sync.set, and then getting the items back immediately after just to check that it’s working. The chrome.storage.sync.get will eventually be deleted, this was just an idiot check for me in case something went wrong.

Great! We now persist the tab data and can fetch it back straight away.

Ah crap, it might be game over here. My girlfriend sent me a text saying she may be finishing a few hours earlier, so I probably had about an hour left at maximum.

Hurray (kinda), she’s finishing late! I get to work a little harder on this and get it live before heading to sleep. What a dream come true (heh).

Now came the part I’d been dreading: front-end development. This is a part of my skill base that is a work in progress. I will confidently say yes to a back-end task without hesitation, but whenever asked to do some front-end work I umm and ahh for a while.

At this point I’m about ready to call it a night. I could create the most basic UI possible, two buttons and a header, but that wasn’t much to look at.

Fortunately this is 2017 and the internet is a thing, so I found a codepen which had some nice CSS buttons and acquired them. Full credit to Simon Busborg for this code, I doubt I would have gotten much further without you!

About 10 minutes later this was the UI that would be shipped. I was happy, it worked nicely and clearly displayed what it set out to achieve.

Dream: complete.

HTML for the popup and the associated CSS to go along with it.

Some small manual tests were ran in my browser to make sure it worked, and it did! Kind of. The yellow Delete saved tabs button turns red and provides a confirmation button to make sure you didn’t misclick, but that wasn’t working.Let’s take a look at the full source code for the popup to see what’s going on.

Here we’re capturing the click events on 3 buttons, the two that are visible in the popup screenshot above and the red confirmation box ConfirmDelete that appears when you click Delete Saved Tabs.

To handle the click event on the Open Next Tab button, we used this function:

Here we’re getting the top-most item from the items object we played with earlier and temporarily stored that associated URL and image of the tab.

We then spliced the item from the items.data array, which removed it from the queue entirely.

Next we update the LocalStorage copy of items, and then pass the to-be-opened tab object to the OpenSavedTab function.

A simple function. It creates a new tab with the URL of tab, which we just spliced from the items.data array above. We provide the URL, and Chrome opens a new tab to that location and focuses it.

Remember that bug I mentioned in the tweet? Here’s the offending code:

Now, in the popup.css file I have the display property of ConfirmDelete to hidden. This is so it doesn’t cover up the initial yellow button. When we capture a click event on the DeleteAllTabs, my intention was to remove the yellow button and clear the display property of ConfirmDelete, thus rendering it visible. In fact, it did literally nothing. Bollocks. Let’s quickly fix that by giving ConfirmDelete some display property.

Great! Now the yellow button disappears and the red one shows in its place. Bug fixed!

Now, what happens when the user clicks the red button?

We simply grab the LocalStorage associated with our extension (we’re sandboxed against any other extensions so we don’t need to specify an extension ID or anything!) and then clear it. Easy as that!

Hurray! It’s now complete and live on Chrome’s Extension Store. In total we spent 2 hours and 51 minutes developing the extension from scratch, and I couldn’t be happier.

This was an incredible experience, not only to fill my time but it also proved that I was capable of taking an idea and getting it out into the world fairly quickly.

Since launching Tab Queue 2 days ago, nobody has used it! Yay!