Recently I set out a challenge for myself: make a usable prototype of a specific project that I have been meaning to do for a while, and do it over a single weekend. Below I present my experience with such an experiment, where I try to explain some of my decisions.

idea of the project

This project is for my own long term goal management. The basic idea is to make an application that provides an objective value of my satisfaction with my life. This way I can have a very clear understanding of what I want to work on, what I need to work on more and how I’ve been progressing.

The way to achieve this is as follows:

  • Have a list of goals, that can recursively have subgoals
  • Assign a satisfaction value to each of the leaf goals. This is a percentage that indicates how satisfied I feel with my current state with the goal.
  • Then the satisfaction for each non-leaf goal is the (possibly weighted) average of the satisfaction values of the goal’s children subgoals. This should get updated automatically.
  • There are a lot of additional features that could be added but I consider anything else to be optional.

attempt #1

I actually attempted to complete this project a couple years ago using Python and the Kivy GUI framework. At first it went well and using the Kivy language was enjoyable. Then I started struggling, as whenever I would need some non-standard functionality, there wouldn’t be that many resources for how to create new objects with the properties that I needed.

Simply put, Kivy isn’t a very popular framework, and hence there weren’t that many options to choose from in terms of functionality or styling, which meant that I needed to implement a lot of it myself. And I didn’t have a good enough understanding of the framework to implement complicated widgets on my own. Eventually I gave up on this project as I just couldn’t figure out what I was doing wrong when trying to implement drag-and-drop functionality for the goal objects.

This project wasn’t a complete waste of time, as it familiarised me with desktop app development, and the app is somewhat functional, even though it barely has any features. The drag-and-drop functionality actually does work well in some cases and is only broken when scrolling and dragging at the same time. You can view the source code for this old version here. Here is an image of the application in action:

Old project screenshot

attempt #2

I decided to revisit the idea last Friday. I knew that it is possible to package browser apps into desktop apps using Electron, and hence decided to do the project with React, which I was familiar with already. However, I coudn’t figure out a simple way to update the state of an object that can recursively have children of the same type. I spent a significant amount of time searching for a solution, but most of the suggestions either assumed non-recursive types, or looked extremely ugly.

In retrospect, maybe I should have experimented with Redux for state management, but

  • the code was already getting ugly even though I had barely started,
  • Redux did not seem straightforward to start working with it immediately, and
  • I wasn’t even sure if it would solve my problems.

So instead I decided to look for a more suitable framework for my needs.

attempt #3 (the successful one)

I decided to try out Angular. It seemed to be great for this project as it had mutable state and it was straightforward to implement 2-way data binding. Choosing Angular ended up being a great decision.

I looked up an Angular crash course and closely followed the tutorial to set up the basic components for my application. I was lucky that the tutorial was based on building a todo list, which structurally was quite close to what I needed. In a day I had all of the basic logic set up to display the components, and in another day I implemented most of the features to add and remove goals, and to toggle whether subgoals are hidden from view.

components

I used the following components to create the required functionality.

application

The main component of the application, encapsulating all the other components. It contains the overall satisfaction score, a button to add a top-level goal, as well as the goal section

goal section

  • A component that stores all the top level goals
  • Additionally this component handles keyboard events, such as saving when CTRL+s is pressed, as well as loading locally stored goal data at the start of the application.

goal

  • A generic goal object
  • Can have any number (including 0) of goal objects as children
  • Implements functionality to add / delete / hide / show subgoals
  • Any operation that alters the goal structure makes sure that the satisfaction values get updated along the path to the root goal, as well as the overall satisfaction.

goal deleter

This last component is a strange one, and is my solution of going around a restriction I came across while trying to delete goal objects. It is very possible that there is an easier way to achieve my desired functionality but I was not able to figure it out at the time.

The issue is very simple: Angular would not allow me to both emit and consume an event A (say Delete) in the same component. Hence I added this intermediate component. Now if I want goal to consume and emit an event A, I instead have the goal emit A and consume another event B, while goal deleter will then emit B and consume A.

The goal deleter also uses the same trick to update the goal components up the DOM tree when changes occur.

integrating with Electron

Now it was time to integrate the app with Electron as I couldn’t save the state in a file directly through a browser. I was extremely surprised at how simple the setup was: I followed a guide online that basically told me exactly what changes to make, and it just worked. I did have to hackily declare some global Node variables to save state to a file and make some other small changes in the end, but overall this was most definitely the easiest part of the project.

Lastly, I had to style the app to make it look decent. For this I used the Angular Material library, as well as clever ways to denote indentation of subgoals. There are still things that can be improved but I believe that the final product looks really nice.

You can view the source for this project here. Here is an image of the app in action:

Project screenshot

future additions

I am still not fully satisfied with the state of this project. It functions, but there are still a few features that I would like to see added.

  • Firstly, there is no warning when exiting to save the state, and the state is not saved automatically. I want to have either one or the other.
  • Currently everything is white, and therefore it is a bit difficult to distinguish the hierarchy of the projects. I am thinking of doing some sort of colorcoding, where subgoals get lighter shades of the parent goal’s colour, and topmost sections get distinctive / alternating colours. I might also consider making the indentation space coloured (which currently always has a single blue line), if that looks distinctive enough.
  • Not all goals are equal, so I want to add a weight system, where the satisfaction would be counted as a weighted average of the subgoals.
  • Currently deleting a goal with subgoals is too easy, which means that huge portions of the state can be accidentally deleted, so I want to have a popup whenever delete is clicked asking to confirm the action. For a similar reason I also want to add undo/redo functionality in case something goes wrong.
  • Currently there is no way to reorder the goals. I would like to add drag-and-drop functionality to the goal objects, that would allow them to be moved into and out of goals easily
  • It would also be nice to track the daily satisfaction and create some stats based on it. Additionally this would serve as backup in case something goes wrong. This could probably be achieved by using cron jobs or a similar service to daily check the current state.
  • At some point I might consider creating several different themes but that is in no way a priority.
  • I want to add keyboard shortcuts for more actions, and the endgoal is to have the app fully usable with the keyboard only.
  • At some point I might make this into a proper web application that has a central server, so users can register and not have to install any software to use the application, only internet access. However, I am sceptical that there is enough demand for such an application :)

conclusion

This was a fun project, and I am mostly glad to (mostly) finish something that I started a long time ago. It isn’t a difficult application to write but it looks nice and it does exactly what I wanted it to do.

With every new feature I added Angular grew more and more on me. I was a little sceptical to use Angular at first since most comparisons of React and Angular said that Angular is way more difficult to learn. Granted, there are a lot of things that Angular has to offer that I have not yet learned, but I did not seem to struggle with it any more than I did with React. In fact, I found it easier than learning React since the state managing was way more intuitive. All in all, using Angular was a great experience for me.

I also learnt quite a bit about Electron and Material. They weren’t difficult to learn but bettered my understanding of the possibilities to develop good-looking and functional applications.