# JavaScript

Source: https://docs.interop.io/desktop/capabilities/data-sharing/channels/javascript/index.html

## Overview

The Channels API is accessible via the [`io.channels`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md) object.

> ℹ️ *See the JavaScript [Channels example](https://github.com/InteropIO/js-examples/tree/master/channels) on GitHub.*

> ℹ️ *For details on how to use the Channels API in order to work with FDC3 contexts in non-FDC3 apps, see the [FDC3 Compliance > Channels](https://docs.interop.io/desktop/getting-started/fdc3-compliance/index.md#channels-using_fdc3_contexts_in_nonfdc3_apps) section.*

## Enabling Channels

The Channels API is disabled by default. To enable it, set the `channels` property of the configuration object to `true` when initializing the io.Connect library:

```javascript
import IODesktop from "@interopio/desktop";

const config = { channels: true };

const io = await IODesktop(config);
```

> ℹ️ *For details on how to enable the Channel Selector for your apps, see the [Channels > Overview > Channel Selector](https://docs.interop.io/desktop/capabilities/data-sharing/channels/overview/index.md#channel_selector) section.*

## Channel Mode

Available since io.Connect Desktop 9.7 & @interopio/desktop 6.10.0

The Channel mode is set by the io.Connect framework based on the [Channel Selector configuration](https://docs.interop.io/desktop/capabilities/data-sharing/channels/overview/index.md#channel_selector-configuration).

To retrieve the current Channel mode (single or multi), use the `mode` property:

```javascript
const channelMode = io.channels.mode;

if (channelMode === "single") {
    // Handle Channel operations in single Channel mode.
} else if (channelMode === "multi") {
    // Handle Channel operations in multi Channel mode.
}
```

## Multiple Channels

Available since io.Connect Desktop 9.7 & @interopio/desktop 6.10.0

> ⚠️ *Note that the multi Channel support is still an experimental feature of **io.Connect Desktop**. For details on how to enable using multiple Channels and the considerations you should take into account, see the [Channels > Overview](https://docs.interop.io/desktop/capabilities/data-sharing/channels/overview/index.md#channel_selector-types-multi) section.*

> ⚠️ *Note that the [`my()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-my), [`getMy()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-getMy), and [`onChanged()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-onChanged) methods for working with single Channels aren't deprecated, but it's highly recommended to use the [`myChannels()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-myChannels), [`getMyChannels()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-getMyChannels) and [`onChannelsChanged()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-onChannelsChanged) methods instead, which support working both with single and multiple Channels.*

### Current Channels

To retrieve the list of names of the Channels to which your window is currently joined, use the [`myChannels()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-myChannels) method:

```javascript
const channelNames = await io.channels.myChannels();
```

### Retrieving Channel Contexts

To retrieve a list of the contexts of all Channels to which the window is currently joined, use the [`getMyChannels()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-getMyChannels) method:

```javascript
const channelContexts = await io.channels.getMyChannels();
```

## Current Channel

> ⚠️ *Note that it's highly recommended to use the [`myChannels()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-myChannels) method instead, which supports working both with single and multiple Channels.*

To retrieve the name of the Channel to which your window is currently joined, use the [`my()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-my) method:

```javascript
const myChannel = io.channels.my();
```

## All Channels

To retrieve a list of all Channel names, use the [`all()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-all) method:

```javascript
const channelNames = await io.channels.all();
```

## Add & Remove Channels

You can add or remove Channels dynamically. Newly added Channels won't be persisted after restart of **io.Connect Desktop**, unless they are recreated. A Channel that has been defined via [configuration](https://docs.interop.io/desktop/developers/configuration/system/index.md#channels) and is dynamically removed will be restored on restart of **io.Connect Desktop**.

To add a Channel dynamically, use the [`add()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-add) method and pass a [`ChannelContext`](https://docs.interop.io/desktop/reference/javascript/channels/channelcontext/index.md) object as an argument:

```javascript
const channelContext = {
    name: "Black",
    meta: { color: "black" },
    data: { io: 42 }
};

// Adding a new Channel.
const newChannelContext = await io.channels.add(channelContext);
```

To remove a Channel dynamically, use the [`remove()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-remove) method and pass the name of the Channel to remove:

```javascript
// Removing a Channel.
await io.channels.remove("Black");
```

## Join & Leave Channels

To join a Channel, use the [`join()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-join) method and specify the name of the Channel to join:

```javascript
await io.channels.join("Red");
```

To leave the Channel to which your window is currently joined, use the [`leave()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-leave) method. If in [multi Channel mode](#multiple_channels), this will remove your window from all currently joined Channels:

```javascript
await io.channels.leave();
```

To specify a window which to remove from its current Channel, pass a window ID as an argument. If in [multi Channel mode](#multiple_channels), this will remove the specified window from all currently joined Channels:

```javascript
const windowID = win.id;

await io.channels.leave(windowID);
```

Available since @interopio/desktop 6.10.0

The `leave()` method also accepts as an argument an object with optional `windowId` and `channel` properties which you can use to specify which window instance from which Channel to remove. If you provide only the window ID, the specified window will be removed from the current Channel, or from all currently joined Channels if in [multi Channel mode](#multiple_channels). If you provide only the Channel name, the current window will be removed from the specified Channel:

```javascript
const options = {
    windowId: win.id,
    channel: "Red"
};

await io.channels.leave(options);
```

## Retrieving Channel Context

> ⚠️ *Note that it's highly recommended to use the [`getMyChannels()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-getMyChannels) method instead, which supports working both with single and multiple Channels.*

To retrieve the context of the current Channel, use the [`getMy()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-getMy) method:

```javascript
const channelContext = await io.channels.getMy();
```

To retrieve the context of a specific Channel, use the [`get()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-get) method which accepts a Channel name as a required argument:

```javascript
const channelContext = await io.channels.get("Green");
```

To retrieve a list of the contexts of all Channels, use the [`list()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-list) method:

```javascript
const channelContexts = await io.channels.list();
```

> ℹ️ *For details on how to retrieve FDC3 User Channels contexts in a non-FDC3 interop-enabled app, see the [Getting Started > FDC3 Compliance > Channels](https://docs.interop.io/desktop/getting-started/fdc3-compliance/index.md#channels-using_fdc3_contexts_in_nonfdc3_apps) section.*

## Subscribing for Channel Updates

To track the data in the current Channel, use the [`subscribe()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-subscribe) method:

```javascript
const handler = (data) => {
    // The callback will be invoked each time the data is updated.
    console.log(data);
};

// Subscribe for updates from the Channel to which your window is currently joined.
io.channels.subscribe(handler);
```

The callback receives the data from the Channel and information about the current Channel.

The callback will be invoked in three cases:
- the `data` property of the Channel you are currently on is updated;
- the user has switched the Channel and you are receiving a snapshot of the new Channel data;
- your window isn't joined to a Channel anymore (e.g., the user has deselected the current Channel);

To subscribe for updates from a specific Channel, use the [`subscribeFor()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-subscribeFor) method:

```javascript
const channelName = "Green";
const handler = (data) => {
    // The callback will be invoked each time the data is updated.
    console.log(data);
};

await io.channels.subscribeFor(channelName, handler);
```

The `subscribeFor()` method accepts a Channel name as a first argument and a callback to handle Channel data updates.

Use the unsubscribe function returned by `subscribe()` and `subscribeFor()` to stop tracking updates of the Channel data:

```javascript
const unsubscribe = await io.channels.subscribeFor(channelName, handler);

unsubscribe();
```

The handlers passed to the `subscribe()` and `subscribeFor()` methods also accept the Channel context and the updating Interop [`Instance`](https://docs.interop.io/desktop/reference/javascript/interop/instance/index.md) peer ID as second and third arguments. The [`ChannelContext`](https://docs.interop.io/desktop/reference/javascript/channels/channelcontext/index.md) object contains the name of the Channel, the Channel meta data, and the Channel data. The updating Interop instance peer ID can be used to identify the app instance updating the Channel:

```javascript
const handler = (data, channelContext, updaterID) => {
    // Check the current Interop instance peer ID against the updating instance ID.
    const isUpdatedByMe = io.interop.instance.peerId === updaterID;

    if(!isUpdatedByMe) {
        // Another app instance has published in the Channel.
        console.log(`App instance "${updaterID}" has published "${JSON.stringify(data)}" in Channel "${channelContext.name}".`);
    };
};

io.channels.subscribe(handler);
```

## Publishing Data

To update the context of the Channel, use the [`publish()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-publish) method. It accepts the data to publish as a first required argument, and an optional Channel name as a second argument specifying which Channel context to update. If you don't specify a Channel name, the current Channel will be updated.

Updating the current Channel:

```javascript
const data = { io: 42 };

await io.channels.publish(data);
```

Updating a specific Channel:

```javascript
const data = { io: 42 };
const channelName = "Green";

await io.channels.publish(data, channelName);
```

> ⚠️ *Note that similarly to updating [Shared Contexts](https://docs.interop.io/desktop/capabilities/data-sharing/shared-contexts/javascript/index.md#updating_contexts), the `publish()` method supports updating only the top-level properties of the `data` object in the Channel context, as demonstrated in the following example:*
>
> ```javascript
> const update = { text: { color: "grey" } };
>
> await io.channels.publish(update);
>
> // Assuming the Channel context data has this shape: { backgroundColor: "red" },
> // the top-level properties of the `data` object in the Channel context will be merged with the update
> // resulting in the following Channel context data shape:
> // { backgroundColor: "red", text: { color: "grey" } }
>
> const anotherUpdate = { text: { fontSize: 14 } };
>
> await io.channels.publish(anotherUpdate);
>
> // Assuming the Channel context data has this shape: { backgroundColor: "red", text: { color: "grey" } },
> // the top-level properties of the `data` object in the Channel context will be merged with the update,
> // but the existing value for the `text` property will be entirely replaced
> // as the `publish()` method doesn't support updating nested properties:
> // { backgroundColor: "red", text: { fontSize: 14 } }
> ```
>
> ℹ️ *For details on how to update nested properties within the `data` object, see the [Updating Specific Properties](#updating_specific_properties) section.*

The [`publish()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-publish) method will throw an exception if your window isn't joined to a Channel but tries to publish data.

> ℹ️ *For details on how to publish FDC3 User Channels contexts in a non-FDC3 interop-enabled app, see the [Getting Started > FDC3 Compliance > Channels](https://docs.interop.io/desktop/getting-started/fdc3-compliance/index.md#channels-using_fdc3_contexts_in_nonfdc3_apps) section.*

## Updating Specific Properties

Available since @interopio/desktop 6.5.0

You can use the [`setPath()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-setPath) and [`setPaths()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-setPaths) methods to update specific Channel context properties using a dot-separated string path (e.g., `"prop1.prop2"`) to point to the location of the property within the Channel context. If the property (or the path) doesn't exist, it will be created. These methods are useful for updating or creating one or more nested properties within the Channel context.

To update or create a single property, use the `setPath()` method. It accepts a [`PathValue`](https://docs.interop.io/desktop/reference/javascript/shared%20contexts/pathvalue/index.md) object as a first required argument, containing a path to the property to update and a value for it. As a second optional argument, you can pass the name of a Channel whose context to update. If a Channel name isn't specified, the context of the current Channel will be updated:

```javascript
const update = {
    path: "text.color",
    value: "grey"
};

// The current Channel will be updated.
await io.channels.setPath(update);

// Assuming the Channel context data has this shape:
// { backgroundColor: "red" }, it will be updated as follows:
// { backgroundColor: "red", text: { color: "grey" } }
```

To update or create multiple properties, use the `setPaths()` method. It accepts a list of `PathValue` objects as a first required argument, each object containing a path to the property to update and a value for it:

```javascript
const channelName = "Red";
const updates = [
    { path: "table.cells", value: { width: 50, height: 30 } },
    { path: "text.color", value: "white" }
];

await io.channels.setPaths(updates, channelName);

// Assuming the Channel context data has this shape:
// { backgroundColor: "red", text: { color: "grey" } }, it will be updated as follows:
//
// {
//     backgroundColor: "red",
//     text: {
//         color: "white"
//     },
//     table: {
//         cells: {
//             width: 50,
//             height: 30
//         }
//     }
// }
```

Use these methods instead of the `publish()` method when you want to update a nested property within the `data` object of the Channel context, as `publish()` supports updating only top-level properties:

```javascript
// Using the `publish()` method to update a Channel.
const update = { text: { fontSize: 14 } };

await io.channels.publish(update);

// Assuming the Channel context data has this shape: { backgroundColor: "red", text: { color: "grey" } },
// using `publish()` will result in replacing the existing value for the `text` property
// as the `publish()` method doesn't support updating nested properties:
// { backgroundColor: "red", text: { fontSize: 14 } }

// Using the `setPath()` method to update a Channel.
const anotherUpdate = {
    path: "text.color",
    value: "grey"
};

await io.channels.setPath(anotherUpdate);

// Assuming the Channel context data has this shape: { backgroundColor: "red", text: { fontSize: 14 } },
// using `setPath()` will result in updating the existing value for the `text` property:
// { backgroundColor: "red", text: { color: "grey", fontSize: 14 } }
```

## Clearing Chanel Context Data

Available since @interopio/desktop 6.12.0

To clear the context data of the current Channel, use the [`clearChannelData()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-clearChannelData) method:

```javascript
await io.channels.clearChannelData();
```

The `data` property of the [`ChannelContext`](https://docs.interop.io/desktop/reference/javascript/channels/channelcontext/index.md) object will be set to an empty object.

To clear the context data of a specific Channel, pass the Channel name as an argument:

```javascript
await io.channels.clearChannelData("Red");
```

## Channel Restrictions

Available since io.Connect Desktop 9.3 & @interopio/desktop 6.3.1

To apply restrictions to windows for publishing or subscribing to Channels, use the [`restrict()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-restrict) method and provide a [`ChannelRestrictions`](https://docs.interop.io/desktop/reference/javascript/channels/channelrestrictions/index.md) object as a required argument. The following example demonstrates how to prevent the current window from publishing to the "Red" Channel:

```javascript
// Restricting the current window from publishing to a Channel.
const restrictions = {
    name: "Red",
    read: true,
    write: false
};

await io.channels.restrict(restrictions);
```

To apply restrictions to another window for publishing or subscribing to a Channel, provide the window ID in the [`ChannelRestrictions`](https://docs.interop.io/desktop/reference/javascript/channels/channelrestrictions/index.md) object:

```javascript
// Restricting a specific window from publishing to a Channel.
const restrictions = {
    name: "Red",
    read: true,
    write: false,
    // ID of the window you want to restrict.
    windowId: win.id
};

await io.channels.restrict(restrictions);
```

To apply restrictions to windows for publishing or subscribing to all Channels, use the [`restrictAll()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-restrictAll) method and provide a [`RestrictionsConfig`](../../../../reference/javascript/channels/restrictionsconfig /index.html) object as a required argument. The following example demonstrates how to prevent the current window from publishing to all Channels:

```javascript
// Restricting the current window from publishing to all Channels.
const restrictions = {
    read: true,
    write: false
};

await io.channels.restrictAll(restrictions);
```

To apply restrictions to another window for publishing or subscribing to all Channels, provide the window ID in the [`RestrictionsConfig`](https://docs.interop.io/desktop/reference/javascript/channels/restrictionsconfig/index.md) object:

```javascript
// Restricting a specific window from publishing to all Channels.
const restrictions = {
    read: true,
    write: false,
    // ID of the window you want to restrict.
    windowId: win.id
};

await io.channels.restrictAll(restrictions);
```

To retrieve the applied Channel restrictions for the current window, use the [`getRestrictions()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-getRestrictions) method. If you want to get the applied Channel restrictions for a specific window, pass a window ID as an optional argument. The method resolves with a [`Restrictions`](https://docs.interop.io/desktop/reference/javascript/channels/restrictions/index.md) object with a `channels` property holding a list of [`ChannelRestrictions`](https://docs.interop.io/desktop/reference/javascript/channels/channelrestrictions/index.md) objects:

```javascript
// Retrieving the Channel restrictions for the current window.
const { channels } = await io.channels.getRestrictions();

channels.forEach(console.log);
```

## Events

The Channels API provides methods for reacting to various Channel events. Use the returned unsubscribe function to stop receiving notifications about the respective event.

### Channel Changed

> ⚠️ *Note that it's highly recommended to use the [`onChannelsChanged()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-onChannelsChanged) method instead, which supports working both with single and multiple Channels.*

To get notified when the current Channel of your window is changed, use the [`onChanged()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-onChanged) method:

```javascript
const handler = (newChannel) => {
    if (newChannel) {
        // Handle the case where you have switched to another Channel.
        console.log(newChannel);
    } else {
        // Handle the case where your window isn't joined to any Channel
        // (e.g., the user has deselected the current Channel).
        console.log("No Channel selected.")
    }
};

io.channels.onChanged(handler);
```

Available since io.Connect Desktop 9.7 & @interopio/desktop 6.10.0

To get notified when the current Channels of your windows are changed, use the [`onChannelsChanged()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-onChannelsChanged) method. This method can be used both in single and in multi Channel mode:

```javascript
// The handler receives as an argument a list of all currently joined Channels.
const handler = (channelNames) => {
    channelNames.forEach(console.log);
};

const unsubscribe = io.channels.onChannelsChanged(handler);
```

### Channel Restrictions Changed

Available since io.Connect Desktop 9.5 & @interopio/desktop 6.8.0

To get notified when the Channel restrictions for any window have changed, use the [`onChannelRestrictionsChanged()`](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md#API-onChannelRestrictionsChanged) method and provide a handler for the event. The handler will receive as arguments a [`Restrictions`](https://docs.interop.io/desktop/reference/javascript/channels/restrictions/index.md) object describing the current set of Channel restrictions and the ID of the window for which the Channel restrictions have changed:

```javascript
const handler = (restrictions, windowID) => {
    console.log(`Channel restrictions changed for window with ID: "${windowID}"`);
    console.log(restrictions.channels);
};

io.channels.onChannelRestrictionsChanged(handler);
```

## API Reference

For a complete list of the available Channels API methods and properties, see the [Channels API reference documentation](https://docs.interop.io/desktop/reference/javascript/channels/api/index.md).
