Skip to content

Instantly share code, notes, and snippets.

@christiannwamba
Created May 21, 2018 12:55
Show Gist options
  • Save christiannwamba/860594d5ec8b8fd1756686cad7f26c09 to your computer and use it in GitHub Desktop.
Save christiannwamba/860594d5ec8b8fd1756686cad7f26c09 to your computer and use it in GitHub Desktop.

Quite a number of us like to order food. How about we track the delivery of meals right from placing our order to its delivery. In this article, we would be building a food delivery tracking progressive web app (PWA) using Pusher and Vue.js.

Here is what the final demo would look like:

pwa-vuejs-demo

Background

A progressive web app is simply an app that employs various modern technologies to achieve a unique goal. In this article, we will be building a PWA that can engage the user even when they lose connectivity.

To achieve this realtime feature, we will be using Pusher. Pusher is a leader in realtime technologies, and allows us to perform various operations on the web in realtime. Our offline starter project will be an enhanced Vue PWA webpack template. Also, we will be making use of Progressbar.js to implement a smart progress bar functionality on our app and Bulma classes to style our app.

Setup

Install Vue

We will be working with vue-cli, which you can install with:

https://gist.github.com/66980a56f136b9be66a5a909a8d4f5a9

This installs the latest version of Vue CLI globally on your machine. Webpack has a PWA template and we will work with this in building our app. Create a Vue project with the pwa template. This is the syntax for creating a project with a template in Vue:

https://gist.github.com/88878279f9703a909589941a879a9b07

For our app, in the command prompt we run:

https://gist.github.com/213c30a4bd173fc88179b79f7e16e61e

Several popup commands requesting some project details will be displayed on our console. For the purpose of this project, skip all of them and use all default values provided. You can customize these if you choose though.

Now let’s test our app, run:

https://gist.github.com/920cb843fba7ef8a592f29e1b81b4c4f

You should get a welcome screen:

pwa-vuejs-setup

Install Dependencies

Next, let’s install our dependencies with:

https://gist.github.com/be875d378dfc8a4e9aabf7d742356cce

  • Bulma: CSS framework based on flexbox
  • Progressbar.js: Used to animate SVG paths
  • Pusher: Provides realtime service (server-side)
  • Pusher-js: Provides realtime service (client-side)
  • Dotenv: Used to load environment variables

dotenv is used to load environmental variables from a .env file in our root folder. Our pusher keys will be saved in this file. No pusher keys yet? You’ll get them next.

Create a Pusher Account

Go on to Pusher and create an account. On the left nav bar, create a new app with any name of your choice. You will be issued an app-id, a pusher key, and a pusher-secret. Select a cluster near you and keep these credentials safe; we will require them later in our app.

Configure a Server

For our delivery app, we will configure our pusher server with the pusher credentials obtained on Pusher account creation. In our root folder, we create a file .env and pass in our credentials:

https://gist.github.com/d6c31d985fca9d47e0d4228bf36b8b49

This file will be utilized by dotenv, which we installed earlier.

Create a file in the root directory named server.js, here we shall configure our server. First, we require dotenv and apply the .config() method to it. This allows us to use the process.env object.

https://gist.github.com/143327ad20e99a4dd2ff26252eef0866

As seen above, the ES6 destructuring technique is used to assign the data in our .env object to the listed constants. Next, we create a new pusher instance and assign our pusher credentials to their respective object properties as seen above.

For our demo we shall use a setInterval() function to simulate the timing of the pizza delivery process. Let’s configure this timing function:

https://gist.github.com/1b8e89cf1b174568bac41cfa051b7948

We created a stage variable to keep track of the iteration. In our setInterval() function, we pass it the usual callback using the ES6 arrow function. In this function, we create an object containing the individual processes as values with their properties being numbered keys. The stage value is incremented by one for every iteration. This ensures that at the last stage, the interval is cleared out and exited.

We created a Message ID to refer to the message used from the array, and then converted that to a fraction to serve as the progress bar as well. This serves as our progress. The if block creates a termination case for our function based on the value of messageId. As long as the function is not terminated, we keep calling the trigger method on the pusher instance, passing it arguments of channel, event, and a payload. This payload is an object containing data we would like to send over our pusher channel.

Create a Client with Vue

Vue is used to create the client interface as stated earlier. In our src folder, there is an already configured Vue component — App.vue. Delete all the content in it and let’s get to creating our own content. Add the following template to represent the UI:

https://gist.github.com/c279be40829032faa074b1f428bbd4dd

Notice the <status> component and the :progress binding? Their values are received from the component’s object as shown below:

https://gist.github.com/a5d95244072481e77d1fa06bfb589e3d

First, we require bulma, this provides all the bulma classes used to style our app in the template.

Next, we import pusher-js which we installed at the beginning. A Status custom component is also imported which we will create soon. In our Vue data method, we create and return an object whose properties values will be used in the template. The statusText property displays the status of our order.

The value of connectivityText is displayed whenever our device is offline and this depends on the value of the connectivityStatus property to be visible or not. We will set these values soon.

In the created() lifecycle method, we create a new pusher instance on the client side. This instance receives the payload from the server once we subscribe to our channel created on the server.

Note: Multiple events can be created per channel.

After a new pusher instance is created with our pusher-key (obtained on registration) as seen above. With this instance, we can subscribe to the channel we used on the server. Next, bind the status event to channel, passing it a callback which fetches the payload from the channel. The data fetched is passed as values to the properties we created in our data() method.

Progress Status Component

In the components folder, delete the Hello.vue component and create a Status.vue file. You also need to replace Hello.vue with Status.vue in the routes list if you opted for routing when creating with Vue CLI.

Add a div with a status class to the template as shown below:

https://gist.github.com/7e4d4ccc2d7f521df9f5e80b830cf2db

The div tag serves as a mount point for the progress status widget. Next, create the component’s logic that imports the progressbar plugin:

https://gist.github.com/92e0cffb7b28c7b2aec30238824d37a2

The component has a bar property which is used to keep track of the widget configuration instance. It is used to configure the awesome circular status bar.

We configure the Progress Bar in our mounted lifecycle method to ensure that the DOM is ready before manipulating it. This is done by creating an instance of the Circle constructor on the ProgressBar object. After configuring our circular bar, the .animate() object is called on bar and passed a parameter which is the value of progress property. The property is received from the parent component via props. This triggers an animation in the status bar but with a zero initial value so nothing is obvious.

Remember the :progress property we bound to the value of progress in App.vue? It would only be accessible to the Status child component if we specify it in the props array.

Now to a fun part; in the watch object, we state a progress function which listens for value changes on the progress property. Whenever the value changes, the new value is passed as a parameter to the animate method on this.bar. This means that whenever a new progress value is received from the server, it propagates through the App parent component, down to the child’s watch object and updates the status of our order. This update is observed from the progress circular bar.

If you don’t have the app running, run:

https://gist.github.com/1a103eb48c1d69847a6464b14e91c8e5

Then start the server:

https://gist.github.com/a71360ce19b055121b4d27ee02d5e351

Once the server starts running and is emitting events, you should see the client update as shown in the video below:

pwa-vuejs-demo

Handling Offline

How do we handle notifications when offline? We listen to the windows object for an offline event or an online event, in which case this.conectivityStatus is false or true respectively. When connectivityStatus is false, connectivityText resolves to:

https://gist.github.com/e9357a463c7b0f2dbe08532984a4d347

In the created() method of App.vue, let’s create an EventListener:

https://gist.github.com/35c362fa93ca73af02077410a4475fe4

While the app is running, turn off your network connectivity and you should see a warning message pop-up on the screen as shown in the image below:

pwa-vuejs-offline

Conclusion

In this article, we built a realtime delivery tracking PWA using Pusher and Vue while implementing status bar features with Progressbar.js and styling with Bulma. This app has minimal styling, feel free to add more styles and possibly more cool functionalities.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment