Since my recent post about the Teamwork.com Timer app, I feel more confident writing about my projects and, today, I’m writing about one of my favorites – Teamwork Projects for Chrome, which as you may have guessed is a Chrome extension.
The development of the Chrome extension started in December 2013, with first version going live at the end of January 2014.
After one minor and six patch updates throughout a period of 18 months, I had secretly started working away on a brand new version, modularizing and rewriting the codebase to use CoffeeScript, LESS, and Jade as the extension was initially made before we adopted these technologies. It was basically a complete rewrite and as such from there on it was considered version 2 of the extension.
Roughly a month after I began work, we started talking to HubSpot about an integration – it was decided to use the Chrome extension for the HubSpot side of things whereas on the Teamwork Projects side we’d add the required changes to the codebase (we also created a more future-proof integrations system to make it easier in the future). Lo and behold, I was no longer the only person working on the Chrome extension:
- Our resident designers Ivo and Vsevolod quickly came up with a new beautiful design (just compare it to the old one!).
- Peter S (the S is important, since we now have three Peters working at Teamwork!) did the majority of converting said design to actual LESS and Jade.
- And last but not least, James helped out on the CoffeeScript/functional side of things, as we had a deadline to meet and there was too much to cover on my own.
After more than tripling the amount of commits, we finally released version 2.0.0 on the 19th of August, with further hotfix patches, and eventually during INBOUND 2015, the HubSpot-enabled release, which was what prompted the accelerated development in the first place.
How It Works
The chrome extension environment, both for our extension and in general, could be described as three different contexts:
- Popup: This is the “page” that opens up when you click the browserAction icon in the extension bar. All data is lost when popup is closed.
- Background: This context has no associated page, it is – as you might have guessed – in the background. Background pages are persistent and always stay open/running, although there are also event pages which run on demand, however they are currently not in use by our extension.
To communicate between all of these contexts, we use chrome messaging API, which allows you to send messages both across background scripts and to content scripts in specific tabs. It also supports cross-extension messaging and even native messaging, but neither of those were required for this project.
With the available contexts and messaging systems in mind, here’s what at typical Teamwork Projects for Chrome action involves:
- User opens the popup or right clicks to open the context menu. User clicks a specific action. A message is sent to the current active tab in the current window with the action specified.
- The content script in said tab receives the message and starts building the DOM from a template.
- The content scripts sends a message to the background script requesting projects for the current account.
- Background script receives a message and fires off an AJAX request.
- The AJAX request finishes and the callback sends a response message back to the content script with the data or error message.
- The content script renders the projects (and may repeat steps 6-8 to get task lists, tasks, categories, available people).
- The content script binds input events and, finally, appends the modal to the DOM and makes it visible to the user.
- Further AJAX requests are possibly made to get different tasks, people, etc.
- User submits the form and if it is valid, data is sent via a message to the background script and another AJAX request is made.
- User is informed of success or failure, in the case of the former the modal is closed and a notification is displayed.
What’s with all the messages?
At a glance, it may seem like a very involved process, however the only parts that are more complicated than using a single script, are the decoupled AJAX requests and the internal messaging. Although we are capable of making AJAX requests from a content script context, due to Chrome’s content security policy they can be blocked. Although there are some caveats and ways of making it work (initial versions of our extension didn’t use background AJAX), it is better to make a failsafe system using the messaging API, which is easy to get the grasp of and most developers prefer to modularise/decouple their code regardless.
The notifications API makes this a piece of cake. To avoid having to write a full notification setup and binding buttons’ click functions, we created a simple Notifier class to wrap all of this, allowing us to simply call
notifier.notify with the relevant options:
title: 'Project Created'
message: 'Project has been created successfully.'
url: @currentAccount.installation.apiEndPoint 'projects/' + xhr.id
url is passed, we automatically create a button that opens a new tab with the link provided.
A new feature we added in v2 was a built-in timer. Since we already have an in-app timer and a desktop timer app, we wanted to keep it simple and easy to use. The main goals were:
- Display timer everywhere, but not intrusively
- Starting a timer should be immediate and hassle-free
- Timer should be synchronised across all computers
Displaying the timer on all tabs and windows could have been a very involved process, requiring us to message every tab and add the relevant markup and scripts and then update them all when any action is taken. We would have also needed to make it moveable or hide-able as it could overlap some websites’ interfaces. We found a way around this issue by using the browser icon and setting its badge text and colour. Since there is a limit to number of characters visible, we can only show hours and minutes, but in most cases, seconds are irrelevant and rounding can be adjusted later.
Starting the timer immediately was a shortcut that turned into a feature – since we don’t display the task name in the badge, there is no reason to pick it in advance. This meant you could start a timer and track time immediately, only having to pick a project/task when you stop the timer.
As for synchronisation, we used the same mechanism we use for storing added accounts – chrome.storage.sync. We store start date, time recorded so far and running state and it is then synchronised to the user’s Google account. We can then load any timer data on startup and update it when user interacts with the timer.
I hope you enjoyed getting a little insight to the workings of the Teamwork Projects for Chrome extension and technology behind it. There are still many other parts of the extension I could discuss, so if you have any questions about our extension or Chrome extensions in general, leave a comment below or get in touch with me via Twitter (@ripexz).
If you’re a developer looking for a new home, you’re always welcome to join us at Teamwork.com. Don’t be shy – get in touch!