Intents
Overview
In certain workflow scenarios, your app may need to start (or activate) a specific app. For instance, you may have an app showing client portfolios with financial instruments. When the user clicks on an instrument, you want to start an app which shows a chart for that instrument. In other cases, you may want to present the user with several options for executing an action or handling data from the current app.
The Intents API makes all that possible by enabling apps to register, find and raise Intents.
The case with the "Portfolio" and the "Chart" app above can be implemented in the following way:
The "Chart" apps registers an Intent called "ShowChart", specifying the data type (predefined data structure) that it works with - e.g., "Instrument".
When the user clicks on on instrument, the "Portfolio" app raises the "ShowChart" Intent, optionally specifying an Intent target, data type and app start up options.
This way, the "Portfolio" and "Chart" apps can be completely decoupled. If later the "Chart" app needs to be replaced, the new app for showing charts only needs to register the same Intent in order to replace the old one (provided that it works with the "Instrument" data structure as well).
Another case where the Intents API can be useful is if you want to find (and possibly filter) all apps that have registered a certain Intent. This may be because you want to present the user with all available (or appropriate) options for executing an action or handling data - e.g., on hover over an instrument or when clicking an instrument, the user sees a menu with all apps that have registered the Intent "ShowChart" and can work with the "Instrument" data structure:
All apps that can visualize data in charts register an Intent called "ShowChart", specifying the data structure they work with. Some of them work with "Instrument" data type, others work with different data types.
When the user clicks on an instrument in the "Portfolio" app, the "Portfolio" app searches for all registered Intents with a name "ShowChart" and filters them by the data type they work with.
The user sees a menu built on the fly which shows all currently available apps for visualizing charts that work with "Instrument" data type.
Defining Intents
Intents are either defined through the app definition, or dynamically at runtime. Intents are configured under the intents
top-level key of the app definition object in the Main app.
It's possible for different apps to register an Intent with the same name, which is useful when several apps perform the same action or work with the same data structure. This allows for easy replacement of apps. You may have an old app that has registered an Intent called "ShowChart"
which you want to replace with a new app. Your new app only needs to register the same Intent (you can either remove the old app or leave it as an additional option for the users who prefer it). No changes to the calling app are necessary - when it raises the "ShowChart"
Intent, the new app will be called.
To define an Intent, use the intents
top-level key in the app definition:
import IOBrowserPlatform from "@interopio/browser-platform";
const config = {
licenseKey: "my-license-key",
applications: {
local: [
{
name: "Instrument Chart",
details: {
url: "http://localhost:4242/chart"
},
// Intent definitions.
intents: [
{
name: "ShowChart",
displayName: "Instrument Chart",
contexts: ["Instrument"]
}
]
}
]
}
};
const { io } = await IOBrowserPlatform(config);
Property | Type | Description |
---|---|---|
contexts |
string[] |
The type of predefined data structures with which the app can work. |
displayName |
string |
The display name of the Intent. Can be used in context menus, etc., to visualize the Intent. |
name |
string |
Required. The name of the Intent. |
Intents Resolver
The Intents Resolver UI allows you to manually select which app to handle the raised Intent and to choose whether to start a new instance of an Intent handler or use an already running instance of it:
io.Connect Browser provides the @interopio/intents-resolver-ui-react
library and an Intents Resolver UI template which you can use to easily build your Intents Resolver App (see Extending the Intents Resolver section). The Intents Resolver UI is enabled by default for all Browser Client apps. It will be started when there are more than one handlers available for a raised Intent.
To disable the Intents Resolver UI for an app, set the enableIntentsResolverUI
property of the intents
key in the configuration object for the @interopio/browser
library to false
:
import IOBrowser from "@interopio/browser";
const config = {
intents: {
enableIntentsResolverUI: false
}
};
const io = await IOBrowser(config);
The intents
object has the following properties:
Property | Type | Description |
---|---|---|
enableIntentsResolverUI |
boolean |
If true (default), will enable using the Intents Resolver UI app for handling Intents. |
intentsResolverAppName |
string |
The name of the Intents Resolver UI app to be used for handling Intents. If not provided, the default app will be used. |
methodResponseTimeoutMs |
number |
Interval in milliseconds to wait for a response from the Intents Resolver UI app. |
Extending the Intents Resolver
You can use the Intents Resolver UI template when creating your own Intents Resolver App.
To use your custom Intents Resolver App in io.Connect Browser, you must provide an app definition for it. To create the Intents Resolver App itself, you can either use the <IOConnectIntentsResolverUI />
React component exported by the @interopio/intents-resolver-ui-react
library, or implement your own app and functionalities by using the @interopio/intents-resolver-api
library.
Configuration
Your custom Intents Resolver App, as every interop-enabled app, must have an app definition provided to the Main app:
import IOBrowserPlatform from "@interopio/browser-platform";
const config = {
licenseKey: "my-license-key",
applications: {
local: [
{
name: "my-custom-intents-resolver",
type: "window",
details: {
url: "http:localhost:3000"
}
}
]
}
};
const { io } = await IOBrowserPlatform(config);
By default, io.Connect Browser expects an Intents Resolver App with the name
property set to "intentsResolver"
. If you choose another name, you must specify it when initializing the io.Connect library in all Browser Clients that should use your custom Intents Resolver App when raising an Intent:
import IOBrowser from "@interopio/browser";
const config = {
intents: {
// Name of your custom Intents Resolver App.
intentsResolverAppName: "my-custom-intents-resolver"
}
};
const io = await IOBrowser(config);
Intents Resolver Component
Available since io.Connect Browser 3.4
The @interopio/intents-resolver-ui-react
library exports the <IOConnectIntentsResolverUI />
component which you can use to create your custom Intents Resolver app.
To use the library in your project, execute the following command:
npm install @interopio/intents-resolver-ui-react
The <IOConnectIntentsResolverUI />
component expects a configured and initialized io.Connect API object. You can pass the initialized API object directly to the component via its required config
prop, or you can use the @interopio/react-hooks
library and wrap the component in the <IOConnectProvider />
component.
The <IOConnectIntentsResolverUI />
component uses internally the functionalities provided by the @interopio/intents-resolver-api
library. This requires you to provide the IOConnectIntentsResolver()
factory function in the libraries
arrays of the configuration object for initializing the io.Connect API.
The default styles for the Intents Resolver App must be imported from the @interopio/intents-resolver-ui-react
library. You can override them by providing your own styles.
Initializing the io.Connect API and passing the API object to the <IOConnectIntentsResolverUI />
component as a prop:
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 Intents 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 = ReactDOM.createRoot(domElement);
root.render(
// Provide the initialized io.Connect API object to the component.
<IOConnectIntentsResolverUI config={{ io }} />
);
You can also use the <IOConnectProvider />
component from the @interopio/react-hooks
library to wrap the <IOConnectIntentsResolverUI />
component. This will automatically provide the initialized io.Connect API object to the <IOConnectIntentsResolverUI />
component:
import { IOConnectProvider } from "@interopio/react-hooks";
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 Intents 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 = {
browser: {
factory: IOBrowser,
config: {
libraries: [IOConnectIntentsResolver]
}
}
};
const domElement = document.getElementById("root");
const root = ReactDOM.createRoot(domElement);
root.render(
// The initialized io.Connect API object will be provided automatically
// to the `<IOConnectIntentsResolverUI />` component.
<IOConnectProvider settings={config}>
<IOConnectIntentsResolverUI config={{}} />
</IOConnectProvider>
);
The @interopio/intents-resolver-ui-react
library injects an ioResolver
object in the global window object. You can use it to retrieve the versions of the @interopio/intents-resolver-ui-react
and the @interopio/intents-resolver-api
libraries:
const { uiVersion, apiVersion } = ioResolver;
console.log(`UI version: ${uiVersion}, API version: ${apiVersion}`);
Intents Resolver API
The @interopio/intents-resolver-api
library offers out-of-the-box functionalities that facilitate the implementation of a custom Intents Resolver App.
Initialization
To use the library in your project, execute the following command:
npm install @interopio/intents-resolver-api
To initialize the @interopio/intents-resolver-api
library, pass its factory function to the libraries
array of the configuration object when initializing the @interopio/browser-platform
library:
import IOBrowser from "@interopio/browser";
import IOIntentsResolver from "@interopio/intents-resolver-api";
const config = {
libraries: [IOIntentsResolver]
};
const io = await IOBrowser(config);
Usage
The Intents Resolver API is accessible via the io.intents.resolver
object. It offers methods for listening for events fired when a new Intent handler has been added, or when an Intent handler has been removed. A method for sending the selected Intent handler to the app that has raised the Intent is also available.
To check the name of the Intent that has been raised, use the intent
property:
const intentName = io.intents.resolver.intent;
To listen for events fired when a new Intent handler app has been added or removed, use the onHandlerAdded()
and onHandlerRemoved()
methods respectively. Both methods receive as an argument a callback that will be invoked when the respective event is fired, and return an unsubscribe function. The callback receives as an argument a ResolverIntentHandler
object describing an Intent handler:
const callback = handler => console.log(`App name: ${handler.applicationName}, instance ID: ${handler.instanceId}`);
const unsubscribe = io.intents.resolver.onHandlerAdded(callback);
const unsubscribe = io.intents.resolver.onHandlerRemoved(callback);
The ResolverIntentHandler
object has the following properties:
Property | Type | Description |
---|---|---|
applicationIcon |
string |
Base64 string of the app icon. |
applicationName |
string |
Name of the handler app as defined within the io.Connect framework. |
instanceId |
string |
Unique ID of the app instance within the io.Connect framework. |
The onHandlerAdded()
and onHandlerRemoved()
methods allow you to keep an up-to-date list of the available Intent handlers. After the user selects from the UI which app to start or which already running instance to use, you can find the selected handler from the list and send it to the app that has raised the Intent. Use the sendResponse()
method and pass a ResolverIntentHandler
object as an argument:
await io.intents.resolver.sendResponse(selectedIntentHandler);
Saving Intent Handlers
Available since io.Connect Browser 3.4
To save a chosen Intent handler as a handler for the raised Intent, pass a SendResolverResponseOptions
object as a second optional argument to the sendResponse()
method.
If the Intents 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:
const options = {
saveHandler: true,
// Providing the Intent name is necessary only when the Intents Resolver UI
// was opened via the `filterHandlers()` method instead of the `raise()` method.
name: "ShowChart"
};
await io.intents.resolver.sendResponse(selectedIntentHandler, options);