# io.Connect Browser 3.4

Source: https://docs.interop.io/browser/getting-started/changelog/3-4/index.html

## io.Connect Browser 3.4

*Release date: 05.09.2024*

Versions of all main and additional libraries of **io.Connect Browser** for the current release:

| Library | Version |
|---------|---------|
| [`@interopio/browser`](https://www.npmjs.com/package/@interopio/browser) | 3.4 |
| [`@interopio/browser-platform`](https://www.npmjs.com/package/@interopio/browser-platform) | 3.4 |
| [`@interopio/browser-worker`](https://www.npmjs.com/package/@interopio/browser-worker) | 3.4 |
| [`@interopio/cli`](https://www.npmjs.com/package/@interopio/cli) | 1.4 |
| [`@interopio/fdc3`](https://www.npmjs.com/package/@interopio/fdc3) | 2.4 |
| [`@interopio/home-ui-react`](https://www.npmjs.com/package/@interopio/home-ui-react) | 1.4 |
| [`@interopio/intents-resolver-api`](https://www.npmjs.com/package/@interopio/intents-resolver-api) | 2.4 |
| [`@interopio/intents-resolver-ui-react`](https://www.npmjs.com/package/@interopio/intents-resolver-ui-react) | 1.0 |
| [`@interopio/ng`](https://www.npmjs.com/package/@interopio/ng) | 4.4 |
| [`@interopio/react-hooks`](https://www.npmjs.com/package/@interopio/react-hooks) | 3.4 |
| [`@interopio/search-api`](https://www.npmjs.com/package/@interopio/search-api) | 2.4 |
| [`@interopio/widget`](https://www.npmjs.com/package/@interopio/widget) | 1.1 |
| [`@interopio/workspaces-api`](https://www.npmjs.com/package/@interopio/workspaces-api) | 3.4 |
| [`@interopio/workspaces-ui-react`](https://www.npmjs.com/package/@interopio/workspaces-ui-react) | 3.4 |

## New Features

> ⚠️ *Note that each new feature is listed under all libraries it affects.*

> ### @interopio/browser
>
> - Added configuration for the `allow` attribute of the `<iframe>` element in which the app will be started when opened in a Workspace:
>
> ```javascript
> const definitions = [
>     // App definition.
>     {
>         name: "my-app",
>         type: "window",
>         details: {
>             url: "https://example.com",
>             // Permissions policy for the `<iframe>` element in which the app will be started when opened in a Workspace.
>             iframePermissionsPolicy: {
>                 // List of flags that will be applied to the `allow` attribute of the `<iframe>` element.
>                 flags: "geolocation 'self' https://a.example.com https://b.example.com; fullscreen 'none'"
>             }
>         }
>     }
> ];
>
> await io.appManager.inMemory.import(definitions);
> ```
>
> - Updated the methods of the io.Connect Channels API to be able to send and receive FDC3 context data in order to facilitate [using FDC3 User Channel contexts](https://docs.interop.io/browser/getting-started/fdc3-compliance/index.md#channels-using_fdc3_contexts_in_nonfdc3_apps) in non-FDC3 interop-enabled apps.
>
> The `data` object in a [`ChannelContext`](https://docs.interop.io/browser/reference/javascript/channels/channelcontext/index.md) object may now contain an `fdc3` property if FDC3 context data has been published to the Channel. This allows non-FDC3 interop-enabled apps to use FDC3 User Channel contexts more easily. The optional `fdc3` property is an object with a required `type` property holding the type of the FDC3 context. This is valid for all methods of the Channels API that utilize the `ChannelContext` object in any way:
>
> ```javascript
> // Example of accessing FDC3 context data by using the `get()` method.
> // The `ChannelContext` returned by `get()` will contain a `data.fdc3` property if FDC3 context data has been published to the Channel.
> const context = await io.channels.get("Red");
>
> if (context.data.fdc3) {
>     // The `type` property of the `fdc3` object is required.
>     const contextType = context.data.fdc3.type;
>
>     console.log(contextType);
> };
>
> // Example of subscribing for FDC3 context data by using the `subscribe()` method.
> // The `data` argument received by the callback will have an `fdc3` property if FDC3 context data has been published to the Channel.
> const handler = (data) => {
>     if (data.fdc3) {
>         // The `type` property of the `fdc3` object is required.
>         const contextType = data.fdc3.type;
>
>         console.log(contextType);
>     };
> };
>
> const unsubscribe = io.channels.subscribe(handler);
> ```
>
> The [`publish()`](https://docs.interop.io/browser/reference/javascript/channels/api/index.md#API-publish) method of the Channels API now accepts also a [`PublishOptions`](https://docs.interop.io/browser/reference/javascript/channels/publishoptions/index.md) object as a second optional argument. You can use it to specify the name of the Channel to update and whether the published data is FDC3 context data. This allows non-FDC3 interop-enabled apps to work with FDC3 contexts more easily:
>
> ```javascript
> // FDC3 context data to publish.
> const data = {
>     type: "fdc3.contact",
>     name: "John Doe",
>     id: {
>         email: "john.doe@example.com"
>     }
> };
>
> const options = {
>     name: "Red",
>     // Specify that the published data is FDC3 context data.
>     fdc3: true
> };
>
> await io.channels.publish(data, options);
> ```
>
> The [`get()`](https://docs.interop.io/browser/reference/javascript/channels/api/index.md#API-get), [`getMy()`](https://docs.interop.io/browser/reference/javascript/channels/api/index.md#API-getMy), [`subscribe()`](https://docs.interop.io/browser/reference/javascript/channels/api/index.md#API-subscribe), and [`subscribeFor()`](https://docs.interop.io/browser/reference/javascript/channels/api/index.md#API-subscribeFor) methods for retrieving and subscribing to Channel contexts now accept an [`FDC3Options`](https://docs.interop.io/browser/reference/javascript/channels/fdc3options/index.md) object as an optional argument. You can use this argument to specify the type of the FDC3 context in which you are interested. The `get()` and `getMy()` methods will resolve with an empty object if FDC3 context data of the specified type isn't available in the Channel. The callback passed to the `subscribe()` and `subscribeFor()` methods won't be invoked unless FDC3 context data of the specified type is published in the Channel:
>
> ```javascript
> // Specifying the type of an FDC3 context.
> const fdc3Options = { contextType: "fdc3.contact" };
>
> // Retrieving an FDC3 context of a specific type.
> const fdc3Context = await io.channels.getMy(fdc3Options);
>
> // Subscribing to an FDC3 context of a specific type.
> const handler = (data) => {
>     // The `type` property of the `fdc3` object is required.
>     const contextType = data.fdc3.type;
>
>     console.log(contextType);
> };
>
> const unsubscribe = io.channels.subscribe(handler, fdc3Options);
> ```
>
> - To [clear](https://docs.interop.io/browser/capabilities/data-sharing/intents/intents-api/index.md#clearing_saved_intent_handlers) all previously [saved Intent handlers](https://docs.interop.io/browser/capabilities/data-sharing/intents/overview/index.md#extending_the_intent_resolver-intent_resolver_api-saving_intent_handlers), use the [`clearSavedHandlers()`](https://docs.interop.io/browser/reference/javascript/intents/api/index.md#API-clearSavedHandlers) method of the Intents API. To remove a saved handler for a specific Intent, use the `clearSavedHandler` property of the [`IntentRequest`](https://docs.interop.io/browser/reference/javascript/intents/intentrequest/index.md) object when raising the Intent:
>
> ```javascript
> const intentRequest = {
>     name: "ViewChart",
>     // Clearing a previously saved handler for this Intent.
>     clearSavedHandler: true
> };
>
> await io.intents.raise(intentRequest);
>
> // Clearing all saved handlers for all previously raised Intents.
> await io.intents.clearSavedHandlers();
> ```

> ### @interopio/browser-platform
>
> - [**io.Insights**](https://docs.interop.io/insights/general-overview/index.md) now comes as a built-in feature of **io.Connect Browser**. **io.Insights** is disabled by default. To enable it and configure its features in **io.Connect Browser**, use the `otel` property of the configuration object when [initializing your Main app](https://docs.interop.io/browser/developers/browser-platform/setup/index.md):
>
> ```javascript
> import IOBrowserPlatform from "@interopio/browser-platform";
>
> const config = {
>     licenseKey: "my-license-key",
>     // Enabling io.Insights and providing settings for publishing data.
>     otel: {
>         metrics: {
>             url: "http://localhost:4242/my-metrics-collector",
>             metrics: [
>                 {
>                     type: "app_error",
>                     enabled: false
>                 }
>             ]
>         }
>     }
> };
>
> const { io } = await IOBrowserPlatform(config);
> ```
>
> - New [configuration settings](https://docs.interop.io/manager/configuration/platform/index.md#ioconnect_browser) are available for adding extra headers to the requests sent to **io.Manager**, for caching and persisting data from **io.Manager** locally, and for the timeouts of the requests sent to **io.Manager**.
>
> To add extra headers to the requests sent to **io.Manager**, use the `getHeaders` property of the `manager` object. Provide a callback that will receive as an argument all current headers and will be invoked on each request to **io.Manager**. The following example demonstrates how to provide a callback that will obtain and add to the headers a valid access token on each request sent to **io.Manager**:
>
> ```javascript
> import IOBrowserPlatform from "@interopio/browser-platform";
>
> // Callback for handling the request headers.
> const getHeaders = async () => {
>     // Your custom internal logic for refreshing and retrieving the access token.
>     const token = await getAccessToken();
>
>     const extraHeaders = {
>         Authorization: `Bearer ${token}`
>     };
>
>     // The returned headers will be merged with the existing ones.
>     return extraHeaders;
> };
>
> const config = {
>     licenseKey: "my-license-key",
>     manager: {
>         url: "https://my-io-manager.com:4242/api",
>         // Providing a callback that will be invoked on every request sent to io.Manager.
>         getHeaders
>     }
> };
>
> const { io } = await IOBrowserPlatform(config);
> ```
>
> To specify settings for caching data from **io.Manager**, use the `cache` property of the `manager` object. The `cache` object has the following properties:
>
> | Property | Type | Description |
> |----------|------|-------------|
> | `clearOld` | `boolean` | If `true`, on opening a new session to **io.Manager**, all cache databases from previous sessions will be deleted. Defaults to `false`. |
> | `enabled` | `boolean` | If `true` (default), will enable caching and persisting data from **io.Manager** locally (e.g., in case of connection interruptions). |
>
> To specify settings for the timeouts of the requests sent to **io.Manager**, use the `requests` property of the `manager` object. The `requests` object has the following properties:
>
> | Property | Type | Description |
> |----------|------|-------------|
> | `closeSessionTimeout` | `number` | Interval in milliseconds to wait for a response to the `closeSession` request to **io.Manager**. Defaults to `10000`. |
> | `openSessionTimeout` | `number` | Interval in milliseconds to wait for a response to the `openSession` request to **io.Manager** before proceeding from cache. Defaults to `10000`. |
> | `timeout` | `number` | Interval in milliseconds to wait for a response from **io.Manager**. Defaults to `10000`. |
>
> - Added global configuration for the `allow` attribute of the `<iframe>` elements in Workspaces:
>
> ```javascript
> import IOBrowserPlatform from "@interopio/browser-platform";
> import IOWorkspaces from "@interopio/workspaces-api";
>
> const config = {
>     licenseKey: "my-license-key",
>     workspaces: {
>         src: "https://my-workspaces-app.com",
>         // Permissions policy for the `<iframe>` elements in a Workspace.
>         iframePermissionsPolicy: {
>             // List of flags that will be applied to the `allow` attribute of each `<iframe>` element in a Workspace.
>             flags: "geolocation 'self' https://a.example.com https://b.example.com; fullscreen 'none'"
>         }
>     },
>     browser: {
>         libraries: [IOWorkspaces]
>     }
> };
>
> const { io } = await IOBrowserPlatform(config);
> ```
>
> - Added support for saving preferred Intent handlers.
> - Exposed details about the initial caller of the Intent Resolver UI.

> ### @interopio/fdc3
>
> - Added support for saving preferred Intent handlers.

> ### @interopio/home-ui-react
>
> - Redesigned and improved the [initial platform setup](https://docs.interop.io/browser/capabilities/home-app/overview/index.md#using_the_home_app). The platform will now initialize even if the user doesn't grant the necessary browser permissions. Added steps for pop-ups permission and for installing the Home App as a [Progressive Web Apps](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps) (PWA). If you have created your custom Home App by using the io.Connect [CLI](https://docs.interop.io/browser/developers/cli/index.md), it will be fully configured as a PWA. If you are using the [`@interopio/home-ui-react`](https://www.npmjs.com/package/@interopio/home-ui-react) library directly, you will have to configure your custom Home App as a PWA if you want to use it as such.
>
> ![Initial Setup](https://docs.interop.io/browser/images/home/initial-setup.mp4)
>
> - Added a `<SystemLoader />` component to the library that can be used to customize the [default loader](https://docs.interop.io/browser/capabilities/home-app/library-features/index.md#using_the_components-loader) of the Home App via the `components` property of the `config` object for the `<IOConnectHome />` component:
>
> ```javascript
> import { IOConnectHome, SystemLoader } from "@interopio/home-ui-react";
> import { getConfig } from "./helpers";
> import "@interopio/workspaces-ui-react/dist/styles/workspaces.css";
> import "@interopio/home-ui-react/src/index.css";
>
> const getIOConnectConfig = await getConfig();
>
> // Configuration for the `<IOConnectHome />` component.
> const config = {
>     getIOConnectConfig,
>     components: {
>         // Providing a custom icon for the default loader of the Home App.
>         SystemLoader: () => <SystemLoader Logo={() => <span>My Logo</span>} />
>     }
> };
>
> const App = () => {
>     return <IOConnectHome config={config} />
> };
>
> export default App;
> ```
>
> - Added the option for integrating [custom authentication](https://docs.interop.io/browser/capabilities/home-app/library-features/index.md#using_the_components-authentication-custom) when using the Home App. When using custom authentication, you have to display your own login page to the user, authenticate them, and then display the `<IOConnectHome />` component and pass the user credentials to it:
>
> ```javascript
> import { useState } from "react";
> import { IOConnectHome } from "@interopio/home-ui-react";
> import { getConfig, authenticateUser, logOutUser } from "./helpers";
> import MyLoginPage from "./MyLoginPage";
> import "@interopio/workspaces-ui-react/dist/styles/workspaces.css";
> import "@interopio/home-ui-react/src/index.css";
>
> const App = () => {
>     const [user, setUser] = useState(null);
>
>     const config = {
>         getIOConnectConfig: getConfig,
>         // Settings for custom authentication.
>         login: {
>             // Type must be set to `"custom"`.
>             type: "custom",
>             // Provide details about the authenticated user.
>             user: user: {
>                 id: user?.id,
>                 username: user?.username
>             },
>             // Callback that will be invoked on logout of the platform.
>             onLogout: () => {
>                 // Use your custom logic to log out the user.
>                 logOutUser();
>
>                 setUser(null);
>             }
>         }
>     };
>
>     const logInUser = async () => {
>         // Use your custom logic to authenticate the user.
>         const user = await authenticateUser();
>
>         if (user) {
>             // Set the user object to provide an ID and a username for the Home App.
>             setUser(user);
>         };
>     };
>
>     // Display the Home App only if the user is logged in.
>     return user ? <IOConnectHome config={config} /> : <MyLoginPage logInUser={logInUser} />
> };
>
> export default App;
> ```
>
> - The Launchpad now won't be displayed in the Home App if the Home App isn't a Browser Platform app.

> ### @interopio/intents-resolver-api
>
> - Exposed information about the initial calling app of the Intent Resolver UI via the `caller` property of the Intent Resolver API. The `caller` property returns a `ResolverCaller` object holding the ID, name and title of the calling app:
>
> ```javascript
> const caller = io.intents.resolver.caller;
> ```
>
> - The callbacks passed to the `onHandlerAdded()` and `onHandlerRemoved()` methods now receive an `IntentInfo` object as a second optional argument which you can use to obtain details about the Intent that the app can handle.
>
> - The `sendResponse()` method now accepts a `SendResolverResponseOptions` object as a second optional argument. You can use this object to [save the chosen Intent handler](https://docs.interop.io/browser/capabilities/data-sharing/intents/overview/index.md#extending_the_intent_resolver-intent_resolver_api-saving_intent_handlers) as a handler for the raised Intent. If the Intent Resolver UI was opened via the `filterHandlers()` method instead of the `raise()` method, you also have to specify the name of the Intent for which the handler will be saved:
>
> ```javascript
> const options = {
>     saveHandler: true,
>     // Providing the Intent name is necessary only when the Intent Resolver UI
>     // was opened via the `filterHandlers()` method instead of the `raise()` method.
>     name: "ViewChart"
> };
>
> await io.intents.resolver.sendResponse(selectedIntentHandler, options);
> ```

> ### @interopio/intents-resolver-ui-react
>
> - Added the [`@interopio/intents-resolver-ui-react`](https://www.npmjs.com/package/@interopio/intents-resolver-ui-react) library which exports the `<IOConnectIntentsResolverUI />` React component that can be used to [create an Intent Resolver App](https://docs.interop.io/browser/capabilities/data-sharing/intents/overview/index.md#extending_the_intent_resolver-intent_resolver_component).
>
> To use the library in your project, execute the following command:
>
> ```cmd
> npm install @interopio/intents-resolver-ui-react
> ```
>
> Using the `<IOConnectIntentsResolverUI />` component:
>
> ```javascript
> import { createRoot } from "react-dom/client";
> import IOBrowser from "@interopio/browser";
> import IOConnectIntentsResolver from "@interopio/intents-resolver-api";
> import IOConnectIntentsResolverUI from "@interopio/intents-resolver-ui-react";
> // The default styles for the Intent Resolver App must be imported.
> import "@interopio/intents-resolver-ui-react/styles";
>
> // Provide the factory function for the `@interopio/intents-resolver-api` library.
> const config = {
>     libraries: [IOConnectIntentsResolver]
> };
>
> // Initialize the io.Connect API.
> const io = await IOBrowser(config);
>
> const domElement = document.getElementById("root");
> const root = createRoot(domElement);
>
> root.render(
>     // Provide the initialized io.Connect API object to the component.
>     <IOConnectIntentsResolverUI config={{ io }} />
> );
> ```

> ### @interopio/widget
>
> - Added a `displayInWorkspace` property to the `widget` object for configuring the io.Connect [widget](https://docs.interop.io/browser/capabilities/widget/index.md). If set to `true`, the widget will be displayed in the app even if the app is currently inside a Workspace:
>
> ```javascript
> import IOBrowser from "@interopio/browser"
> import IOBrowserWidget from "@interopio/widget";
> import "@interopio/widget/styles";
>
> const config = {
>     libraries: [IOBrowserWidget],
>     widget: {
>         displayInWorkspace: true
>     }
> };
>
> const io = await IOBrowser(config);
> ```

## Improvements & Bug Fixes

> ⚠️ *Note that each improvement or bug fix is listed under all libraries it affects.*

> ### @interopio/browser
>
> - Added error events and event handlers for platform and client errors.

> ### @interopio/browser-platform
>
> - Added error events and event handlers for platform and client errors.
> - Fixed controller variable error that was causing a reference error.

> ### @interopio/fdc3
>
> - Improved handling of dynamic registration of Intent handlers.
> - Changed log level to warn instead of error when `broadcast()` is used while not joined to a Channel.

> ### @interopio/home-ui-react
>
> - Improved handling permission and user check when the window isn't a Browser Platform.
> - Improved instructions for the notifications permission prompt if it's automatically blocked by the browser.
> - Fixed `useBeforeunload()` firing on logout.

> ### @interopio/intents-resolver-api
>
> - Optimized the type of the `IOConnectIntentsResolver()` factory function.
> - Added [`@interopio/browser`](https://www.npmjs.com/package/@interopio/browser) as a dependency.

> ### @interopio/widget
>
> - Added a check to prevent the widget from showing in an app if the app is a Workspaces App acting as a Main app.
> - Fixed a bug where Channels wouldn't be displayed when switching from default to compact mode.
> - Improved types.
