npm package: @getchat-dev/web-button
Description: GetChat is an out-of-the-box chat/messenger for your web services with closed source code. GetChat is available either as a cloud-based SaaS solution or as on-premises software for local installation.
Author: Evgeniy Tyurin
Latest version: 1.3.0

## Terms

GetChat is an out-of-the-box chat/messenger for your web services with closed source code. GetChat is available either as a cloud-based SaaS solution or as on-premises software for local installation.

## Overview

Web-button is a compact JS library that allows you to integrate GetChat as a single button on your website. When this button is clicked, a small window with GetChat opens. This library is designed exclusively for browser usage and does not support Node.js, as it uses primitives available only in the browser environment.

## Installation

```bash
npm install @getchat-dev/web-button
```

You can also use the legacy way and include the library on the page as follows:

```javascript
(function (d, s, id) {
    var js, fjs = d.getElementsByTagName(s)[0]
    if (d.getElementById(id)) return
    js = d.createElement(s)
    js.id = id
    js.src = 'https://getchat-dev.github.io/web-button/browser.js'
    fjs.parentNode.insertBefore(js, fjs)
}(document, 'script', 'getchat-jssdk'))
```

## Getting started

The main purpose of this library is to add a button to the page that, when clicked, opens or closes the GetChat window. The main parameter, uri, is a specially generated link to the GetChat service, which loads the messenger in an iframe. For security reasons, this link is generated exclusively on the backend. In the code example below, it is assumed that the link generated by the backend is somehow assigned to the variable GETCHAT_URL_GENERATED_ON_BACKEND during page generation.

### Adding the Button to the Page

#### Module way
```typescript
import { createButton } from '@getchat-dev/web-button'

const messenger: Chat = createButton({
    uri: GETCHAT_URL_GENRATED_ON_BACKEND,
    showUnread: 'chats',
    autoload: true,
    autoopen: false,
    autoopenDelay: false,
    buttonStyle: {
        position: 'fixed',
        bottom: '30px',
        right: '30px',
        display: 'inline-block',
        verticalAlign: 'middle',
        marginLeft: '22px'
    },
    chatStyle: {
        position: 'fixed',
        zIndex: '1000',
        bottom: '30px',
        right: '30px'
    }
}: CreateButtonOptions)
```

#### Script tag way

```typescript
window.getchatonloaded = async function (SDK) {

    const messenger: Chat = SDK.createButton({
        uri: GETCHAT_URL_GENRATED_ON_BACKEND,
        showUnread: 'chats',
        autoload: true,
        autoopen: false,
        autoopenDelay: false,
        buttonStyle: {
            position: 'fixed',
            bottom: '30px',
            right: '30px',
            display: 'inline-block',
            verticalAlign: 'middle',
            marginLeft: '22px'
        },
        chatStyle: {
            position: 'fixed',
            zIndex: '1000',
            bottom: '30px',
            right: '30px'
        }
    }: CreateButtonOptions)
```

If using this method, you can get an instance of the Chat class as follows:

```typescript
const messenger: Chat = document.querySelector('getchat-button').getChatInstance();
```


In the [Interfaces](#createbuttonoptions) section, you will find all the available parameters that can be passed to the `createButton` function.

### How to Know When the Chat is Loaded

> The Internet: An Unprecedented and Unparalleled Platform

Any loading process is asynchronous, and GetChat is no exception. The `createButton` method returns a web custom component instance `GetChatButton`.
To interact with the chat, use the `getChatInstance` method to obtain an instance of the `Chat` class, which provides the `whenReady` method. This method returns a promise that resolves when the chat is loaded and ready for use.

#### Important Details

- The `GetChatButton` instance will have a reference to the real chat `<iframe>` **only after loading has started**.
- You can start loading the chat in two ways:
  1. By setting the `autoload` property in the options (the chat will load immediately).
  2. By triggering loading manually—typically when the user clicks the button.

There is **no special method** to call that initializes the chat; either set `autoload` in the `createButton` options, or trigger loading by dispatching a click event on the button.

#### Manual Loading Example

```typescript
const button: GetchatButton = SDK.createButton({
    uri: GETCHAT_URL_GENRATED_ON_BACKEND,
});

// Manual: start loading the chat when the button is clicked
button.addEventListener('click', () => {
    const chat = button.getChatInstance();
    chat.whenReady().then(() => {
        console.log('Chat is ready');
    });
});
```

#### With Autoload

```typescript
const button: GetchatButton = SDK.createButton({
    uri: GETCHAT_URL_GENRATED_ON_BACKEND,
    autoload: true
});

const chat = button.getChatInstance();
chat.whenReady().then(() => {
    console.log('Chat is ready');
});
```

### How to Change the Button Style

Currently, the library supports changing the following seven parameters:

- `bgcolor` – background color of the button
- `bdradius` – border radius of the button
- `bdwidth` – border width of the button
- `bdcolor` – border color of the button
- `color` – text and icon color of the button
- `badgebg` – background color of the badge on the button
- `badgecolor` – text color of the badge on the button

#### Declarative Button Style Declaration

When creating the button, you can pass style parameters in the options object.

```typescript
const button: GetChatButton = SDK.createButton({
    uri: GETCHAT_URL_GENRATED_ON_BACKEND,
    bdcolor: 'white',
    color: 'black',
    bdradius: '8px',
    bdwidth: '1px',
}: CreateButtonOptions)
```

#### Imperative Button Style Change

Here's an example of how to change the button style using the standard browser API through `Element.setAttribute`:

```typescript
const button: GetChatButton = SDK.createButton({/* some params */});

button.setAttribute('data-bgcolor', 'white');
button.setAttribute('data-color', 'black');
button.setAttribute('data-bdradius', '8px');
button.setAttribute('data-bdwidth', '1px');
```

### How to Set a Custom Icon

Users can customize the button icon by passing either a **URL** or an **SVG string** to the `setCustomIcon` method.

#### Example Usage

**Using an Icon URL:**
```typescript
const button: GetChatButton = SDK.createButton({/* some params */});

button.setCustomIcon('https://example.com/my-custom-icon.png');
```

**Using an SVG String:**
```typescript
const button: GetChatButton = SDK.createButton({/* some params */});
button.setCustomIcon(`
    <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
        <circle cx="12" cy="12" r="10" stroke="black" stroke-width="2" fill="white"/>
        <path d="M8 12L12 16L16 12" stroke="black" stroke-width="2"/>
    </svg>
`);
```

#### Important Notes
- Set the icon as soon as possible: Avoid waiting for whenReady(), as this method waits for the chat iframe to fully load. However, setting the button icon does not depend on the iframe and should be done immediately after retrieving the button instance.
- Icon format flexibility: The method supports both image URLs and inline SVG, allowing for easy customization.

### How to Work with Push Notifications

If you want to enable push notifications, you need to request permission from the user. The library provides a method called initWebPushNotification that initializes the web push notification system and manages notification permissions.

#### Example: Calling the initWebPushNotification Method

You must call this method strictly after the chat is loaded and ready. The method returns an object with two properties: status and token.
- The status property can have one of the following values:
  - "granted": The user has allowed notifications.
  - "denied": The user has blocked notifications.
  - "default": The user has not yet made a decision.
  - "unsupported": The browser does not support web push notifications.
- The token property contains the FCM token if the status is "granted"; otherwise, it is null.

Using the status property, you can determine the current notification permission state to display an appropriate message to the user or update the notification toggler in your web application's settings.

```typescript
await chat.whenReady();
const { status, token } = await chat.initWebPushNotification();

if (status === "granted" && token) {
    console.log("You have granted permission to receive notifications. FCM Token:", token);
}
else if(status === "denied") {
    console.log("You have denied permission to receive notifications.");
}
else if(status === "default") {
    console.log("You have not yet made a decision on whether to receive notifications.");
}
else if(status === "unsupported") {
    console.log("Your browser does not support web push notifications.");
}
```

#### Example: Requesting Notification Permission

```typescript
await chat.whenReady();
// This must be called after the chat is fully initialized.
const { status, token } = await chat.initWebPushNotification();

// The browser allows requesting notification permission only if the status is "default".
// Otherwise, the user can change permissions only in the browser settings.
if (status === "default") {
    const activatePushButton = document.querySelector<HTMLButtonElement>('button[data-action="activate-push"]');
    if (activatePushButton) {
        activatePushButton.addEventListener("click", async (e: MouseEvent) => {
            e.preventDefault();

            try {
                const result: NotificationPermissionResult = await chat.requestNotificationPermission(e);
                console.log("Notification permission status:", result.status);
                if (result.token) {
                    console.log("FCM Token:", result.token);
                }
            }
            catch (error) {
                console.error("Failed to request notification permission:", error);
            }
        });
    }
}
```

#### Example: Disable Push Notifications

```typescript
await chat.whenReady();
// This must be called after the chat is fully initialized.
const { status, token } = await chat.initWebPushNotification();

if (status === "granted" && token) {
    const button = document.querySelector<HTMLButtonElement>('button[data-action="disable-webpush"]');
    if (button) {
        button.addEventListener("click", async (e: MouseEvent) => {
            e.preventDefault();

            try {
                const status: Boolean = await chat.disableNotifications();
                if (status) {
                    console.log("FCM Token deleted successfully");
                }
            }
            catch (error) {
                console.error("Failed to disable notifications:", error);
            }
        });
    }
}
```


### Handling Chat Events

To handle chat events, you can use the `addEventListener` method. For example, to handle the event of a new message, you can use the following code. It opens the chat upon receiving a new message and logs "New message received" to the console.

```typescript
const button: GetChatButton = SDK.createButton({
    uri: GETCHAT_URL_GENRATED_ON_BACKEND
});

button.

messenger.addEventListener('getchat.chat.message.new', function () {
    messenger.open();
    console.log('New message received');
});
```

### How to Know When the Chat Window is Opened or Closed

To determine whether the chat is currently open, you can use the `isOpened` method. This method returns `true` if the chat window is open and `false` otherwise.

#### Example Usage
```typescript
if (messenger.isOpened()) {
    console.log('Chat is open');
} else {
    console.log('Chat is closed');
}
```

### How to Check the Library Version at Runtime

To determine which version of the `GetChat Web Button` library is currently loaded, you can access the `version` property on the global `window.GetChat` object.

#### Example Usage:
```typescript
console.log('GetChat Web Button version:', window.GetChat?.version);
```

**Notes**:

This property is available only after the library is fully loaded.
If window.GetChat is undefined, it means the script has not yet been initialized.

## Interfaces

### CreateButtonOptions

> **Note:** Options marked with `*` are required.

| Option                   | Type                                             | Description                                                | Default      |
|--------------------------|--------------------------------------------------|------------------------------------------------------------|--------------|
| `uri`*                   | `string`                                         | The URI for the chat service.                              | `undefined`  |
| `insertButtonTo`*        | `HTMLElement \| (() => HTMLElement) \| null`     | The HTML element to insert the button into.                | `undefined`  |
| `className`              | `string`                                         | The class name to apply to the button.                     | `undefined`  |
| `showUnread`             | `boolean \| 'messages' \| 'chats'`               | Whether to show unread messages count.                     | `undefined`  |
| `autoload`               | `boolean`                                        | Whether to autoload the chat.                              | `false`      |
| `autoopen`               | `boolean`                                        | Whether to open the chat widget automatically.             | `false`      |
| `autoopenDelay`          | `number`                                         | Delay in milliseconds before auto open.                    | `5000`       |
| `closeOnEscape`          | `boolean`                                        | Whether to close the chat on escape press.                 | `true`       |
| `mobileModeMaxWidth`     | `number`                                         | The maximum width of the mobile mode.                      | `460`        |
| `bgcolor`                | `string`                                         | Background color of the button.                            | `undefined`  |
| `bdradius`               | `string`                                         | Border radius of the button.                               | `undefined`  |
| `bdwidth`                | `string`                                         | Border width of the button.                                | `undefined`  |
| `bdcolor`                | `string`                                         | Border color of the button.                                | `undefined`  |
| `badgebg`                | `string`                                         | Background color of badge on the button.                   | `undefined`  |
| `badgecolor`             | `string`                                         | Color of badge on the button.                              | `undefined`  |
| `color`                  | `string`                                         | Text color of the button.                                  | `undefined`  |
| `buttonStyle`            | `Partial<CSSStyleDeclaration>`                   | Custom styles to apply to the button.                      | `undefined`  |
| `chatClassName`          | `string`                                         | Class name to apply to the chat window.                    | `undefined`  |
| `chatParent`             | `HTMLElement \| null`                            | The parent HTML element of the chat window.                | `undefined`  |
| `chatStyle`              | `Partial<CSSStyleDeclaration>`                   | Custom styles to apply to the chat window.                 | `undefined`  |
| `chatNode`               | `HTMLElement \| null`                            | The HTML element representing the chat node.               | `undefined`  |
| `node`                   | `HTMLElement \| null`                            | The HTML element representing the node.                    | `undefined`  |
| `showUnreadInBrowserTab` | `boolean`                                        | Whether to show unread messages in the browser tab favicon.| `true`       |

### ChatOptions Interface

| Property                       | Type                            | Default | Description                                                       |
|--------------------------------|---------------------------------|---------|-------------------------------------------------------------------|
| `id`                           | `string`                       .      | N/A     | A unique identifier for the chat instance.                  |
| `url`                          | `string`                       .      | N/A     | The URL of the chat service.                                |
| `node`                         | `HTMLElement \| string \| null`.      | N/A     | The HTML element where the chat will be embedded.           |
| `nodeStyle`                    | `Partial<CSSStyleDeclaration>` .      | N/A     | Custom styles to apply to the chat node.                    |
| `handleKeyboardOnTouchDevices` | `boolean`                      .      | `true`  | Whether to handle keyboard events on touch devices.         |
| `showUnreadInBrowserTab`       | `boolean`                      .      | `true`  | Whether to show unread messages in the browser tab favicon. |
| `onBeforeLoad`                 | `(iframe: HTMLIFrameElement) => void` | N/A     | Callback function to call before the chat is loaded.        |
| `onLoaded`                     | `() => void`                   .      | N/A     | Callback when the chat is fully loaded.                     |

### Notification Interface
> **Note:** Options marked with `*` are required.

| Property                | Type                            | Default | Description                                                  |
|-------------------------|---------------------------------|---------|--------------------------------------------------------------|
| `title`*                | `string`                        | N/A     | The title of the notification. This is displayed in the notification popup. |
| `body`*                 | `string`                        | N/A     | The body text of the notification. This is displayed in the notification popup. |
| `icon`                  | `string`                        | `null`  | The icon URL for the notification. This is displayed in the notification popup. |
| `url`                   | `string`                        | `null`  | The URL to open when the notification is clicked. This can be used to direct the user to a specific page or resource. |

### InitWebPushNotificationsOptions Interface
| Property                | Type                            | Default | Description                                                  |
|-------------------------|---------------------------------|---------|--------------------------------------------------------------|
| `onNotificationClicked` | `(event: NotificationEvent) => void`| N/A     | Callback function to call when a user clicks on a notification and existing window is focused. |
| `iosStandalonePWALink`  | `string \| null`                    | `null`  | A link to the iOS standalone PWA, used for web push notifications. This is required for iOS devices to handle notifications correctly. |
| `welcomeMessage`        | `Notification`                      | N/A     | A welcome message to send to the user when they enable notifications. This is used to greet the user and inform about successful subscription. |

### Chat Class

#### Note:

Instantiating a Chat does not automatically create or load a chat `<iframe>`. This is by design: creating an object should never cause direct DOM side effects. Embedding and loading an iframe is a resource-intensive operation and should be performed explicitly (e.g., via the load or open method), allowing you to control when and if the chat interface appears in the DOM.

The `load` method returns the same promise as `whenReady` until this promise is settled (resolved or rejected). While loading is still in progress, repeated calls to both `load` and `whenReady` will return the same pending promise. Once the loading promise is settled, subsequent calls to either method will return new promises.

| Method              | Parameters                                         | Returns                     | Description                                                   |
|---------------------|----------------------------------------------------|-----------------------------|---------------------------------------------------------------|
| `constructor`       | `options: ChatOptions`                             | N/A                         | Initializes a new chat instance with the provided options.     |
| `whenReady`         | N/A                                                | `Promise<void>`             | Resolves to `true` if the messenger is loaded successfully, or `false` otherwise.              |
| `load`              | N/A                                                | `Promise<void>`             | Loads the chat interface, optionally showing a loader.        |
| `isLoaded`          | N/A                                                | `boolean`                   | Returns `true` if the chat iframe is fully loaded.            |
| `addEventListener`  | `event: string, listener: () => void`              | N/A                         | Registers an event listener for specified chat events.        |
| `getChatNode`       | N/A                                                | `HTMLElement \| null`       | Retrieves the chat container element.                         |
| `getChatIframeNode` | N/A                                                | `HTMLElement \| null`       | Retrieves the chat's iframe element, if using an iframe.      |
| `rpc`               | `method: string, params: any[], timeout?: number`  | `Promise<any>`              | Performs a remote procedure call to the chat service.         |
| `initWebPushNotifications` | `options: InitWebPushNotificationsOptions`                                        | `Promise<{ status: "granted" \| "denied" \| "default" \| "unsupported", token: string \| null }>` | Initializes web push notifications, retrieves FCM configuration, and manages notification permissions. |
| `requestNotificationPermission` | `event?: MouseEvent`                | `Promise<NotificationPermissionResult>` | Requests notification permission from the user.               |
| `disableNotifications` | N/A                                          | `Promise<boolean>`          | Disables web push notifications.                              |

### GetChatButton Class

The `GetChatButton` class represents the custom chat button element and provides various methods for managing its state, icon, styles, and chat instance.

| Method               | Parameters                                      | Returns          | Description                                                 |
|----------------------|-----------------------------------------------|------------------|---------------------------------------------------------------|
| `constructor`        | N/A                                           | N/A              | Initializes a new instance of `GetChatButton`.                |
| `setChatInstance`    | `chatInstance: Chat`                          | `void`           | Associates a `Chat` instance with this button.                |
| `getChatInstance`    | N/A                                           | `Chat`           | Retrieves the associated `Chat` instance.                     |
| `loadChat`           | `showLoader?: boolean`                        | `Promise<void>`  | Loads the chat interface, optionally showing a loader.        |
| `toggleChat`         | N/A                                           | `Promise<void>`  | Toggles the visibility of the chat interface.                 |
| `openChat`           | N/A                                           | `Promise<void>`  | Opens the chat interface.                                     |
| `closeChat`          | N/A                                           | `Promise<void>`  | Closes the chat interface.                                    |
| `isOpened`           | N/A                                           | `boolean`        | Returns `true` if the chat window is currently open.          |
| `setState`           | `state: 'loaded' | 'loading'`                 | `void`           | Sets the state of the button (e.g., `"loading"`, `"loaded"`). |
| `setBadge`           | `value: number`                               | `void`           | Sets the badge count on the button.                           |
| `setCustomIcon`      | `icon: string, catchError: boolean`           | `boolean`        | Sets a custom icon, either as an image URL or an SVG string.  |
| `setStyles`          | `styles: object`                              | `void`           | Applies custom styles to the button.                          |
| `render`             | N/A                                           | `void`           | Re-renders the button element.                                |

#### Custom Element Registration
The `GetChatButton` class is registered as a custom HTML element and can be used in the DOM as `<getchat-button>`. It is also available in the global `HTMLElementTagNameMap`:

```typescript
declare global {
    interface HTMLElementTagNameMap {
        'getchat-button': GetChatButton;
    }
}
```