Every day people use ride share sites such as Ola, Uber, Lyft, Grab, Gojek, and Be. One of the most widely used features of these sites is live chat. In this tutorial you will use CometChat communications SDK, Firebase, and Mapbox to build an Ola clone, allowing users to to book rides and chat with their drivers, either via text or voice calling.
Installing the App Dependencies
- Create a new project with named ola-clone using the command: npx create-react-app ola-clone
- Copy the dependencies from here into your package.json file, and run npm install
*Note: At this time of writing this tutorial, we are using the Firebase SDK with the version @8.9.1
Configuring the CometChat SDK
You will need to create an account with CometChat in order to use the SDK to take advantage of the realtime communication services our application will depend on.
You can create an account by following these steps:
- Head to CometChat Dashboard and create an account
- Log in to the CometChat dashboard
- Add a new app named ola-clone
- Select your app from the list
- Navigate to the Users tab, and delete all the default users
- Navigate to the Groups tab and delete all the default groups
- From the Quick Start copy the APP_ID, REGION, and AUTH_KEY
- Create a file called .env in the root folder of your project.
- Import and inject your secret keys in the .env file containing your CometChat and Firebase in this manner.
Make sure to include .env in your gitIgnore file to prevent it being included in your git repository
Setting Up a Firebase Project
Please follow the below steps to set up Firebase.
Creating a New Firebase Project
- Go to https://firebase.google.com and click on the Sign in
- Sign in to Firebase using a Gmail account
- Click on Go to console
- Click on Add project to create a new Firebase project
- Input the project name, tick the checkbox to accept the Firebase terms, and then click on Continue
6. Click on Continue
- Tick the two checkboxes shown below and click Create project
- You may not see the above image, You may see the below image. If you see the below image, please select your account and then click on the "Create project" button.
- Firebase will handle the remaining tasks for us and we wait until everything has been set up successfully
- Click on Continue
Setting Up Firebase Realtime Database
We will use Firebase’s Realtime Database service as the data store for our application. To set up a Firebase Realtime Database service, follow the steps below:
- Expand the Build menu, if it is not expanded, and click on Realtime Database
- Click on Create Database
- Choose the Realtime Database location and click on Next
- Select Start in test mode and click on Enable
- After finishing all of the above steps, you will see the below screen.
We need to use the users email address to select their information, therefore, we need to update the Firebase rules so that this field is indexed to make searching faster
Congratulation! We have set up the "Firebase Realtime Database" service successfully. In the following section, we will set up the "Firebase Authentication" service together.
Setting Up the Firebase Authentication Service
We will be using Firebase’s Authentication Service to manage users accounts. The service will be configured so that users need to provide a username (email address) and password in order to use the application.
- Expand the Build menu and click on Authentication
- Click on Get Started
- Select Email/Password
- Enable Password and Email authentication, then click on Save
Setting Up Firebase Storage
When creating new accounts, end-users can upload their avatars. We need to enable the "Firebase Storage" service to store these files.
- Expand the Build menu and click on Storage
- Click on Get started
- Select Start in test mode, then click on Next
- Click on Done
- For our purposes, we need to update the rules which control who has access to the data stored in the service. Update your storage rules to match the image below and click on the Publish to apply changes
Adding a New Web App in Firebase
In order to interact with the different Firebase services, we need to create a set of credentials for our application to use by creating a new Web Application in Firebase.
- Click on the Settings icon and choose the Project Settings
- Scroll down to the bottom of the page and click on the web icon
- Input your App’s name and click on Register app
- You will be presented with your applications Firebase credentials. These need to be added to the .env file. To finish creating the new web app, click on Continue to console
Setting Up Mapbox
For our ride sharing app, we need to use the leaflet-routing-machine library to draw a route between two locations. We will use Mapbox as an OSRM service.
Follow the steps below to setup Mapbox:
- Head to Mapbox and create an account. After signing in to Mapbox, you will be redirected to the page in which you will see the default public token.
- Add the Mapbox default public token to your .env file
Note: If the Mapbox platform requires a credit card/debit card to create an account and you do not have one, please feel free to use the below Mapbox API key for the demo and learning purposes.
Styling for the Application
In order to style your application, you can copy the style sheet here and add the rules to your index.css file.
React Context API
In order to pass data between components, we are going to used the React Context API. This API helps us pass data through the component tree without passing it down each level. Copy the context.js file here and add it to the src folder for the project.
The app.js file retrieves your CometChat API Credentials from the the environment in order to initialise the CometChat SDK, so that it can be used by the application. The full source code can be found here.
Configuring the Firebase File
You need to create a firebase.js file inside the src folder, this file is responsible for interfacing with the Firebase authentication and database services. In the snippet below we load the firebase credentials from the environment and initiate the different Firebase services. The full source code can be found here.
Services help us centralise our application logic and to avoid duplication in our code. Each service provides methods that will be used to handle an individual aspect of our application, for example, inserting the data to the Firebase Realtime Database service, interacting with the CometChat services, and working with the UI elements.
Create a services folder inside the src folder. Inside the services folder, create the below files. For the content of each file, you can find here.
- cometchat.js: this file contains functions that will be used to interact with the CometChat services such as creating a new account, login to the CometChat platform and so on. The full source code can be found here.
- firebase.js: this file contains some functions that will be used to interact with the Firebase services. The full source code can be found here.
- ui.js: this file contains functions that are related to the UI such as showing/hiding the loading indicator. The full source code can be found here.
The image below demonstrates the project structure. Make sure you see the folder arrangement before proceeding.
Now, let's make the rest of the project components as seen in the image above.
The App.js File
The App.js file is responsible for rendering different components for given routes. For example, it will render the login page if the user has not logged in, yet or it renders the home page if the user has signed in to the system. The full source code of the App.js file can be found here.
Create a components folder inside the src folder, and create a common folder inside it. The common folder is used to store the common components that will be used in different places in your application.
The Loading Component
The loading component will be shown when the system performs actions such as interacting with Firebase or calling CometChat APIs. You need to create the Loading.js file inside the common folder. The full source code of the loading component can be found here.
The Login Component
This component is responsible for authenticating our users using the Firebase authentication service. It accepts the user credentials and either signs them in or registers a new account. Create the Login.js file inside the login folder. The full source code can be found here.
The above code indicates that we are using withModal as a higher-order component. This higher-order component will be used to reuse the code for showing and hiding the custom modal. In this case, we want to show the sign-up modal to let end-users register new accounts.
We store the authenticated information in local storage for further use, so that we can access it in different places in the application, preventing the end-users come back to the login page after signing in to the application.
To login to the CometChat, we call the login function from the cometchat.js file, you can refer to the below code snippet for more information.
The withModal - Higher-Order Component
In our application, there are multiple places where we want to display components in a modal. In order to reuse the common logic to show and hide the modal we will create a higher-order component called withModal. That higher-order component helps us to avoid duplicating code each place we want to use a modal within the application. Create a Modal.js file inside the common folder. The full source code can be found here.
The Sign Up Component
The sign-up component will be used for end-users to register new accounts. This component will do two things. It will register new accounts on Firebase using the Firebase authentication service, and it also registers new accounts on CometChat using the CometChat SDK.
Create a register folder inside the components folder, and create the SignUp.js file inside it. The full source code can be found here.
To create a new CometChat account, the createAccount() function is called, which is loaded from the cometchat.js file. You can see the function below.
Add the CometChat UI to our Application
Before we can use the CometChat Pro React UI kit, we need to add it in our project so that we can reference it.
- Clone the CometChat React UI Kit Repository: git clone https://github.com/cometchat-pro/cometchat-pro-react-ui-kit.git
- Copy the folder of the CometChat Pro React UI Kit you just cloned into the src folder of your project:
- Copy all the dependencies from the package.json file in the CometChat Pro React UI Kit folder and paste them in the dependencies section of the package.json file of your project.
- Save the file and install the dependencies using npm install
Once the installation is completed, you will have access to all the React UI Components. The React UI kit contains different chat UI components for different purposes as you can see in the documentation here. It includes:
The Private Route Component
To ensure users can’t access features of the application without logging in, we need to add some private routes and check a users authentication before the application can load. This will be doe through a PrivateRoute component. The PrivateRoute component will check the authenticated information in the local storage, if the information exists the users can access the private route if not they will be redirected to login.
Create the PrivateRoute.js file inside the common folder. The full source code can be found here.
The Header Component
The Header component, will show the app’s name, user’s avatar, greeting, and the logout button.
When users click on the Logout button, a confirmation dialog will be shown. If the users click on the Ok button, the application will log out from the application by calling the cometChat.logout() function. After that, we will update the user state to null, and remove the authenticated information from localstorage.
Create the Header.js file inside the common folder. The full source code of the Header.js file can be found here.
The AddressPicker Component
The AddressPicker component allows users to input their pickup and destination locations. The application will suggest the locations based on the keywords they enter. If the users want to change their pickup or destination location, they just need to click on the locations that they want to change. After selecting the pickup and destination locations, a route will be drawn on the map and a confirmation dialog will be shown (that dialog will be a component which is called RequestRide component). We will develop these features in the next sections.
Create a folder named address-picker inside the components folder and add the AddressPicker.js inside it. The full source code of the AddressPicker.js file can be found here.
The RequestRide Component
As mentioned above, after the users select their pickup and destination locations, we will show the RequestRide component as a modal. The RequestRide component, will have a title, close icon, content and two buttons (Change and Requesting a ride now). If the users clicks on the Change button, the RequestRide component will be closed and they can change their pickup and destination locations. However, if the user clicks on the Requesting a ride now button, the application will insert the ride request in to the Firebase Realtime Database service.
Create a request-ride folder inside the components folder. The full source code of the RequestRide component can be found here.
The RideList Component
The RideList component component will be used to show a list of requesting rides, this component is only available for drivers and if the current user is a driver, the user will see this component instead of the address picker component.
When the users clicks on the Accept button, we will update the ride request on the Firebase Realtime Database. There are four states of a request (waiting, accepted, canceled and done). Following that, a component, which is called RideDetail component will be shown on both sides (the user’s side and the driver’s side).
Create a ride-list folder inside the components folder and add the RideList.js file inside it. The full source code can be found here.
The RideDetail Component
After a driver accepts a ride request on the RideList component, that ride request’s status will be changed to accepted and the RideDetail component will be shown on both sides (the user’s side and the driver’s side). On the user’s side, the RideDetail component will be used to show the driver’s information. And on the driver’s side, the RideDetail component will be used to show the user’s information.
On the driver’s side, there are three buttons - chat, cancel, and finish. If the driver clicks on the chat button, that driver will be redirected to the chat page where they can chat with the passenger. If the driver clicks on the cancel button, the current ride request’s status will be changed to canceled and the application will update that request in the Firebase Realtime Database service. If the driver clicks on the finish button, the current ride request’s status will be changed to done and the application will update that request in the Firebase Realtime Database service.
On the user’s side, there are two buttons - chat and cancel. If the user clicks on the chat button, that user will be redirected to the chat page where they can chat with the driver. If the user clicks on the cancel button, the current ride request’s status will be changed to canceled and the application will update that request on the Firebase Realtime Database service.
Create the ride-detail folder inside the components folder and add the RideDetail.js file inside it. The full source code of the RideDetail.js file can be found here.
The Home Component
In the previous sections, we have defined the AddressPicker, RideList, and RideDetail components. It is time to import and use those components in the Home component. The Home component is responsible for rendering the map, and displaying the corresponding components based on different scenarios. For example, if the current user is a passenger and that user has not requested any ride, yet, the Home component will show the AddressPicker component. If the current user is a driver, the RideList component will be used to show the list of available ride requests. If a request is accepted by the driver, the RideDetail component will be shown on both sides (the driver’s side and the passenger’s side).
Create the home folder inside the components folder and add the Home.js file. The full source code can be found here.
Note: To display the map correctly, we need to import leaflet CSS in the index.html file, the content of the index.html will be like this.
The Chat Component
From the RideDetail component, both the driver and the passenger can chat with each other by clicking on the chat icon. Once the driver and the passenger click on the chat icon, they will be redirected to the Chat page. The Chat page uses the CometChat UI Kit. The CometChat team has handled everything for us and we just need to import the components that we want to use. This means, we can add chat, voice, and video calling features with minimal effort.
In this case, we will import the CometChatMessages component from cometchat-pro-react-ui-kit, which we have integrated before. The CometChatMessages component accepts a prop (chatWithUser) and the prop’s value should be the CometChat id of the user that we want to chat with. For example, on the driver’s side, the value of the chatWithUser prop should be the passenger’s id. And on the passenger’s side, the value of the chatWithUser prop should be the driver’s id.
Create the chat folder inside the components folder and add the Chat.js file inside it. The full source code can be found here.
You can now run your app with the command: npm run start
In conclusion, we have done an amazing job in developing an Ola clone app by leveraging React.js, Firebase, CometChat SDK, and React UI Kit. You’ve been introduced to the chemistry behind the Ola clone app and how the CometChat services make the Ola clone app buildable.
You have seen how to build most of the chat functionalities such as real-time messaging using CometChat. I hope you enjoyed this tutorial.