# Task Channel Definitions

Flex is a multichannel contact center. We support a number of channels out-of-the-box, and are constantly adding more. As of version 1.0 we support the following native channels:

* Voice
* SMS
* WebChat with Media Attachments
* Facebook Messenger
* WhatsApp
* RCS

With the Task Channel Definition API you can also add custom channels and override the behavior of existing ones.

## Steps to add a custom channel

To add a custom channel, you need to make changes in the following places:

* Add a custom channel in [TaskRouter](/docs/flex/developer/routing)
* Trigger the custom channel to be routed to the agent
* Define how the custom channel is rendered in the UI with Task Channel Definition API

## Task Channel Definition API

All task channels that Flex UI handles are defined and registered by the Task Channels API. Flex registers its default Task Channel definitions (see below), but users and plugins can register their own. When task-based components are rendered, the first matching channel definition for a given task will be used. If there is more than one channel definition match for a task, then the most recently registered definition will be used. This allows you to register a more specific channel definition to override the behavior of a general one.

See below interface `TaskChannelDefinition` for what can be set up. All parameters are optional; the parameters from the `Default` task channel definition will be used if not specified. The most important property of a task channel definition is the `isApplicable` callback function. The callback receives a task as an argument and must return Boolean `true` if this definition can be used to handle the given task.

In a task channel definition you can specify:

* callback to determine which tasks are applicable
* strings (templates) to use in different Flex components based on task status
* colors to be used in task list based on task status
* icons to be shown in task list, tabs, and canvases based on task status
* custom components to be added to task based components if channel is applicable
* custom components to be replaced in task based components if channel is applicable

Predefined Task Channels definitions are available via `Twilio.Flex.DefaultTaskChannels` objects for reference. Channels that are defined by default:

* Call - `Twilio.Flex.DefaultTaskChannels.Call`
* Chat - `Twilio.Flex.DefaultTaskChannels.Chat`
* Chat SMS - `Twilio.Flex.DefaultTaskChannels.ChatSms`
* Chat Messenger - `Twilio.Flex.DefaultTaskChannels.ChatMessenger`
* Chat WhatsApp - `Twilio.Flex.DefaultTaskChannels.ChatWhatsApp`
* Default - `Twilio.Flex.DefaultTaskChannels.Default`

> \[!WARNING]
>
> It is not recommended to change `Twilio.Flex.DefaultTaskChannels` at runtime. You should create your own definition and register it instead.

## Override Predefined Task Channels definitions Notifications

Predefined Task Channels definitions are available via Twilio.Flex.DefaultTaskChannels and they can be altered with custom logic when required.

## Override Default Chat Channel notifications

The example below demonstrates how you can define your own custom icon for browser notification of an `IncomingTask` with channel type `Chat`

```javascript
const originalChatNotifications = Flex.DefaultTaskChannels.Chat.notifications.override.IncomingTask;
Flex.DefaultTaskChannels.Chat.notifications.override.IncomingTask = {
  ...originalChatNotifications,
  options: {
    ...originalChatNotifications.options,
    browser: {
       ...originalChatNotifications.options.browser,
      options: { 
        ...originalChatNotifications.options.browser.options,
        icon: "CUSTOM_ICON_URL" 
      }
    }
  }
}; 
```

## Override Default Voice Channel notifications

The example below demonstrates how you can define your own custom icon for browser notification of an `IncomingTask` with channel type `Voice`

```javascript
const overrides = Flex.DefaultTaskChannels.Call.notifications.override.IncomingTask;
Flex.DefaultTaskChannels.Call.notifications.override.IncomingTask = (notification, cancel) => {
  overrides(notification, cancel);
  notification.options.browser.options.icon = "CUSTOM_ICON_URL";
}; 
```

## Channel Media Type

Tasks can be rendered depending on which media type they support. We have helper functions to define custom channels that support the following media type:

* Call - voice call based
* Chat - messaging based like WebChat, SMS, Facebook Messenger, etc.
* Generic - no media

## Creating custom channel definitions with helper functions

Flex has the following helper functions to create channel definitions with default values for chat, call, and generic.

**Chat channel creation with default chat templates:**

```javascript
Flex.DefaultTaskChannels.createChatTaskChannel(name: string, isApplicable: TaskChannelApplicableCb,
    icon: string | React.ReactNode = "Message", iconActive: string | React.ReactNode = "MessageBold", color: string = defaultChannelColors.chat): TaskChannelDefinition
```

**Call channel creation with default templates:**

Call channel definition uses callbacks to determine the icon and colors (based on call state and destination to render)

```javascript
Flex.DefaultTaskChannels.createCallTaskChannel(name: string, isApplicable: TaskChannelApplicableCb): TaskChannelDefinition
```

**Generic channel creation with default templates:**

```javascript
Flex.DefaultTaskChannels.createDefaultTaskChannel(name: string, isApplicable: TaskChannelApplicableCb,
  icon: string | React.ReactNode = "GenericTask", iconActive: string | React.ReactNode = "GenericTaskBold", color: string = defaultChannelColors.custom): TaskChannelDefinition
```

## Registering custom channels

To register a task channel definition use:

```javascript
Flex.TaskChannels.register(definition: TaskChannelDefinition, mergeWithDefaultChannel = true);
```

You must register your channel definitions before Flex finishes initializing.

For example:

```javascript
const myOwnChatChannel = Flex.DefaultTaskChannels.createChatTaskChannel("myChat",
  (task) => task.taskChannelUniqueName === "chat" && task.attributes.somethingSpecial === "myCustom");
// can modify myOwnChatChannel here

Flex.TaskChannels.register(myOwnChatChannel);
```

## Other API capabilities

* `Flex.TaskChannels.unregister(myOwnChatChannel);` - to unregister previously registered channel
* `Flex.TaskChannels.getRegistered();` - to get all registered task channels
* `Flex.TaskChannels.getForTask(task: ITask);` - to get a matching task channel definition for a task

```js title="Complete TaskChannelDefinition Interface"
export enum TaskChannelCapability {
    Info = "Info", // whether channel has info panel
    Call = "Call", // whether channel has call canvas capabilities
    Chat = "Chat", // whether channel has chat canvas capabilities
    Video = "Video", // whether channel has video calling capabilities
    Wrapup = "Wrapup" // whether channel needs to go to Wrapup state before can be completed
}

export type TaskCallbackType<T> = (
    task: ITask,
    componentType: React.ComponentType,
    ...args: Array<any>
) => T;
export type TaskStatusBasedTypeBase<T> = {
    Reserved?: T;
    Assigned?: T;
    Wrapping?: T;
    Completed?: T;
    Canceled?: T;
    Pending?: T;
};
export type TaskStatusBasedType<T = string> =
    | T
    | TaskCallbackType<T>
    | TaskStatusBasedTypeBase<T>;
export type TaskChannelApplicableCb = (task: ITask) => boolean;

export type TaskChannelComponentRegistration = {
    target: keyof FlexComponents;
    component: React.ReactChild;
    options?: ContentFragmentProps;
};

export type TaskChannelComponentRemoveRequest = {
    target: keyof FlexComponents;
    key: React.Key;
    options?: RemoveComponentCallOptions;
};

/**
 * Defines a task channel
 *
 * @export
 * @interface TaskChannelDefinition
 */
export interface TaskChannelDefinition {
    // for internal usage, will be set to an array of callbacks to invoke to unregister custom components
    _registrationCallbacks?: Array<Function>;

    name: string;

  /**
   * Used in TaskList, TaskCard, Canvases
   */
    colors?: {
        main?: TaskStatusBasedType<string>;
    };

  /**
   * Returns whether this task channel is applicable for a given task.
   * @param task task instance to evaluate the channel for
   */
    isApplicable: TaskChannelApplicableCb;

  /**
   * Icons to render to the task channel
   */
    icons?: {
    /**
     * List icon to be used in TaskList and TaskCardList
     */
        list?: TaskStatusBasedType<string | React.ReactNode>;
    /**
     * Icon to be used in Tab headers if tab is not selected
     */
        main?: TaskStatusBasedType<string | React.ReactNode>;
    /**
     * Icon to be used in Tab headers if tab is selected and in Task Canvases as the main icon
     */
        active?: TaskStatusBasedType<string | React.ReactNode>;
    };

  /**
   * Templates for components
   */
    templates?: {
        IncomingTaskCanvas?: {
            firstLine?: TaskStatusBasedType<string>;
            secondLine?: TaskStatusBasedType<string>;
        };
        CallCanvas?: {
            firstLine?: TaskStatusBasedType<string>;
            secondLine?: TaskStatusBasedType<string>;
        };
        TaskListItem?: {
            firstLine?: TaskStatusBasedType<string>;
            secondLine?: TaskStatusBasedType<string>;
            extraInfo?: TaskStatusBasedType<string>;
        };
        TaskCanvasHeader?: {
            title?: TaskStatusBasedType<string>;
            endButton?: TaskStatusBasedType<string>;
        };
        TaskCard?: {
            firstLine?: TaskStatusBasedType<string>;
            secondLine?: TaskStatusBasedType<string>;
        };
        TaskInfoPanel?: {
            content: TaskStatusBasedType<string>;
        };
        Supervisor?: {
            TaskCanvasHeader?: {
                title?: TaskStatusBasedType<string>;
                endButton?: TaskStatusBasedType<string>;
            };
            TaskInfoPanel?: {
                content: TaskStatusBasedType<string>;
            };
            TaskOverviewCanvas: {
                firstLine?: TaskStatusBasedType<string>;
                secondLine?: TaskStatusBasedType<string>;
            };
        };
    };

  /**
   * Set of capabilities, used to render Tabs
   */
    capabilities: Set<TaskChannelCapability>;

  /**
   * Character limit of a message.
   */
    charLimit: number;

  /**
   * Custom components to be added for this task channel. E.g. custom Tabs.
   * Supports only components that have a static "Content" property
   */
    addedComponents?: Array<TaskChannelComponentRegistration>;

  /**
   * Custom components to be replaced for this task channel.
   * Supports only components that have a static "Content" property
   */
    replacedComponents?: Array<TaskChannelComponentRegistration>;

  /**
   * Custom components to be removed for this task channel
   */
    removedComponents?: Array<TaskChannelComponentRemoveRequest>;

  /**
   * Custom component props to be passed in if we have a matching task channel.
   * Works only for task based components.
   */
    // componentProps?: {
    //     [K in keyof FlexComponents]?: any;
    // };
}
```

## Example Use Cases

**Adding a Tab to custom channel**

```javascript
MyCallChannel.addedComponents = [
    {
        target: "TaskCanvasTabs",
        component: <MyTab
            key="myTab"
            icon={<img src="https://someimage.png" />}
            iconActive={<img src="someimage.png" />}
        />
    }
];
```

**Replacing a component (e.g. TaskInfoPanel or MessagingCanvas)**

```javascript
const MyComponent = <div key="X">My Call Task Info Panel</div>;
MyCallChannel.replacedComponents = [
    { component: MyComponent, target: "TaskInfoPanel" }
];
```

**Replacing a component conditionally**

```javascript
MyCallChannel.replacedComponents = [
    { component: MyComponent, target: "MessagingCanvas", options:
        { if: (props) => props.task.status === "wrapping" }
    }
];
```

**Change strings based on task types (e.g., for end task button in the header):**

```javascript
myOwnChatChannel.templates.TaskCanvasHeader.endButton = {
    Assigned: "End Task",
    Reserved: undefined,
    Wrapping: "Wrap up",
    Completed: "Template1",
    Canceled: "Template2",
    Pending: "Template3"
};
```

**Removing a component conditionally (e.g., remove action button from TaskListItem if task is in state "wrapping")**

```javascript
Flex.DefaultTaskChannels.Call.removedComponents = [{
    target: "TaskCanvasHeader",
    key: "actions",
    options: {
        if: (props) => props.task.status === "wrapping"
    }
}];
```
