Build an Ionic chat app with TalkJS

TalkJS provides an API that you can use to add chat to your web and mobile apps. It includes features like a pre-built UI and out-of-the-box notifications. This tutorial will teach you how to create an Ionic chat app by integrating TalkJS.

We assume that you already have a basic understanding of Ionic. To follow this tutorial, you'll need an Ionic application to add TalkJS to – if you don't already have one, you can use Ionic's starter template.

In this tutorial we'll create an Ionic service that stores some hard-coded user data (in a real app you'll retrieve this from your database) and then set up conversations between these users with TalkJS. Let's get started.

Getting ready

First, you'll need to create a TalkJS account and get the App ID. You can find the App ID in the Settings tab of your TalkJS dashboard. We’ll start from a blank starter template and use Angular as our web framework on top of Ionic.

To install TalkJS with npm, run the following command in the root folder of your project:

npm install talkjs --save

Let's add our App ID to our environment variables inside environment.prod.ts and environment.ts:

TALKJS_APP_ID: 'YOUR_APP_ID'

Setting up the UI

Next we need to create an Ionic page for our chat. Run the following command:

ionic g page chat

The Ionic CLI will create a new folder and scaffold the skeleton code for our chat page. We will also generate a service for communicating with the TalkJS API:

ionic g service shared/services/chat

This command will scaffold a service inside the shared/services folder. Let's import the ChatService class and inject it into the constructor in chat.page.ts:

import { ChatService } from '../shared/services/chat.service';
 
constructor(private chatService: ChatService) { }

We will add some hardcoded data to the chat.page.ts to use it for this chat example. You will probably pull this data from the database in a real app.

  users = [
    {
      id: 0,
      name: 'Ken',
      email: 'ken@test-email.io',
      photoUrl: 'https://i.picsum.photos/id/818/200/200.jpg?hmac=gfhJZngz3JDsSmE1obNFY5OeAQBVsJLED2VkwuGsC-o',
      welcomeMessage: 'Welcome to the chat!',
      role: 'default'
    },
    {
      id: 1,
      name: 'Denis',
      email: 'denis@test-email.io',
      photoUrl: 'https://i.picsum.photos/id/730/200/200.jpg?hmac=wK_2ZX79XZRP1wVJ-dW_r-OkOjiz1kK8eHIyTw2Lr6s',
      welcomeMessage: 'Hi',
      role: 'default'
    },
    {
      id: 2,
      name: 'Tim',
      email: 'tim@test-email.io',
      photoUrl: 'https://i.picsum.photos/id/521/200/200.jpg?hmac=J25eIJlH4Vz83r581TpDZbrmu21tzbZMognm7gqkoWo',
      welcomeMessage: 'Hello friend',
      role: 'default'
    },
    {
      id: 3,
      name: 'Sarah',
      email: 'sarah@test-email.io',
      photoUrl: 'https://i.picsum.photos/id/194/200/200.jpg?hmac=f1VYjvgDG_6vPwJyTb-Xl1HpXKM23stmhFUnmPE_yL8',
      welcomeMessage: 'How can I help you?',
      role: 'default'
    },
    {
      id: 4,
      name: 'Adam',
      email: 'adam@test-email.io',
      photoUrl: 'https://i.picsum.photos/id/653/200/200.jpg?hmac=tZtho3csFdJ2rLHTTlT7WhXtDwbXgJNIIUvOQQb2dIo',
      welcomeMessage: 'What\'s up?',
      role: 'default'
    }
  ];

To display the users data on the frontend we will add the following code to our chat.page.html template file:

<ion-header>
  <ion-toolbar>
    <ion-title>Chat</ion-title>
  </ion-toolbar>
</ion-header>
 
<ion-content>
  <h3 class="page-title">Pick your chat partner</h3>
  <ion-list>
    <ion-item *ngFor="let user of users" type="button" (click)="chatInit(user)">
      <ion-avatar slot="start">
        <img [src]="user.photoUrl">
      </ion-avatar>
      <ion-label>
        <h3>{{ user.name }}</h3>
        <p>{{ user.email }}</p>
      </ion-label>
      <ion-icon name="chevron-forward"></ion-icon>
    </ion-item>
  </ion-list>
 
  <div #inboxContainer id="inboxContainer">
  </div>
  <button ion-button #closeInboxBtn color="light" id="closeInboxBtn" (click)="closeInbox()">
    <ion-icon name="chevron-back"></ion-icon> Close this chat
  </button>
</ion-content>

This will create a list of users we can chat with. By clicking on a user in the list we are passing the information of that user to chatInit function in order to initiate a chat session. Once the chat partner is selected, we'll display the inboxContainer and closeInboxBtn elements and mount the TalkJS chat UI to the inboxContainer.

Let's add a little bit of styling to it by adding the following code to our chat.page.scss:

.page-title {
  margin: 20px 0;
  text-align: center;
}
 
#inboxContainer {
  height: 90vh;
  position: fixed;
  top: 0;
  width: 100%;
  z-index: 999;
  display: none;
}
 
#closeInboxBtn {
  display: none;
  position: fixed;
  bottom: 0px;
  height: 10vh;
  width: 100%;
  font-size: 18px;
  line-height: 2;
  background-color: #ff0000;
  font-weight: 600;
 
  ion-icon {
    position: relative;
    top: 6px;
    font-size: 25px;
  }
}

We need to define an element we'll use to display the inbox and a close button to hide the inbox. We will use the ViewChild decorator.

  @ViewChild('inboxContainer') inboxContainer!: ElementRef;
  @ViewChild('closeInboxBtn') closeInboxBtn!: ElementRef;

Now let's go to the chat.service.ts file to write the code that will interact with the TalkJS API. We need to import the app ID from the environment file and Talk from TalkJS.

import Talk from 'talkjs';
import { environment } from 'src/environments/environment';

We’ll use the currentUser object to store the data of the user that is currently logged in to this instance of TalkJS.

currentUser: Talk.User;

Now let's start the session by using Talk.Session. A session represents a connection between the mobile app and TalkJS. A session begins after authentication through the app ID and ends when the app is closed. Let's add a function to initiate a TalkJS session:

  async startSession() {
    return Talk.ready.then(() => {
      this.currentUser = new Talk.User({
        id: 99,
        name: 'Omar',
        email: 'omar@test-email.io',
        photoUrl: 'https://i.picsum.photos/id/681/200/200.jpg?hmac=GBX3TnpfRpzN-ZDEpA01hkf2-G7DkpMs53xqLpv8pa4',
        welcomeMessage: 'Hey there!',
        role: 'default'
      });
 
      const session = new Talk.Session({
        appId: environment.TALKJS_APP_ID,
        me: this.currentUser
      });
 
      return session;
    });
  }

First, we need to assign the value to the currentUser object and create the session object. As before we're using hard-coded user data here, which would normally be retrieved from a database.

Starting a conversation

Next, we'll start a conversation between our current user and the other user we select from the user list:

  async startConversation(session: Talk.Session, user: any) {
      const otherUser = new Talk.User({
        id: user.id,
        name: user.name,
        email: user.email,
        photoUrl: user.photoUrl,
        welcomeMessage: user.welcomeMessage,
        role: user.role
      });
 
      const conversation = session.getOrCreateConversation(Talk.oneOnOneId(this.currentUser, otherUser));
 
      conversation.setParticipant(this.currentUser);
      conversation.setParticipant(otherUser);
 
      return conversation;
  }

We use the getOrCreateConversation method can be used to begin a conversation and the setParticipant method to add our users to it. The oneOnOneId method uses the users IDs to create a unique ID for the conversation.

Creating the inbox element

We'll call the startConversation method and display TalkJS's Inbox UI when we click on a user in the list:

  async createInbox(session: Talk.Session, user: any) {
      const conversation = await this.startConversation(session, user);
      const inbox = session.createInbox();
      inbox.select(conversation);
      return inbox;
  }

We're passing a session object and a user object to this function so we can initiate a conversation. The conversation is created by assigning the return value of the startConversation function to the conversation constant. We are using the createInbox function to display the chat UI.

Removing the inbox element

We'll also want a removeInbox method to destroy the inbox when we click on the close button:

  removeInbox(inbox: Talk.Inbox) {
    inbox.destroy();
  }

We can pass the inbox instance to our removeInbox method and remove it by using the destroy function provided by TalkJS.

Wrapping it up

Now all we need to do is to import an instance of TalkJS into chat.page.ts and write the logic for invoking the functions that are defined in chat.service.ts.

import Talk from 'talkjs';

We need to define a variable that will contain the data that is returned by ChatServices createInbox function.

  inbox: Talk.Inbox;

When the user clicks on a list item the chatInit function is called.

  async chatInit(user: any) {
    const session = await this.chatService.startSession();
    this.inbox = await this.chatService.createInbox(session, user);
    this.inbox.mount(this.inboxContainer.nativeElement);
    this.showChat();
  }

After starting a session and creating an inbox we need to display the chat on the screen by using the showChat function:

  showChat() {
    this.inboxContainer.nativeElement.setAttribute('style', 'display: block');
    this.closeInboxBtn.nativeElement.setAttribute('style', 'display: block');
  }

The user can click on the close button to close the chat by calling the closeChat function:

  closeChat() {
    this.chatService.removeInbox(this.inbox);
    this.closeInboxBtn.nativeElement.setAttribute('style', 'display: none');
    this.inboxContainer.nativeElement.setAttribute('style', 'display: none');
  }

Conclusion

To recap, in this tutorial we have:

  • created an Ionic service that provides user data
  • created an Ionic template that displays a list of users
  • integrated TalkJS into the service so that you can start a conversation when you click on a user in the list

For more ideas for how to use TalkJS in your app, see our docs.