Coding a Planning Poker app

The 2 week sprint is over, the scope for the next sprint has been established by management, it’s time to estimate. Many programmers will likely have to go through the process of estimating the next chunk of work to get a feel for how much of the chunk can be done in the next couple of weeks.

There are many ways to go about estimating, using the Fibonacci sequence, the modified Fibonacci sequence, t-shirt sizing esimating, etc. My team tends to go for the Fibonnaci sequence to measure the complexity of tickets in order to story point.

Once upon a time in a not-so-digital world one might use physical playing cards to do the estimation. But now, we have no choice but to use software to fill the void! My team tend to go for planning poker apps like planningpokeronline.com

Image of planningpokeronline

Where members of the team choose the card they think represents the complexity of the ticket in question and then at the end – when everybody has voted – the cards are turned and the estimations shown, followed by a discussion on why people chose said card.

Anyway, planningpokeronline.com is indeed a great app, but it only allows you to play a certain amount of games before trying to sell you the premium version, which does my bloody head in.

So this week, I decided to create my own, free version.

My planning poker app

At the time of writing this blog, I’ve got the app deployed at lukegarrigan.github.io. You should be greeted with a button and nothing else, no fluff.

When you click Start new game a socket connection is established with the server – currently hosted on heroku. When the socket connection is made the server makes use of socket.io‘s rooms, which is a server-only concept that allows us to split sockets into an arbitrary channel. I generate an id using short-uuid to represent a room, this id is then passed back to the front-end which then handles the routing.

io.on('connection', (socket) => {
    console.log('a user connected', socket.id);
    let roomId = socket.handshake.query['roomId'];
    if (!roomId) {
      roomId = short.generate();
      socket.emit('room', roomId); 
    }
    socket.join(roomId);

As shown above the server emits a room event socket.emit('room', roomId); passing along the generated id back to the front-end. The front-end then stores the socket connection to state, which is just vue.js state management library. Then we route the user to the path game/${roomId}.

    const socket = io(process.env.VUE_APP_SERVER);
    store.commit("setSocket", socket);
    store.state.socket.on("room", (roomId: string) => {
       router.push({ path: `game/${roomId}`});
    });

Something like this:

Then you’re asked to enter your name:

When you hit enter the front-end emits a name event for the server to pick up:

  public enteredName(name: string) {
    store.state.socket.emit('name', name);
    this.modal = false;
  }

The server then emits this information to all players in your room:

function updateClientsInRoom(roomId) {
  const roomPlayers = players.filter(p => p.roomId == roomId);
  io.to(roomId).emit('update', roomPlayers);
}

But of course, in our scenario there’s no other players to send the information to as we’ve created a new game with the intention of inviting our team to play!

Let’s invite our team by clicking the Invite players button:

A little bit of hacking was needed to get this to work on the front-end. In order to copy something to the clipboard the value that is to be copied must be within an input tag. My button is quite clearly not an input so I had to create a temporary input which gets appended to the body, the value of the current url is put into that input – window.location.href – we then copy the value to the clipboard and finally remove the element.

public copyToClipboard() {
    const tempInput = document.createElement("input");
    tempInput.value = window.location.href;
    document.body.appendChild(tempInput);
    tempInput.select();
    document.execCommand("copy");
    document.body.removeChild(tempInput);
    this.showCopiedToClipboard = true;
    setTimeout(() => this.showCopiedToClipboard = false, 3000);
  }

So, go ahead and send the link to your team!

So Bill is quite eager and dished out a vote quite early:

That vote is sent to the server through our socket connection:

  public performVote(vote: string) {
    store.state.socket.emit('vote', vote);
  }

And then subsequently sent to all the clients in the room so they can see that Bill has voted:

    socket.on('vote', (vote) => {
      let player = players.find(p => p.id == socket.id);
      if (player) {
        player.vote = vote;
      }
      updateClientsInRoom(roomId);
    });

When we’ve all voted, it’s time to show the votes. Let’s click Show Votes!

This follows the same process as above, sending a ‘show’ event which gets sent to all clients and upon receiving the event on the front-end we set showVotes to true which just makes the votes visible:

    store.state.socket.on('show', () => {
      this.showVotes = true;
    })

Jeez, turns out Elon is pretty optimistic when it comes to estimating, who’d have guessed?

And that’s pretty much it for this weeks hack, it’s been fun to make something I might actually use more than once! Hope you’ve enjoyed the blog, follow me on twitter https://twitter.com/luke_garrigan where I just talk even more dribble!

2 comments

Leave a Reply