Pelmodoro - a Pomodoro app built with Elm
Tags: functional, productivity, elm, javascript
Published: 2021-07-25 21:54:23

[**Pelmodoro**](https://www.pelmodoro.com/) is an app that helps you use the _pomodoro technique_ in your daily life. If you don't know about it take a look at the [Wikipedia](https://en.wikipedia.org/wiki/Pomodoro_Technique) article.

There are many Pomodoro apps out there of different sizes and colors. After using some of them I tried to build my own. **Pelmodoro** is the result of that effort.

The app has the traditional timer and a few other features:

- Personalize the number of rounds and sessions duration (work, break, and long break);
- Control how the timer will behave after each session ends;
- Control how you will be notified about session endings;
- Themes!!! 🎨
- Spotify integration to play any playlist during your work rounds;
- Rate your work sessions;
- App usage stats, including worked minutes, breaks, and more;
- Import/export your stats data.

On top of that, **Pelmodoro** is an [off-line first](http://offlinefirst.org/) [PWA](https://en.wikipedia.org/wiki/Progressive_web_application), so you can install it on your mobile device or desktop. I have been using it as a stand-alone app with Edge's PWA support.

![Pelmodoro with themes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b7dbjjvqeumg1n29eqhf.png) 
_Pelmodoro running as a PWA on my desktop and some of the themes you can choose from_

The project is open-source and you can check it out @ [GitHub](https://github.com/eberfreitas/pelmodoro).

## Yet another Pomodoro app? Why?

There isn't one reason why, and the answer could be just "because I wanted" 😁 But I also wanted to create something with [**Elm**](https://elm-lang.org/), a language I've been using daily for the last year and a half, but that I have never used to build something from my own.

Besides, I was using [Habitica](https://habitica.com/) (an RPG game like that helps you achieve your goals and daily tasks) to track my productivity but I felt it was overkill. The idea was to replace Habitica building some of the features that made sense for me into the Pomodoro app, mainly the round rating system.

![Calendar with stats](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mcjzw3llh5c0yu2bka3g.png) 
_The calendar @ the stats screen also dubs as a graph showing your most productive days, inspired by GitHub's graph_

## Making it work

The main tool I used to build the app was the [**Elm**](https://elm-lang.org/) language and its ecosystem. It is not a large ecosystem, but it offers lots of quality packages that really surprised me during development.

**Elm** is a functional language, strongly typed and pure (with controlled side effects) targeted at front-end development. Although it is not popular, I was impressed to see that most of the problems I needed to solve were already solved by the community.

- For the timer rendering I used SVG and the excellent [official package](https://package.elm-lang.org/packages/elm/svg/latest/) to create and handle SVG documents;
- I used [elm-css](https://package.elm-lang.org/packages/elm/svg/latest/) for the CSS, which allowed me to write safe and dynamic styles;
- For dates handling I used the [date](https://package.elm-lang.org/packages/justinmimbs/date/latest/) package;
- The calendar on the stats area was easily solved using the [calendar](https://package.elm-lang.org/packages/abradley2/elm-calendar/latest/) package.

For some of the features, I had to use JavaScript, mainly the Spotify integration. Luckily Spotify has a [reasonlably complete documentation](https://developer.spotify.com/documentation/web-api/) for its API. After fighting the [PKCE](https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow-with-proof-key-for-code-exchange-pkce) auth system, developing the integration was easy.

To persist the app's state I used both [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) (for settings and the timer state) and [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) (to persist usage stats). Instead of using the IndexedDB API directly, I used [Dexie](https://dexie.org/) which abstracts most of the IndexedDB complexities.

To play sounds I used the [howler.js](https://howlerjs.com/) lib.

![Rounds rating](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dbqigf35j6xoe8zk6cwl.png)
_Rate your work rounds and check your stats_

## Making it beautiful

After being satisfied with the features and the way everything looked and worked, I showed my code to some people that gave me valuable feedback, mainly about my code structure. In about two days I refactored my app's structure completely. When I was done I had a PR [adding 5,934 lines and removing 3,756](https://twitter.com/eber_freitas/status/1418434236084797443).

If that was a JavaScript project, that would be a very daunting PR, but I'm using Elm so refactors are safe and cheap. If it compiles it probably works as intended, so I could just merge the PR without blinking twice.

The original code structure grew very organically during development, which produced a functional code but was ill-organized. One example was that I was separating `Model`, `Msg`, and `Types` on different modules. The idea was to avoid [import cycles](https://github.com/elm/compiler/blob/9d97114702bf6846cab622a2203f60c2d4ebedf2/hints/import-cycles.md), but that was just evidence that my code needed better structure.

Looking at the [Real World](https://github.com/rtfeldman/elm-spa-example) application I could see that there were better ways to structure my modules using nested TEAs and keeping the `Main` module as a hub for everything in the app.

My original `update` function [was massive](https://github.com/eberfreitas/pelmodoro/blob/6d4a9e16b254d6b27fd2d5c8699657bbcb6b226d/src/Main.elm#L465), but after separating messages for each ["page"](https://github.com/eberfreitas/pelmodoro/tree/main/src/Page) I ended up with a more organized, contained, and easy to understand code.

There is a lot to talk about code patterns with Elm so I would recommend reading [Elm patterns](https://sporto.github.io/elm-patterns/) for a more in-depth study.

I also implemented a few stylistic decisions to standardize my code such as:

- Avoid exposing constructors in module definitions;
- Avoid exposing functions and types when importing modules;
- When aliasing a module, use the module's own name, mimicking Elixir's `alias`. Ex.: `Html.Attributes as Attributes`;
- In case of name conflicts a) don't alias at all or b) join module's names. Ex.: `Svg.Attributes as SvgAttributes`;
- Prefix every view function with `view` 👀

The idea behind most of these was to make the code more explicit, making it clear where types and functions are coming from as well as their effects.

![Most productive hours](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f8yzlj4wwc188x661peh.png) 
_Monthly stats, including your most productive hours_

## Final thoughts

Although I did this big refactor I know my code structure and design could improve at various places, but I want to finish the project. I feel like software in general is never really done, except for a few outliers, so I need to stop investing so much time on this project that is already working very well for me. That way I can invest my time on another side project that will suck out all my free time 🤡

In general, I'm very satisfied with the final result and have been using the app daily. With some luck other people will find it useful as well and if you are one of those people I'll be extremely happy to know 😊