Pomodoro timer - how good was your pomodoro?

Checking how good was pomodoro that you are working on

Posted on Sun 12 March 2017

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.

tags: javascript,

Comments !