Previously, I had an article showing how to make a chat app with Flutter at here.
Today, I’ll do the same things, but with ReactJS to build a web chat, then we can send messages between app and web to see interest things.
This web also has the following features:
- Sign-in by Google account.
- Chat one to one with other users (send text, image, and sticker).
- Update profile and avatar.
Let’s get started
1. Creating the project on Firebase
After creating the project, at Project Overview, click Add app and select web platform.
Start the Cloud Firestore and Storage
Then enable sign-in method with Google
Now you’re done this step, don’t need to add or init any data manually at Firebase.
Rules at Storage:
Rules at Database:
2. Installing plugins
Runnpm install --save firebase
to install firebase package, it helps us use login, database, and storage service.
Also install moment
, react-loading
, react-toastify
and react-router-dom
to handle date time, show loading, toast and navigate (check out my package.json
for more).
3. Configuring firebase and define web app flow
Create a file MyFirebase.js
to config and use some firebase services
The web app will have 3 screens, all of them are inside root.js
then we can use react-router-dom
to navigate between these screens.
- Login screen (auth by google).
- Main screen (includes 3 components, list user, welcome board and chat board).
- Profile screen.
4. Rendering root screen with navigation
We’ll use import { BrowserRouter, Switch, Route } from 'react-router-dom'
to navigate between screens.
Not like React Native (just call show() is enough), toast at ReactJS need a component (ToastContainer) inside render function, so we should render it at root, then all child screens can use it and catch the case show toast at this screen, but immediately navigate to next screen (eg: toast login success).
5. Rendering login screen
If authenticate success, the variable result giving some info like displayName, email, photoURL,… and after that, we should check if this user is new? if true, we write to the database. The last thing to do is save to localStorage (no matter new or not) for easier to use later.
Notice that we should always check login at this screen since the user re-access this page, auto navigate them to the main screen instead require login.
6. Rendering main screen
Notice in this screen, we have 3 components, list user at the left, and welcome board/chat board at the right. When a user navigates to this screen at first, since they’re not chatting with any user, we should show welcome board, and when they choose another user to start the conversation, we should render chat board with loading that conversation’s history.
This screen also needs to check login to prevent invalid navigate since the user can type exact path URL to browser’s address bar.
Rendering list user
If logged in, we load list user and render them. A thing interesting at here is ReactJS doesn’t have listview built-in or something similar (I worked with RN before and quite surprise with it). So we need to loop an array (list users) and render each item by ourselves, then add all item views to an array view (viewListUser) and return it, so we just call this function at render is fine.
Rendering history messages
The welcome board is just simple UI, so I’ll ignore it to keep a short article, check out my source code at WelcomeBoard.js
.
When the user chooses other people, let’s load the history and render them at initial. Notice that onSnapshot
at here will load all child node first like the docs said, so we don’t need to separate load history and listener into 2 parts.
Notice that when user A (current user) chat with user B (peer user), the problem is which groupChatId we’ll use to read and write data chat to the server since conversation of AB need save at the same node? So my idea is hashing the A’ uid and B’ uid, then creates a string like this.
So at user A or B, they both read and write data at the same Firebase node.
The variable this.removeListener
be called to remove listening new data added at the current node when the current user leave this screen or switching to chat with other users.
About rendering list messages, it’s just some check left and right message with CSS style, please refer Chatboard.js
for more details.
Handling input form
The object we can send is image, sticker, and text. Notice that we need a field like a type to detect it is the normal message, image or sticker to render appropriately.
With image, first we upload it (it has a callback with return URL after upload success), so the message content should be the URL. Then just using <img/>
to render it.
Filter file type to only accepting image, but we need to check the prefix file type too since accept="image/*"
only provides a hint to the browser as to what file-types to display to the user, but this can be easily circumvented.
With sticker, the message content should be the file name (we stored local these stickers) and when render it, check if file name match? If true, use <img/>
to show this sticker.
Sending messages
When the user clicks an icon or hit enter to send a message, image or sticker, we need to check if the content is not empty, if true, write new data to the node appropriately. When add data success, onSnapshot
will catch the event and render to the list.
Handling logout
Nothing special here except remember clear the localStorage to prevent user use exactly path URL to go main screen without logged in (I use local data to check user logged in or not).
7. Rendering profile screen
You can add more field if you would like, but in this demo scope, I just use name, description, and avatar for quickly.
First, we need to check login like the main screen to prevent the user goes to this screen directly by path without login.
When the user chooses a file from their computer, we assign this file to a variable name this.newAvatar
since we don’t know user update their info with or without a new avatar, if they update avatar too, first we need upload this file, then get URL and add to user node this info.
After updating info success, we should save it to local for easier to use later.
Cloud Firestore structure
I can’t public my firebase server, so just giving a little information to help you imagine the structure.
Congratulations
Now we are done 👏
If you guys have any questions. Please feedback so we can discuss and improve each other.
Source code:
Would like to pack this website to a desktop app? Please read: