This is my biggest project until now. These are its key features:
- PWA.
- i18n support (internacionalization).
- English
- Español
- Español (Argentina)
- Server and client side state management with Redux.
- The
habit reminder
- Dark mode
The initial state
I decided to init the Redux state in the client side, through a <Init />
component with 2 effects:
- Intialize the Redux state.
- Once per day, show a modal telling the user that they have pending habits. (the habit reminder)
Middlewares
Storage
Automatically updated the localStorage
data on every change.
Theme
Automatically updating the html dark
class (tailwind) on every change.
There a 2 types of tasks: tasks and habits.
Tasks
Simple tasks. They have a done
key with a boolean
value. But also have a difficulty level and a due date. Tasks can (or not) be put into groups.
Groups
The groups are a collection of tasks. They have a color, a name and a list of tasks.
Habits
They are pretty similar to tasks, but with a small difference: they have a days
key wich refers to the weekdays that the habit must be done. If the actual day is not included in the weekdays of the habit, it will be shown as disabled in the screen.
The problem
There was a problem with habits: if I wanted the app to work offline, I needed to be able to restart the habits in the client-side. But browser JS cannot execute background processes, like it would in a server.
The solution that I found is to use useLayoutEffect
to check the habit before rendering it. This works with the redux reducer checkHabit
, which sets the done
key to false
. If the habit lastChecked
date is different from the current day, checkHabit
reducer will be called.
Even though it works fine, this is not great for performance. Since this requires to make a date check every single time that a habit is rendered.