Krzysztof Żuraw

Pomodoro timer - how good was your pomodoro?

  • #javascript
  • #pomodoro technique

Hello! This week I made the main feature of my pomodoro timer - checking if pomodoro was good or bad. I believe that is one of the ways to measure your productivity. Let’s get started!

How to check how good your pomodoro was?

When did I decide that I want my pomodoro timer to record if my 25 minutes work was worth something I have this burning question: How to do it? Some time ago I used a tool called Kanbanflow. It has great pomodoro extension where you can select if your pomodoro was good. Based on that I started thinking what if at the end of 25 minutes I display modal to the end user: please select how good was your pomodoro?. Which this thought in my head I start coding.

Modals in javascript

First I need some HTML structure for my modal:

<div class="is-hidden modal-overlay">
  <div class="modal">
    <h2 class="modal_question">How was your pomodoro?</h2>
    <div class="modal_buttons">
      <button
        class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored"
        data-productive="true"
      >
        Productive
      </button>
      <button
        class="mdl-button mdl-js-button mdl-button--raised mdl-button--accent"
        data-productive="false"
      >
        Not really productive
      </button>
    </div>
  </div>
</div>

I apply some styling - thanks to that when modal is displayed rest of the web page is dimmed:

.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.6);
}

.modal {
  padding: 20px 30px;
  width: 90%;
  max-height: calc(100% - 150px);
  position: relative;
  min-height: 300px;
  margin: 5% auto 0;
  background: #fff;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

I apply some styling to buttons and questions too:

.is-hidden {
  display: none;
}

.modal .modal_question {
  flex: 2;
}

.modal_buttons {
  display: flex;
  justify-content: space-between;
  flex-direction: row;
  flex: 1;
}

.modal_buttons .mdl-button {
  margin-right: 10px;
}

Thanks to that my modal looks like this:

Modal for pomodoro timer

As I have my CSS & HTML done right now it’s time to write some code in javascript.

Firstly, I have to add a new argument for entry in localStorage - wasGood. It is boolean so I know if this time entry was good or not:

function saveTimeEntryToLocalStorage(startSeconds, endSeconds, type, wasGood) {
  // rest of the function body
  const entry = {
    startTime,
    endTime,
    type,
    wasGood,
  };
  entries.push(entry);
  localStorage.setItem('entries', JSON.stringify(entries));
}

As the saving of entry cannot be invoked when timer stops - as the user have to click the button first with productive or not productive pomodoro I have to introduce two global variables so I can access them not only from timer function:

const modal = document.querySelector('.modal-overlay');
const modalButtons = modal.querySelectorAll('[data-productive]');
let now;
let then;

function timer(seconds, hasBreakAfter = true) {
  now = Date.now();
  then = now + seconds * 1000;
  // rest of timer body
}

modalButtons.forEach((button) => {
  button.addEventListener('click', closeModal);
});

The last 3 lines of code are setting up the event listeners for both of buttons in the modal. When a user clicks one of them I run closeModal:

function closeModal(event) {
  modal.classList.add('is-hidden');
  saveTimeEntryToLocalStorage(now, then, 'Pomodoro', event.target.dataset.productive);
  retrieveTimeEntryFromLocalStorage();
}

It closes modal by adding is-hidden which is equivalent to display: none. Then I simply save entry and retrieve it. As I wanted something different than true or false to be displayed to end user I have updated retriveTimeEntryFromLocalStorage:

function retrieveTimeEntryFromLocalStorage() {
  tableBody.innerHTML = entries
    .map(
      (entry) => `
     <tr>
       <td class="mdl-data-table__cell--non-numeric">${entry.wasGood === true ? '✔' : '✖'}</td>
     </tr>
  `
    )
    .join('');
}

The last thing I have to do was to display modal when pomodoro ends:

function timer(seconds, hasBreakAfter = true) {
  // function body

  if (secondsLeft < 0) {
    // do the break, display notfication, play sound
    if (hasBreakAfter) modal.classList.remove('is-hidden');
  }
}

And timer works! If you want to see it in action go here. That’s all for today blog post - stay tuned for the next. Feel free to comment!

Repo with this code is available on github.

References