Notifications
Overview
Notifications represent events important enough to be brought to the attention of the user. Some examples are:
- an alert from an app for an automatic machine restart, or from a metrics monitoring tool for a system running hot;
- a trade order execution notification from an Order Management System;
- a workflow task assigned to a user or a group of users that can optionally need a response - e.g., a change of client address;
- an activity that is forwarded or re-assigned to a user - e.g., handling a client call;
io.Connect Desktop provides a notification service that normalizes and consolidates notifications from different apps and delivers them directly to the user on the desktop in a customizable UI as both toasts and ordered lists in a Notification Panel.
The notification toasts and the Notification Panel are configurable and completely customizable system apps.
Interop-enabled apps can use the io.Connect Notification APIs to raise notifications at runtime.
Using Notifications
io.Connect Desktop shows notifications on the desktop using notification toasts and a Notification Panel:
Notification Toasts
Notifications can be shown on the desktop as notification toasts. You can close them manually, snooze them, or interact directly with them by clicking on the notification or on an action button in it. Toasts expire based on configuration and hide automatically after the specified time has elapsed:
Notification toasts can be configured from the "Settings" menu of the Notification Panel to group automatically in stacks by app or by severity. This improves the user experience and saves space on the screen. When you click on a notification stack, it expands and displays all notification toasts in it. You can then interact with the notifications in it, clear the entire stack, or collapse it by clicking on a notification in it:
Notification Panel
The Notification Panel is accessible via the io.Connect Floating Toolbar or the io.Connect Launchpad and contains all received notifications that haven't been closed or acknowledged by the user. Notifications in it can be sorted by date, severity, snoozed status, read or unread status. You can close notifications individually. snooze them, or interact with them by clicking on the notification or on an action button in it. You can use the "Clear All" button to clear all notifications from the Notification Panel:
Notification Settings
The Notification Panel has a "Settings" button that opens a menu with notification settings. You can use it to enable or disable notifications globally or per app and provide various settings for the Notification Panel and the notification toasts:
After selecting the desired settings, close the Notification Settings screen and your changes will be saved automatically.
Snoozing Notifications
Available since io.Connect Desktop 9.3
Notifications can be snoozed from the UI and programmatically. Snoozed notifications will be shown to the user again as notification toasts after the specified snooze duration has elapsed. To snooze a notification manually, hover over a notification toast or a notification inside the Notification Panel and click the "Snooze" button that appears on hover. You can set the snooze duration from the "Settings" page in the Notification Panel:
Notification Badges
Notification badges will appear on the io.Connect Desktop taskbar icon and the system tray icon when there are pending notifications, so that the user won't miss them. The badge on the taskbar icon shows the number of notifications that the user hasn't seen yet, while the system tray icon badge simply alerts the user that there is at least one new notification:
Configuration
By default, notifications are enabled in io.Connect Desktop. To specify global notification settings, use the "notifications"
top-level key in the system.json
system configuration file located in the %LocalAppData%/interop.io/io.Connect Desktop/Desktop/config
folder.
The "notifications"
object has the following properties:
Property | Type | Description |
---|---|---|
"closeNotificationOnClick" |
boolean |
If true , when the user clicks on the body of a notification in the Notification Panel, the notification will be removed from the list. Set to false if you want users to be able to click on the notification body (e.g., to copy text) without the notification disappearing from the Notification Panel. Available since io.Connect Desktop 9.6. |
"enabled" |
boolean |
If true (default), will enable all notifications - notification toasts and notifications in the Notification Panel. |
"enableToasts" |
boolean |
If true (default), will enable notification toasts. |
"placement" |
object |
Placement settings for the Notification Panel and the notification toasts. Available since io.Connect Desktop 9.2. |
"showNotificationBadge" |
boolean |
If true , will enable showing a notification badge on the tray menu icon, in the "Notifications" section of the io.Connect launcher, and on the taskbar icon of the io.Connect launcher (or your shell app). Available since io.Connect Desktop 9.4. |
"snooze" |
object |
Settings for snoozing notifications. Available since io.Connect Desktop 9.3. |
"sourceFilter" |
object |
Filter with names of apps that are allowed or not allowed to raise notifications. |
"toastExpiry" |
number |
Interval in milliseconds after which the notification toasts will be hidden. Defaults to 15000 . |
"toasts" |
object |
Settings for the notification toasts. |
The "snooze"
object has the following properties:
Property | Type | Description |
---|---|---|
"duration" |
number |
Interval in milliseconds for which the notifications will be snoozed. Defaults to 600000 . |
"enabled" |
boolean |
If true , will enable snoozing notifications. Defaults to false . |
The "placement"
object has the following properties:
Property | Type | Description |
---|---|---|
"panel" |
"left" | "right" |
Sets the position of the Notification Panel on the screen - left or right. Defaults to "right" . |
"toasts" |
"top-left" | "top-right" | "bottom-left" | "bottom-right" |
Sets the position of the notification toasts on the screen - top right, top left, bottom right or bottom left. Defaults to "bottom-right" . |
The "sourceFilter"
object has the following properties:
Property | Type | Description |
---|---|---|
"allowed" |
string[] |
Names of apps allowed to raise notifications. Defaults to [] . |
"blocked" |
string[] |
Names of apps not allowed to raise notifications. Defaults to [] . |
The "toasts"
object has the following properties:
Property | Type | Description |
---|---|---|
"mode" |
"single" | "stacked" |
Mode for the notification toasts. Notification toasts can appear individually, or can be stacked together based on a criterion (severity or app that has raised the notification). Defaults to "single" . |
"stackBy" |
"severity" | "application" |
Criterion for stacking the notification toasts. Defaults to "severity" . |
The following is an example configuration for notifications:
{
"notifications": {
"enabled": true,
"enableToasts": true,
"sourceFilter": {
"allowed": ["*"]
},
"toastExpiry": 15000
}
}
Extending Notifications
The @interopio/components-react
library enables you to create your own Notifications App for io.Connect Desktop. The library provides hooks and default components which you can use to build your own Notifications App by using, modifying or replacing the available components and functionalities.
To use the library in your project, execute the following command:
npm install @interopio/components-react
⚠️ Note that the
@interopio/components-react
library doesn't include a built Notifications App. A Notifications App is provided in io.Connect Desktop. You can also use and customize the Notifications App template.
Configuration
To use your custom Notifications App built with the @interopio/components-react
library, modify the notifications.json
app definition file located in %LocalAppData%/interop.io/io.Connect Desktop/Desktop/config/apps
. Set the "url"
property of the "details"
top-level key to the URL of your custom Notifications App:
{
"details": {
"url": "http://localhost:3000"
}
}
To change the default location of the Notification Panel on the screen, use the "placement"
property of the "details"
top-level key in the app definition file. The following example demonstrates how to place the Notification Panel on the left of the screen and change its width in pixels:
{
"details": {
"placement": {
// Make the Notification Panel preserve its specific position in case of screen resolution changes.
"snapped": true,
// Place the Notification Panel on the left of the screen.
"horizontalAlignment": "left",
// Stretch the Notification Panel to occupy the entire screen height.
"verticalAlignment": "stretch",
// Change the Notification Panel width.
"width": 350
}
}
}
For more details on io.Connect Window placement, see the Developers > Configuration > Application > Placement section.
Hooks
The useNotificationsContext()
hook provides context information about the Notifications App and the available notifications, as well as methods for the default notification functionalities which you can use directly in your custom components:
const {
allApplications,
allApps,
allowedApplications,
clearAll,
clearAllOld,
configuration,
hidePanel,
hideSettingsPanel,
isPanelVisible,
isSettingsVisible,
notifications,
notificationStack,
saveFilter,
saveSettings,
setAutoHidePanel,
setHideOnClose,
settings,
showPanel,
showSettingsPanel,
sortBy
} = useNotificationsContext();
Property | Type | Description |
---|---|---|
allApplications |
number |
The number of all available io.Connect apps. |
allApps |
object[] |
Array of objects describing all available io.Connect apps. Each object has title , name and allowed properties. The allowed property holds a Boolean value specifying whether the app is allowed to raise notifications. |
allowedApplications |
number |
The number of io.Connect apps allowed to raise notifications. |
clearAll() |
function |
Clears all notifications from the Notification Panel. |
clearAllOld() |
function |
Clears all notifications that have already been seen by the user in the Notification Panel. |
configuration |
object |
Object with configuration for the io.Connect Notifications. |
hidePanel() |
function |
Hides the Notification Panel. |
hideSettingsPanel() |
function |
Hides the "Settings" section in the Notification Panel. |
isPanelVisible |
boolean |
Whether the Notification Panel is currently visible. |
isSettingsVisible |
boolean |
Whether the "Settings" section in the Notification Panel is currently visible. |
notifications |
object[] |
A list of all available notification objects. |
notificationStack |
object[] |
A list of all available notification stacks. Each notification stack object has key and items properties. The key property holds a string value which is either the name of the app which has raised the notifications in the stack, or the notification severity, depending on the criteria by which the notifications are grouped in stacks. The items property holds an array of all notification objects in the stack. |
saveFilter() |
function |
Accepts an array of objects as an argument, each object describing an io.Connect app (same object structure as the objects in the allApps array). This array is used as a filter for determining which app is allowed to raise notifications. |
saveSettings() |
function |
Accepts as an argument an object describing the updated notification settings (same object structure as the object in the settings property). Saves the current notification settings. |
setAutoHidePanel() |
function |
Accepts a Boolean value as an argument and specifies whether the Notification Panel should hide automatically when it loses focus. |
setHideOnClose() |
function |
Accepts a Boolean value as an argument and specifies whether the Notification Panel should be hidden or closed when the user presses ALT + F4 . |
settings |
object |
Object describing the current notification settings (see below). |
showPanel() |
function |
Shows the Notification Panel. |
showSettingsPanel() |
function |
Shows the "Settings" section in the Notification Panel. |
sortBy() |
function |
Sorts the notifications in the Notification Panel. Accepts a string value as an argument. Pass "timestamp" to show the latest notifications, "priority" to sort notification by priority, or "application" to sort the notifications by the name of the app that has raised them. |
The settings
object has the following properties:
Property | Type | Description |
---|---|---|
alwaysOnTop |
boolean |
Whether the Notification Panel should always be on top. |
autoHidePanel |
boolean |
Whether the Notification Panel should hide automatically when it loses focus. |
enabledNotifications |
boolean |
Whether notifications are enabled or disabled globally. |
stackBy |
"application" | "severity" |
Whether the notifications are to be stacked by the app that has raised them, or by severity. |
toastStacking |
boolean |
Whether grouping notifications in stacks is enabled or disabled. |
Components
All default components can be reused and composed with custom code. If usage of such component has been detected, its default behavior will be applied. For instance, if you use the <PanelFooterClearAllButton />
component, it will automatically clear all notifications when the button is clicked, without the need of custom code to induce this behavior. If you pass the same component more than once, an error will be thrown.
To remove a component, pass a <Fragment />
component. If you want to use a default functionality (e.g., for clearing or sorting notifications) in your custom components, use the useNotificationsContext()
hook to acquire it and pass it to your component.
In your custom Notifications App, you should place the <Panel />
and <Toasts />
components in the <NotificationsProvider />
component. This component is a React context provider component that provides notification-related data and functionality accessible via the useNotificationsContext()
hook. The <NotificationsProvider />
component should contain a <NotificationsWrapper />
component whose purpose is to render either the <Panel />
or the <Toasts />
component when a notification is received, based on whether the Notification Panel is currently visible or not. If, for instance, you don't want to use notification toasts, you can place your customized <Panel />
component directly under the <NotificationsProvider />
component.
The following example demonstrates a basic usage of the library:
import {
NotificationsProvider,
useNotificationsContext
} from "@interopio/components-react";
import CustomPanel from "./CustomPanel";
import CustomToasts from "./CustomToasts";
// Create a wrapper component that will render either the Notification Panel or
// the toasts wrapper when a notification is received based on whether the panel is visible.
const NotificationsWrapper = () => {
const { isPanelVisible } = useNotificationsContext();
return isPanelVisible ? <CustomPanel /> : <CustomToasts />;
};
const App = () => {
return (
<NotificationsProvider>
<NotificationsWrapper />
</NotificationsProvider>
);
};
export default App;
Notification Panel
The <Panel />
component is used for rendering the Notification Panel. The following example demonstrates the structure of the <Panel />
component, its properties and default values:
<Panel
hideOnFocusLost={true}
hideOnClose={true}
components={{
NotificationsList: NotificationsList,
Notification: Notification,
Header: () => {
return (
<div>
<HeaderCaption />
<div>
<HeaderSortButtons />
</div>
</div>
);
},
HeaderCaption: () => {
return (
<div>
<HeaderCaptionTitle />
<HeaderCaptionFilter />
<HeaderCaptionButtons />
</div>
);
},
HeaderCaptionButtons: () => {
return (
<ButtonGroup>
<HeaderCaptionButtonSettings />
<HeaderCaptionButtonClose />
</ButtonGroup>
);
},
HeaderCaptionButtonClose: HeaderCaptionButtonClose,
HeaderCaptionButtonSettings: HeaderCaptionButtonSettings,
HeaderCaptionFilter: HeaderCaptionFilter,
HeaderCaptionTitle: HeaderCaptionTitle,
HeaderSortButtons: () => {
return (
<ButtonGroup>
<HeaderSortButtonsTimestamp />
<HeaderSortButtonsPriority />
<HeaderSortButtonsApplication />
</ButtonGroup>
);
},
HeaderSortButtonsApplication: HeaderSortButtonsApplication,
HeaderSortButtonsPriority: HeaderSortButtonsPriority,
HeaderSortButtonsTimestamp: HeaderSortButtonsTimestamp,
Body: () => {
return (
<div>
<NotificationsList />
</div>
);
},
Footer: () => {
return (
<div>
<FooterButtons />
</div>
);
},
FooterButtons: () => {
return (
<ButtonGroup>
<FooterButtonsClearAllOld />
<FooterButtonsClearAll />
</ButtonGroup>
);
},
FooterButtonsClearAll: FooterButtonsClearAll,
FooterButtonsClearAllOld: FooterButtonsClearAllOld,
PanelSettings: () => {
return (
<div>
<Modal title="Notification Settings">
<Block title="General Settings">
<div>
<Toggle label="Allow Notifications" />
</div>
<div>
<Toggle label="Auto Hide Panel" />
</div>
<div>
<Toggle label="Toast Stacking" />
</div>
<div>
<Radio label="Stack by Application" />
<Radio label="Stack by Severity" />
</div>
</Block>
<Block title="Allowed Notifications">
<div/>
<div>
<div>
<span>Subscriptions</span>
</div>
<Toggle label="All Applications" />
</div>
{apps.map((app) => (
// All available io.Connect apps.
<div>
<span>{app.title ?? app.name}<span/>
</div>
<div>
<Toggle label={app.allowed ? "Subscribed" : "Unsubscribed"} />
</div>
))}
</div>
</Block>
</Modal>
</div>
);
},
Block: Block,
Modal: Modal,
Radio: Radio,
Toggle: Toggle
}}
/>
The following example demonstrates creating a custom Notification Panel with custom header, footer and custom text on the buttons for clearing notifications. The header contains two custom buttons for sorting notifications that use the default functionality for sorting by timestamp and by priority:
import {
useNotificationsContext
Panel,
PanelFooterButtons,
PanelFooterClearAllButton,
PanelFooterClearAllOldButton
} from "@interopio/components-react";
// Get the default method for sorting notifications.
const { sortBy } = useNotificationsContext();
// Custom header for the Notification Panel containing
// custom buttons with default sorting functionality.
const CustomPanelHeader = () => {
return (
<div>
<button onClick={() => sortBy("timestamp")}>Show Latest</button>
<button onClick={() => sortBy("priority")}>Sort by Priority</button>
</div>
);
};
// Custom footer for the Notification Panel.
const CustomPanelFooter = ({ className, ...rest }) => {
return (
<div className={className} {...rest}>
<div>Custom Footer</div>
<PanelFooterButtons />
</div>
);
};
const CustomPanel = () => {
return (
<Panel
// The Notification Panel will remain visible when it loses focus.
hideOnFocusLost={false}
components={{
Header: CustomPanelHeader,
// Providing a custom class for the footer.
Footer: props => <CustomPanelFooter className={"my-custom-class"} {...props}/>,
// Providing custom text for the Notification Panel footer buttons.
FooterButtonsClearAll: () => <PanelFooterClearAllButton text="Remove All" />,
FooterButtonsClearAllOld: () => <PanelFooterClearAllOldButton text="Remove All Read" />
}}
/>
);
};
export default CustomPanel;
By default, the Notification Panel app will only be hidden when the user tries to close it. If you want the Notification Panel app to close instead of hide, set the hideOnClose
property to false
:
const CustomPanel = () => {
return <Panel hideOnClose={false} />
};
export default CustomPanel;
Toasts Wrapper
The <Toasts />
component is used for rendering the toasts wrapper which shows one or more notification toasts when the Notification Panel is closed. The following example demonstrates the structure of the <Toasts />
component, its properties and default values:
<Toasts
maxToasts={10}
components={{
NotificationsList: NotificationsList,
Notification: Notification,
Header: () => <></>,
Body: () => {
return (
<div>
<NotificationsList />
</div>
);
},
Footer: () => <></>
}}
/>
The following example demonstrates creating a custom toasts wrapper containing a custom header and footer. The toasts wrapper will contain a maximum of three notifications at a time and will appear at the top of the screen, instead of at its default location at the bottom of the screen. The functionality for getting a list of the active notifications (the ones for which the time limit set in the "toastExpiry"
notification configuration property hasn't elapsed yet) and limiting it up to the number specified in the maxToasts
property is taken directly from the default <Body />
component, part of the <ToastsWrapper />
component, provided by the library:
import { useEffect, useState } from "react";
import { Toasts, Notification, NotificationsList } from "@interopio/components-react";
const CustomToastsBody = ({ className, notifications, maxToasts, ...rest }) => {
const [activeNotifications, setActiveNotifications] = useState([]);
// Getting the active notifications and limiting the list of notification toasts
// displayed in the toasts wrapper up to the number specified in the `maxToasts` property.
useEffect(() => {
const active = notifications?.filter(n => n.state === "Active").slice(0, maxToasts);
setActiveNotifications(active);
}, [notifications]);
return (
// Use the `"toast-top"` class to make the toasts wrapper appear at the top of the screen.
<div className={`toast-top ${className}`} {...rest}>
<NotificationsList
Notification= {Notification}
notifications={activeNotifications}
noNotificationText=""
/>
</div>
);
};
const CustomToasts = () => {
return (
<Toasts
// Specifying the number of notifications that can appear in the toasts wrapper.
maxToasts={3}
components={{
Header: () => <div>Custom Header</div>,
// Passing a custom class.
Body: props => <CustomToastsBody className={"my-custom-class"} {...props} />,
Footer: () => <div>Custom Footer</div>
}}
/>
);
};
export default CustomToasts;
If you want the toasts wrapper to adjust dynamically and display all currently active notifications, instead of limiting their number, pass a negative value to the maxToasts
property:
const CustomToasts = () => {
return <Toasts maxToasts={-1} />
};
export default CustomToasts;
Notification
The <Notification />
component is used for rendering the notification itself. The following example demonstrates the structure of the <Notification />
component, its properties and default values:
<Notification
notification={Notification}
components={{
Body: () => {
return (
<div>
<BodyIcon />
<div>
<BodyTitle />
<BodyDescription />
<BodyTimestamp />
</div>
</div>
);
},
BodyDescription: BodyDescription,
BodyIcon: BodyIcon,
BodyTitle: BodyTitle,
Footer: () => {
return (
<div>
<ButtonGroup>
{notification.actions?.map(action => <FooterButton />)}
</ButtonGroup>
<FooterTimestamp />
</div>
);
},
FooterButton: FooterButton,
FooterTimestamp: FooterTimestamp,
Header: () => {
return (
<div>
<HeaderBadge />
<HeaderState />
<HeaderTitle />
<HeaderButton />
</div>
);
},
HeaderBadge: HeaderBadge,
HeaderButton: HeaderButton,
HeaderIcon: HeaderIcon,
HeaderState: Header State,
HeaderTitle: HeaderTitle,
NotificationStack: NotificationStack
}}
/>
The following example demonstrates creating a custom notification for the Notification Panel that has a customized header and body titles. The color of the notification titles will change depending on the severity of the received notification:
import { Panel, Notification } from "@interopio/components-react";
const CustomNotification = ({ notification }) => {
const { severity, appTitle, title } = notification;
let textColor;
// Assign a color value according to the notification severity.
switch (severity) {
case "Low": textColor = "#0066c0";
break;
case "Medium": textColor = "#43a047";
break;
case "High": textColor = "#f9a825";
break;
case "Critical": textColor = "#ff511f";
break;
default: textColor = "grey"
};
return (
<Notification
notification={notification}
components={{
HeaderTitle: () => <div style={{color: textColor}}>{appTitle}</div>,
BodyTitle: () => <h1 style={{color: textColor}}> {title}</h1>
}}
/>
);
};
const CustomPanel = () => {
return (
<Panel
components={{
Notification: CustomNotification
}}
/>
);
};
export default CustomPanel;