Node Webkit, Firebase and Ionic Framework – A one to one chat client15 min read

A few days ago Pooja Pasupu reached out to me after reading one of my articles on Node webkit named Building a Chat App with node-webkit, Firebase, and AngularJS. In the above article I have shown how to create a desktop based group chat client using Node webkit and Firebase. She wanted to know if we can build a desktop application for one to one chatting like Google talk or Yahoo messenger using node webkit. And this post is a response to that query.

In this post, we are going to build a desktop application that is like your typical one to one chat client. Where, a user logs in, sees a list of other users. And can chat with any of them. This app is called Chatter.

Below is a quick demo of the completed app.

PS : I have opened 2 clients on the same machine to demo the application.

As you can see from the above demo, we have used Firebase as our data store to manage the user’s presence and their chat messages. And also, if you did notice, when User B sent a new message to User A, a new chat window opened automatically, like a typical chat app.

We will take a look at how all the above can be achieved. So, let us get started on building Chatter.

You can find the completed code here.

Prerequisites

We are going to use Node webkit to build the client app, Ionic Framework/Angularjs as the UI framework and Firebase as our data store.

If you are new to Node webkit, check out

If you are new to Angularjs, checkout

If you are new to Firebase, checkout

To get an idea on Node Webkit Firebase Authentication, checkout

To get an Idea on Ionic Framework and Firebase, checkout

Architecture

Our chat client that we are going to build will use Node webkit to build the desktop application, Ionic Framework for the look & feel and Angularjs for the functionality. We will be using Firebase as our real time data store that manages users and their chats.

In a typical chat service, there are 2 major parts.

  • Part I is the user presence
  • Part II is the actual chats

So as soon as a user logs in, a presence server is updated about the User’s status i.e. Online. And the same server is responsible for maintaining the user’s status, say when he goes Away or Idle or Offline. This is the only job of the user presence server.

And when a user in your list of user is online, and you wish to chat with him/her, you will invoke a new chat with the help of the chat server. The chat server keeps a tab on all the users who are in the system and how to reach out to them. Kind of like an address for each client. And when a new chat message comes from user A to user B, it indicates User B to open a new chat window on User B and then start watching for messages on User A.

We are going to implement a similar approach, with one server or rather one data store. Firebase is a super awesome service when it comes to real time messaging. The events and hooks on the data store collections are very real time and we will be taking advantage of this feature to achieve our Chatter app.

To keep things simple, we will be having 2 collections in our data store

  1. Online Users – This will act like the presence server
  2. Chats – This will hold a collection of chats between users

chatterArch

As you can see from the above diagram, as soon as the user logs in, we update the Online user’s list and log him in. And when a new chat is triggered, we will update the chat collection and this trigger the chat on the other client.

This is where it gets tricky. As a desktop client, how do I know when to spawn a new chat window and how do I know with which of the online users I need to establish the connection with.

To solve the above issue, we are using a single collection named chats. And on the desktop client, we will be listening for changes on this collection and whenever there is a sub collection/new chat gets added, we get the name of the sub collection/chat and check if the current desktop client is the target audience and we spawn a new chat window.

This may sound complicated, but when you see the actual implementation it will look a lot easier.

Setup the project

Now that we have an understanding on how the app is going to work, let us get started on implementing it. To scaffold the base for the node webkit app, we will be using a slush generator named slush-wean.

Create a new folder named chatter and open a new terminal/prompt here. First, we will install wean generator globally. Run

npm install --save gulp slush slush-wean

Next, we will scaffold a new node webkit app here. Run

slush wean

Name the project as ChatterThis will take a couple of minutes to setup the app and download its dependencies. Once the dependencies are downloaded, we will run the app.

For the first time when we run the app, it will download the OS specific libs. Execute

gulp run

This process may take up to 5mins depending on your internet connection. Once the download is completed, the app will automatically launch and you should see the below screen.

Screen Shot 2014-11-15 at 4.39.04 pm

Click on the X button on the top right hand corner to close the app.

Below is a quick look at the project structure.

  • app.js : Express server configuration
  • gulpFile.js : Task runner
  • index.html : Application main page/splash screen
  • public folder : static resources (client side – our app will be developed here)
  • routes : Express routes
  • view : Initial view for our Angularjs application, served by Express

We will modify this project to suit our needs. First, we will update the package.json with the dimensions of the App window. The updated packaged.json would look like

The scaffolded project does not have a bower dependency management integrated. We will add that now. Before going further make sure that bower is installed globally ( npm install bower -g ).

From the root of the project run

bower init

Fill in with the default options. Next, create a new file named .bowerrc at the root of the project and update it as below

The above file tells bower where to dump the dependencies. Finally, delete public/lib folder.

To install dependencies, run

bower install --save firebase angularfire ionic firebase-simple-login

When prompted

Select firebase#~2.0.3.

The above step will download a bunch of dependencies and dump it inside the public/libs folder.

Setup Firebase

Next, we need to setup a new Firebase App. Follow the Firebase post mentioned in the prerequisites. Once you have created your Firebase App, we need to enable the Email & Password Authentication so that our users can login via Firebase.

Once you are inside the app’s Forge, click on the Login & Auth tab and Enable the Email & Password Authentication checkbox.

Screen Shot 2014-11-15 at 5.33.44 pm

Continue Development

Now that the base project is setup, we will work on the actual app development.

First, we will update the splash screen. Open index.html present in the root folder and update it as below

Next, we will update the app’s base page. Open views/index.ejs and update it as below

The above file consists of the updated dependencies we have downloaded earlier.

Now, we will update the app.js. The public/js/app.js will consist of the Angular/Ionic init code. Here we will configure a few helper functions and define a few routes. Update app.js as below

Things to notice

Line 1 : We Init a new Angular module, passing in Ionic and Firebase as dependencies.

Line 5 : This is the base Firebase URL for your app. Update it as per your account.

Line 6,7 : Init Firebase simple login

Line 9 :  openchats will consist of the all the chat windows that are open. This will help us when we receive a new incoming message, to check if there is already a conversation window between the incoming user and the current user.

Line 11 : Shows a Modal Dialog

Line 21 : Hides Modal Dialog

Line 25 : Shows a Modal Dialog for 2 seconds and hides it

Line 32 : The logout function. Here we log the user out from the application. And then invoke  offlineUser(). Finally we call the  checkSession() that will take care of redirecting the user.

Line 38 : We Check the state of the User’s auth status and redirect the user appropriately.

Line 62 :  escapeEmailAddress() will be used, when we want to make an Email Address Firebase safe, while naming a sub-collection for our chats.

Line 70 :  getHash() is one of the key functions in our application logic while communicating between users. This function takes in 2 email address, i.e. current logged in user and the user you would like to chat with and return a string that is a constant whenever the above to users are involved. This is the common end point in Firebase chats collection, whenever the above 2 users are involved.

Line 83 : The app routes

Now that the basic app is setup, we will work on our factory. Create a new file named factory.js inside public/js folder. This file will hold 2 factories. Update it as

The GUI service and the Window Service will be used to spawn child windows.

Finally the controllers.js. Create a new file named controllers.js inside public/js folder. This file will consist of the application logic.

Quite a lot of things are happening here.  We have 4 controllers in total. Signup, Login, Home and Chat. As the name suggest, each controller performs one specific task.

Line 1 : Login controller. Responsible for registered user login

Line 36 : Signup controller. Responsible for new user signup

Line 67 : Home controller. Once the user is logged in, s/he will be redirected to the home page where he will see a set of users who are online.

Line 70 : We create a new Firebase instance to the onlineUsers collection.

Line 72  : We sync the data in onlineUsers collection to a local scope variable. This variable will be used to iterate and show a list of users who are online.

Line 75 :  Now, that we have an instance of onlineUsers, we will broadcast our presence so that other users can see us online.

Line 82 :  triggerChat() gets invoked when the current logged in user wants to chat with another user who is online. Here we set the  chatToUser  to be the user, the current logged in user wants to chat to. And then we call the  spawnner()

Line 87 :  spawnner() is responsible for opening a new chat window between the current logged in user and the user you wish to chat with.

Line 88 : Spawn a new child window. The URL of the child window consists of the user you would like to chat with

Line 94 : Once the chat window is successfully loaded, we add the same to the  openchats .

Line 98 : Once the chat window is closed, we need to update   openchats so that if a new chat comes from the same user, we spawn a new chat window again.

Line 110 : When a user logs off, we remove their presence

Line 114 : When a user closes the application window, we remove their presence and close the window. This app does not run in the background

Line 120 : So far we have seen the logic where the current logged in user spawns a new chat. But what if a remote user spawns a chat. How do we update the current user about the intentions of the remote user? – This is how we do it.

We listen to chats collection and watch it for changes.

Line 123 : We watch for 2 events child_added and child_changed. Once we get a notification, we will check if the key variable inside it consists of the logged in user.

Line 124 : If yes, we will get the other user and check if they are already in a conversation with the current user.

Line 128 : If no, we will spawn a new chat window and call the chat controller to take over things.

Line 134 : Chat controller. This controller works with 2 variables.  chatToUser and  loggedInUser.

Line 137 : We create a new Firebase reference to the chats sub collection between the 2 users

Line 138,139 : We sync the values from server and assign it to a scope variable

Line 141 : When a user sends a message, we push it to the same collection.

If needed, spend some time here to understand how the connection between the 2 users happen.

With this we complete the logic part. We will be creating the required partials now.

First delete the head.html from public/partials folder. Create a new file named login.html and inside public/partials folder and update it as below

Next comes the signup.html. Create and update as below

The main home page. home.html

Finally the chat page – chat.html

To clean up some styles, replace the contents of public/css/app.css with below

Save all the files and run the app by executing

gulp run

And you should see the app launch. If you want you can create 2 instance of the same code, run one on port 2000 and other on 3000 as I have done in the demo to test it out.

Hope this post gave you an understanding on how you can Integrate Node webkit with Ionic Framework as well as Firebase and build a private one to one chat application.


Thanks for reading! Do comment.
@arvindr21