This is the first of a few posts that I’m planning regarding discussion about how we implement and work on the desktop and standalone parts of Firefox Hello. We’ve been doing some things in different ways, which we have found to be advantageous. We know other teams are interested in what we do, so its time to share!
First, a little bit of architecture: The panels and conversation window run in content processes (just like regular web pages). The conversation window shares code with the link-clicker pages that are on hello.firefox.com.
Hence those parts run very much in a web-style, and for various reasons, we decided to create them in a web-style manner. As a result, we’ve ended up with using React and Flux to aid our development.
I’ll detail more about the architecture in future posts.
The Flux Pattern
Flux is a recommended pattern for use alongside React, although I think you could use it with other frameworks as well. I’ll detail here about how we use Flux specifically for Hello. As Flux is a pattern, there’s no one set standard and the methods of implementation vary.
The main parts of a flux system are stores, components and actions. Some of this is a bit like an MVC system, but I find there’s better definition about what does what.
An action is effectively a result of an event, that changes the system. For example, in Loop, we use actions for user events, but we also use them for any data incoming from the server.
A store contains the business logic. It listens to actions, when it receives one, it does something based on the action and updates its state appropriately.
A component is a view. The view has a set of properties (passed in values) and/or state (the state is obtained from the store’s state). For a given set of properties and state, you always get the same layout. The components listen for updates to the state in the stores and update appropriately.
We also have a dispatcher. The dispatcher dispatches actions to interested stores. Only one action can be processed at any one time. If a new action comes in, then the dispatcher queues it.
Actions are always synchronous – if changes would happen due to external stimuli then these will be new actions. For example, this prevents actions from blocking other actions whilst waiting for a response from the server.
What advantages do we get?
For Hello, we find the flux pattern fits very nicely. Before, we used a traditional MVC model, however, we kept on getting in a mess with events being all over the place, and application logic being wrapped in amongst the views as well as the models.
Now, we have a much more defined structure:
- Components/views are a place for displaying, there’s some logic about what to display, but it isn’t business logic.
- The business logic exists in the stores, and is triggered by actions.
- You can follow an action through and find out which stores listen for it, and what they do.
- Actions themselves are well-defined with specified parameters.
React provides the component structure, it has defined ways of tracking state and properties, and the re-rendering on state change gives much automation. Since it encourages the separation of immutable properties, a whole class of inadvertent errors is eliminated.
There’s also many advantages with debugging – we have a flag that lets us watch all the actions going through the system, so its much easier to track what events are taking place and the data passed with them. This combined with the fact that actions have limited scope, helps with debugging the data flows.
Simple Unit Testing
For testing, we’re able to do unit testing in a much simpler fashion:
- Components/Views are tested by setting up their state and/or properties and ensuring the correct elements are displayed:
- Stores are tested by setting an initial state, sending an action, and checking the resultant state:
We therefore have many tests written at the unit test level. Many times we’ve found and prevented issues whilst writing these tests, and yet, because these are all content based, we can run the tests in a few seconds. I’ll go more into testing in a future post.
Here’s a few references to some of the areas in our code base that are good example of our flux implementation. Note that behind the scenes, everything is known as Loop – the codename for the project.
Conclusion and more coming…
We’ve found using the flux model is much more organised than we were with an MVC, possibly its just a better defined methodology, but it gave us the structure we badly missing. In future posts, I’ll discuss about our development facilities, more about the desktop architecture and whatever else comes up, so please do leave questions in the comments and I’ll try and answer them either direct or with more posts.
This week, I’m upping the ante a bit: I’m going to live-hack on Firefox for an hour and a half for the next few Wednesday’s on Air Mozilla. I’m calling it The Joy of Coding1. I’ll be working on real Firefox bugs2 – not some toy exercise-bug where I’ve pre-planned where I’m going. It will be unscripted, unedited, and uncensored. But hopefully not uninteresting3!
Anyhow, the first episode airs this Wednesday. I’ll be using #livehacking on irc.mozilla.org as a backchannel. Not sure what bug(s) I’ll be hacking on – I guess it depends on what I get done on Monday and Tuesday.
Anyhow, we’ll try it for a few weeks to see if folks are interested in watching. Who knows, maybe we can get a few more developers doing this too – I’d enjoy seeing what other folks do to fix their bugs!
Anyhow, I hope to see you there!
Common (excluding Website bugs)-specific: (16)
- Fixed: 432675 – Revise layout of Alarms option pane in preference dialog
- Fixed: 639284 – Metadata of “Provider for Google Calendar” extension are not translated at AMO
- Fixed: 909183 – calIDateTime.compare returns incorrect result with floating timezone
- Fixed: 941425 – Yearly rule “Last day of a month” can’t be set with the UI and is wrongly displayed in the views.
- Fixed: 958978 – Yearly recurrences with BYMONTH and more BYDAY are displayed wrongly if the last day of the month is not displayed in the view
- Fixed: 985114 – Make use of CSS variables
- Fixed: 1072815 – Multiple locales were missing in Lightning 3.3.1 release
- Fixed: 1080659 – Converting email into task/event fails when localization uses regular expression special characters
- Fixed: 1082286 – [icaljs] Date/Time Picker seems to have a timezone error
- Fixed: 1107388 – No auth prompt is shown when subscribing to CalDAV calendars [domWin.document is null]
- Fixed: 1112502 – Right clicking on a recurring event, and bringing up the attendance sub-menu gives unreadable titles
- Fixed: 1114504 – Extra localization notes for bug 493389 – Provider for Google Calendar cannot sync tasks
- Fixed: 1115965 – Provide filename and line number in cal.WARN and cal.ERROR
- Fixed: 1117324 – Improve stack trace for calListenerBag
- Fixed: 1117456 – Run unit tests on ical.js as well as libical
- Fixed: 1118489 – promisifyCalendar mis-invokes Proxy constructor
Sunbird will no longer be actively developed by the Calendar team.