Skip to content

LiveChat SDK Documentation

The LiveChat class is part of the @nice-devone/nice-cxone-chat-web-sdk package. This class provides functionalities for managing live chat sessions, sending and receiving messages, and handling agent interactions.

Installation

To use the LiveChat class, you need to install the @investec/plugins-liveChat package. You can install it using npm:

== npm

shell
npm install --save @investec/plugins-liveChat

== yarn

shell
yarn add @investec/plugins-liveChat

Usage

After installing the package, you can import the LiveChat class into your Angular component and create an instance of it. Below is an example of how to use the LiveChat class within a component:

typescript
import { LiveChat } from '@investec/plugins-liveChat';

@Component({
  selector: 'app-live-chat',
  templateUrl: './live-chat.component.html',
  styleUrls: ['./live-chat.component.scss']
})
export class LiveChatComponent {
  private _livechat:LiveChat = new LiveChat();
  // Additional component logic...
}

Methods

setUpChat(customerId: string, customerFirstName: string, customerSurname: string): Promise<string>

Sets up the chat session with the provided customer details.

  • This is how you would use it in your class:
typescript
const fullName = await this._livechat.setUpChat(customerId, customerFirstName, customerSurname);
  • Parameters:
    • customerId: The unique identifier for the customer - this would be the 'ZETA-ID'.
    • customerFirstName: The first name of the customer.
    • customerSurname: The surname of the customer.
  • Returns: A promise that resolves to the full name of the customer.
ts
public async setUpChat(customerId: string, customerFirstName: string, customerSurname: string): Promise<string> {
  this.zetaId = customerId;
  this.clientFirstName = customerFirstName;
  this.clientSurname = customerSurname;
  const clientFullName = `${this.clientFirstName} ${this.clientSurname}`;
  try {
    await this.initializeChatSdk(customerId, clientFullName);
    return clientFullName;
  } catch (e) {
    console.error(`Error initializing chat SDK: ${e}`);
    return '';
  }
}

it uses this method to initialise:

  • Parameters:
    • customerId: The unique identifier for the customer - this would be the 'ZETA-ID'.
    • customerName: The combined full name of the customer.
  • Returns: A promise that resolves when the chat has been initialised.
ts
private async initializeChatSdk(customerId: string, customerName: string): Promise<void> {
  const brandIDEnvironment = environment[livechatEnvironment.activeEnvironment as keyof typeof environment].brandId;
  const channelIDEnvironment = environment[livechatEnvironment.activeEnvironment as keyof typeof environment].channelId;

  this.sdk = new ChatSdk({
    brandId: brandIDEnvironment,
    channelId: channelIDEnvironment,
    customerId: customerId,
    customerName: customerName,
    isLivechat: true,
    isAuthorizationEnabled: false,
    cacheStorage: this.storage,
    environment: EnvironmentName.UK1
  });
}

async startChat(): Promise<void>

Starts the chat session. It checks for an existing chat thread and either creates a new one or recovers an existing one. This checks if your local storage has thread-id, and if not it starts a new chat.

  • Returns: A promise that resolves when the chat has started.
ts
public async startChat(): Promise<void> {
  if (this.sdk) {
    let threadId = this.storage.getItem<ThreadIdOnExternalPlatform>('LIVECHAT-THREAD-ID');

    if(threadId == null) {
      threadId = crypto.randomUUID();
      this.thread = await this.sdk.getThread(threadId);
      if (this.thread instanceof LivechatThread) {
        this.thread.setCustomField('zeta-id', this.zetaId);
        this.thread.startChat('User is connected');
      }
      this.storage.setItem('LIVECHAT-THREAD-ID', threadId,60*60*1000 );
    }
    else {
      this.thread = await this.sdk.getThread(threadId);
      let recovered = null;
      try {
        recovered = await this.thread.recover();
      }
      catch  {
        console.log('error recovering');
      }
      if (recovered != null) {
        this.recoveredMessages = recovered.messages;
        if (this.thread instanceof LivechatThread) {
          this.getRecoveredMessages();
          this.thread.sendTextMessage('User has continued the chat');
        }
      }
    }
  } else {
    throw new Error("SDK initialization failed");
  }
}
  • This is how you would use it in your class:
typescript
await this._livechat.startChat();

handleTyping(timeout: number): void

Indicates that the user is typing. It sends a typing event to the chat service.

  • Parameters:
    • timeout: The duration in milliseconds to consider the user as typing.
ts
handleTyping(timeout: number){
  this.thread?.keystroke(timeout);
  if (this.typingTimeout) {
    clearTimeout(this.typingTimeout);
  }

  this.typingTimeout = setTimeout(() => {
    this.onUserStoppedTyping();
  }, timeout);

  this.userTyping = true;
}

onUserStoppedTyping(): void {
  this.thread?.stopTyping();
  this.userTyping = false;
}
  • This is how you would use it in your class:
typescript
this._livechat.handleTyping(1000);

async sendMessage(message: string): Promise<void>

Sends a text message to the chat service.

  • Parameters:
    • message: The text message to send.
  • Returns: A promise that resolves when the message is sent.
ts
public async sendMessage(message:string){
  if(this.thread == null){
    throw new Error("Thread not initialized - please run setUpChat method to initialize the thread");
  }
  else{
  await this.thread.sendTextMessage(message);
  }
}
  • This is how you would use it in your class:
typescript
await this._livechat.sendMessage('Hello, please can you help me?');

async endChat(): Promise<void>

Ends the current chat session and cleans up resources. (being the thread-id aswell from local storage).

  • Returns: A promise that resolves when the chat session is ended.
ts
public async endChat(){
  this.thread.endChat();
  this.storage.removeItem("LIVECHAT-THREAD-ID");
  this.thread = null;
  this.sdk = null;
}
  • This is how you would use it in your class:
typescript
await this._livechat.endChat();

getRecoveredMessages(): any[]

Retrieves the messages that were recovered from the chat session. This works, as NICE CXONE work on threads, and if there is a valid thread-id in the browsers stoarge, it can just recover the thread and return messages.

  • Returns: An array of recovered messages.
ts
public getRecoveredMessages(): any[] {
  const list = [];

  if (!Array.isArray(this.recoveredMessages) || this.recoveredMessages.length < 1) {
    return list;
  }
  this.recoveredMessages.forEach((message) => {
      if (message.authorUser != null) {
          const messageObject = {
              author: `${message.authorUser.firstName} ${message.authorUser.surname}`,
              text: message.messageContent['text'],
              timestamp: new Date(message.createdAtWithMilliseconds).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
          };
          list.push(messageObject);
      } else if (message.direction === 'outbound') {
          const messageObject = {
              author: "Global Support Center",
              text: message.messageContent['text'],
              timestamp: new Date(message.createdAtWithMilliseconds).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
          };
          list.push(messageObject);
        } else {
          const messageObject = {
              author: `Client`,
              text: message.messageContent['text'],
              timestamp: new Date(message.createdAtWithMilliseconds).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
          };
          list.push(messageObject);
      }
  });
  return list;
}
  • This is how you would use it in your class:
typescript
const messages = this._livechat.getRecoveredMessages();

async loadMoreMessagesFromSDK(): Promise<any[]>

Loads more messages from the chat service, this happens as the recover method returns only a portion, and thus if teh user wants to see previous messages, they can.

  • Returns: A promise that resolves to an array of loaded messages.
ts
public async loadMoreMessagesFromSDK(): Promise<any[]> {
  if (!this.thread) {
    throw new Error("Thread not initialized - please run startChat method to initialize the thread");
  }

  if (this.thread instanceof LivechatThread) {
    const response = await this.thread.loadMoreMessages();
    this.loadMoreMessageResponse = response ? response.data.messages : [];
    if (this.loadMoreMessageResponse.length === 0) {
      return [];
    }
  }

  const loadedMessages = this.loadMoreMessageResponse.reverse() || [];
  const list: any[] = [];

  loadedMessages.forEach((message) => {
    const author = message.authorUser
        ? `${message.authorUser.firstName} ${message.authorUser.surname}`
        : 'Client';

    const messageObject = {
        author: author,
        text: message.messageContent['text'],
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
        timestamp: new Date(message.createdAtWithMilliseconds!) // Store the timestamp as a number
    };

    list.push(messageObject);
  });

  // Sort the messages by timestamp
  list.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());

  // Optionally, format the timestamp for display
  list.forEach(message => {
    message.timestamp = message.timestamp.toLocaleTimeString([], {
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
        hour12: true // Use 24-hour format
    });
  });

  return list;
}
  • This is how you would use it in your class:
typescript
const moreMessages = await this._livechat.loadMoreMessagesFromSDK();

checkForChannelAvailability(): Promise<boolean>

Checks if the chat channel is available for communication. This will return false if no agents are online, and true if one or more are online.

  • Returns: A promise that resolves to true if the channel is online, otherwise false.
ts
public async checkForChannelAvailability(){
  const channelInfo = await this.sdk?.getChannelInfo();
  const channelAvail = await this.sdk?.getChannelAvailability();
  return channelAvail?.status === 'online';
}
  • This is how you would use it in your class:
typescript
const isAvailable = await this._livechat.checkForChannelAvailability();

setUpEventListeners(): void

Sets up event listeners for various chat events, such as agent connectivity and typing status and the chat status.

ts
public setUpEventListeners() {
  this.setAgentConnectedStatus();
  this.setChatStatus();
  this.listenToAgentTyping();
  this.listenToNewMessageFromAgent();
}
  • This is how you would use it in your class:
typescript
this._livechat.setUpEventListeners();

async setChatStatus(): Promise<void>

Sets the chat status and dispatches an event when the chat status changes.

  • Returns: A promise that resolves when the chat status is set.
ts
async setChatStatus() {
  this.sdk?.onChatEvent(ChatEvent.CONTACT_STATUS_CHANGED, (event: CustomEvent<ChatEventData>) => {
    this.chatStatus = event.detail.data as ContactStatusChangedData;
    document.dispatchEvent(new CustomEvent('chat-status-changed', { detail: this.chatStatus.case.status }));
  });
}
  • This is how you would use it in your class:
typescript
document.addEventListener('chat-status-changed', (event: Event) => {
        const customEvent = event as CustomEvent;
        const status = customEvent.detail;
        if (status === 'closed') {
          this.connectionStatusClosed = true;
          this.showLoadingIcon = false;
          this.agentHasConnected = true;
          document.removeEventListener('agent-connected', () => {});
          document.removeEventListener('agent-typing', () => {});
          document.removeEventListener('chat-status-changed', () => {});
          }
        this.chatStatus.next(customEvent.detail);
      });

listenToNewMessageFromAgent(): void

Listens for new messages from the agent and dispatches events when a new message is received.

ts
listenToNewMessageFromAgent() {
  this.thread.onThreadEvent(
    ChatEvent.MESSAGE_CREATED,
    (event: CustomEvent<ChatEventData>) => {
      if (!isMessageCreatedEvent(event.detail)) {
        return;
      }
      const message = event.detail.data.message;
      const timestamp = new Date();

      if (message.authorUser != null) {
        this.messageObject = {
          author: `${message.authorUser.firstName} ${message.authorUser.surname}`,
          text: message.messageContent.text,
          timestamp: timestamp
        };
        document.dispatchEvent(new CustomEvent('new-message-from-agent', { detail: this.messageObject }));
      } else if (message.direction === 'outbound') {
        this.messageObject = {
            author: "Global Support Center",
            text: message.messageContent.text,
            timestamp: timestamp
        }
        document.dispatchEvent(new CustomEvent('new-message-from-agent', { detail: this.messageObject }));
      }

    }
  );
}
  • This is how you would use it in your class:
typescript
this._livechat.listenToNewMessageFromAgent();

listenToAgentTyping(): void

Listens for typing events from the agent and updates the typing status accordingly.

ts
listenToAgentTyping() {
  this.thread.onThreadEvent(ChatEvent.AGENT_TYPING_STARTED, (event: CustomEvent<ChatEventData>) => {
    this.agentTyping = true;
    document.dispatchEvent(new CustomEvent('agent-typing', { detail: this.agentTyping }));
  });

  this.thread.onThreadEvent(ChatEvent.AGENT_TYPING_ENDED, (event: CustomEvent<ChatEventData>) => {
    this.agentTyping = false;
    document.dispatchEvent(new CustomEvent('agent-typing', { detail: this.agentTyping }));
  });
}
  • This is how you would use it in your class:
typescript
document.addEventListener('agent-typing', (event: Event) => {
        const customEvent = event as CustomEvent;
        this.agentTyping = customEvent.detail;
      });

document.removeEventListener('agent-typing', () => {});

async setAgentConnectedStatus(): Promise<void>

Sets the agent connected status and dispatches an event when the agent is connected.

ts
async setAgentConnectedStatus() {
  this.sdk?.onChatEvent(ChatEvent.ASSIGNED_AGENT_CHANGED, (event: CustomEvent<ChatEventData>) => {
    this.agentName = this.parseAgentName((event.detail.data as AssignedAgentChangedData).inboxAssignee);
    document.dispatchEvent(new CustomEvent('agent-connected', { detail: this.agentName }));
  });
}

parseAgentName( inboxAssignee: { firstName: string; surname: string } | null ): string | null {
  if (inboxAssignee === null) {
    return null;
  }
  return `${inboxAssignee.firstName} ${inboxAssignee.surname}`;
}

it uses: parseAgentName(inboxAssignee: { firstName: string; surname: string } | null): string | null

Parses the agent's name from the inbox assignee object.

  • Parameters:

    • inboxAssignee: An object containing the agent's first name and surname.
  • Returns: The full name of the agent or null if not available (meaning no agent is connected to the current chat).

  • This is how you would use it in your class:

typescript
document.addEventListener('agent-connected', (event: Event) => {
        const customEvent = event as CustomEvent;
        if (customEvent.detail === null) {
          this.agentHasConnected = false;
          this.agentHasDisconnected = true;
          this.UserHasSeenConnectionState = true;
          this.chatStatus.next(true);
        }
        else
        {
          this.UserHasSeenConnectionState = false;
          this.agentName = customEvent.detail;
          this.agentHasConnected = true;
          this.showLoadingIcon = false;
          this.agentHasDisconnected = false;
          this.chatStatus.next(true);
        }
      });

Example of Using LiveChat in a Component

Here's how you might implement the LiveChat class in an Angular component:

typescript
import { Component, OnInit } from '@angular/core';
import { LiveChat } from '@nice-devone/nice-cxone-chat-web-sdk';

@Component({
  selector: 'app-chat',
  templateUrl: './chat.component.html',
  styleUrls: ['./chat.component.scss']
})
export class ChatComponent implements OnInit {
  private chatService: LiveChat;

  constructor() {
    this.chatService = new LiveChat();
  }

  async ngOnInit() {
    await this.chatService.setUpChat('customer-id', 'John', 'Doe');
    await this.chatService.startChat();
  }

  async sendMessage(message: string) {
    await this.chatService.sendMessage(message);
  }
}

Conclusion

The LiveChat class provides a comprehensive interface for managing live chat sessions, sending and receiving messages, and handling agent interactions. By following the examples provided, you can easily integrate live chat functionality into your Angular applications.

If you have any further questions or need additional information, feel free to ask!