Initial user experience

Lincoln Anderson
Lincoln Anderson
May 4, 2022

Previously I completed coding the initial System Model.

Also since then, I implemented the Firebase authentication layer on the Timers themselves. It’s going to make it easier to debug once I invite other people to try out the user flow. I already have some helper methods and components for this, so I just need to add a “userId” field to the Timer interface, and wrap the entire app in the Authentication Layout.

It’s time to design an initial user flow and bind it to the model.

User flow

Here’s a basic flow that covers creating and operating timers, which I quickly created in the Obsidian note-taking app using the Mermaid plugin.

Note that this user flow is focused on what you are doing, not what screen you’re looking at. I’ve seen teams take user flows like this and map each node to a unique screen or page in their app. This is premature and results in required checks and awkward redirects on each page. Instead, I like to think about the scope of concern for each node.

In the “view my timers” node, the scope of concern is the user’s identity and all of their associated timers. In every other node, the scope of concern is a specific timer. Everything else is derived from the timer’s state. This means that I only need to create two page routes, and can handle what is displayed on them through conditional rendering.

Basic elements

Instead of designing a mockup in a graphical program, I’m going to just list the elements that need to exist on each user flow node. I want to get the basic flow working before I get caught up in the details.

  • View my timers
  • ~List of links to user’s timers
  • ~“Create timer” button
  • Create segments
  • ~List timer segments
  • ~Form to add new segment
  • ~~Label input
  • ~~Duration input
  • ~~“Add segment” button
  • ~“Start timer” button
  • Operate timer
  • ~Timer countdown
  • ~“Pause” button
  • ~“Resume” button

After creating the page routes and putting the elements on the page, I bound them to the system model, adding conditions and custom event handlers as needed. I created some special UX functionality in key places:

  • When clicking the “Create timer” button, I called the Firebase method to create the record, then used a NextJS redirect with the resulting Id.
  • I disabled the “Start timer” button until there was at least one segment added.
  • I disabled the “Pause” button when the timer is paused, and the disabled “Resume” button when it’s not.
  • ~To centralize the logic for these, I added “canPause” and “canResume” props to the LiveTimerState, and bound them to the buttons’ disabled state.
  • I added the same “constant update” and “alarm sound” code from our earlier examples to the “Operate timer” screen.

Next steps

At this point I’m very tempted to make the screens beautiful, or add some extra features. But, I haven’t validated that what I’ve done so far is working, or if my hypothesis of what would be desirable or useful is correct. This is where a lot of projects run astray, where teams find themselves locked in meeting rooms debating value, or spending weeks sending designs through rounds of review without talking with users. I’ve got to get some other eyes on this before I go any further. I’m going to start with my co-workers, who use MarinaraTimer frequently. Wish me luck!

« previous postnext post »