Skip to main content

Notifications

Finsemble's notifications refresh brings with it 3 new pieces of UI. A Notification bell Icon, new and improved Toasts, and a fully featured Notification Center.

Notifications UI

The Notification Bell Icon indicating that there are unread notifications.
Notification Bell Icon indicating that there are unread notifications.

A notification toast sliding out to grab the user's attention.
A Notification Toast sliding out to grab the user's attention.

A fully featured Notification Center.
A fully featured Notification Center.

Notification ID

Each notification object contains an ID that Finsemble uses to manage notifications sent to various subscribers. You can specify the notification ID yourself by setting it before you send it to the notify() function. (We will see more detail about this function in the next section.) If you leave it blank, Finsemble automatically sets one before sending it out.

Imagine both the Notification center and an app are subscribed to receive notifications. When you send a notification, both the center and the app will receive the notification object. Conceptually both of them receive the same notification object. Behind the scenes, since the app and the notification center are in different windows, they are actually getting different instances of that notification object, each with the exact same values, including the notification ID. Without instances sharing the same ID, they would look the same to the user since they have the same content, but any change in the values of the notification would not be reflected across the system.

When the receiving app performs an action on a notification, for example mark as read, this should be reflected in the notification center and all other apps (subscribers) and not only the app that performed the action. When the notification instances share the same ID, we use the ID to let all other apps know a specific notification's state has changed. Finsemble broadcasts this new state and the notification id to all apps that are subscribing to notifications. It is the responsibility of the app to use the new state and take the appropriate action. Using the notification ID and the new state it is possible for this new state to be reflected on the correct notification in the UI.

The Notification Center provides search functionality. The search is case insensitive and only gets triggered when the search term is at least 3 characters long. Additionally, the search is atomic, meaning that it will try to match the exact characters in the search term as opposed to a best match approach with approximate results.

There are also 2 different search modes. Targeted search and generic search.

  • Targeted search - allows you to specify the field to which the search term will be applied. The targeted search can be performed by prefixing a field: to your search term. The available fields are: title:, details: and source: and the search term will only be applied to the selected field. If an invalid field is inserted, this will be ignored and a Generic search will be performed.

  • Generic search - will search for the search term in all fields (title, details, source).

Notification modes

Finsemble UI distinguishes between two modes of Notification, informational and actionable.

Any notification that does not have actions is treated as an informational notification. Informational notifications differ in behavior from Actionable ones in that Informational notifications will disappear from the Toast UI after a configurable amount of time. Whereas an Actionable Notification will stay on the screen waiting for user interaction.

Sending an informational notification

FSBL.Clients.NotificationClient.notify({
// id: "adf-3484-38729zg", // distinguishes individual notifications - provided by Finsemble if not supplied
// issuedAt: "2021-12-25T00:00:00.001Z", // The notifications was sent - provided by Finsemble if not supplied
// type: "configDefinedType", // Types defined in the config will have those values set as default
source: "Finsemble", // Where the Notification was sent from
title: "Request for Quote",
details: "RIO - 9409 @ $9409",
// headerLogo: "URL to Icon",
// actions: [], // Note this has no Actions making it Informational
// meta: {}, // Use the meta object to send any extra data needed in the notification payload
// devModeOnly: true // A flag to determine whether the notification is only to be displayed if Finsemble is running in dev mode.
})
note

The previous example defined the Notification inline. You're also able to instantiate a Notification object for better code completion and discoverability of the Notification fields.

note

If you want a notification to be displayed only if Finsemble is running in dev mode then set devModeOnly: true as a property in the Notification object.

const notification = new FSBL.Clients.NotificationClient.Notification();
notification.source = "Finsemble";
notification.title = "Request for Quote";
notification.details = "RIO - 9409 @ $9409";
FSBL.Clients.NotificationClient.notify(notification);

Sending an actionable notification

It is possible for users to perform actions on received notifications. You can achieve this by populating the actions field of the Notification object with one or more Action objects. An Action object has 8 fields: buttonText, type, milliseconds, component, spawnParams, channel, payload, and markAsRead. As the name suggests, the buttonText field is the text that appears on the action's button. The type field indicates the type of action the user can perform. markAsRead, when set to true, marks the notification as read after the user performs the action. The rest of the fields are used depending on the type of action being performed.

Finsemble supports 6 action types: SNOOZE, SPAWN, DISMISS, TRANSMIT, QUERY and PUBLISH.

When setting the action type to SNOOZE, use the milliseconds field to tell Finsemble how long to snooze the specific notification for.

A DISMISS action marks the specific notification as read and doesn't use any of the other action fields.

An action with the type set to SPAWN spawns an app using the component field as the type of app to be spawned and the spawnParams field as the spawn params. This is functionally equivalent to calling FSBL.Client.LauncherClient.spawn(component, spawnParams) See the LauncherClient documentation for more info on the values of these fields.

Use the TRANSMIT, QUERY, and PUBLISH to perform complex or custom actions. Actions of these types all use the channel and payload fields. See the Custom actions section below for more info on how to use these action types.

note

Actions with the type set to SNOOZE or DISMISS ignore the markAsRead field.

note

The DISMISS Action is equivalent to marking a notification as read. Don't confuse it with the dismissing of a toast from view.

Here is an example sending actions along with a notification:

// Anatomy of A spawn action
const spawnAction = {
buttonText: "Open Component",
type: FSBL.Clients.NotificationClient.ActionTypes.SPAWN,
component: "Welcome Component", // The Component Type to spawn when using the spawn action.
spawnParams: {}, // The spawnParams passed to the spawn function. See the spawn function on the LauncherClient for more info.
markAsRead: false // If true, the Notification will be marked as read after performing the action.
}

// Anatomy of SNOOZE action
// You're also able to instantiate an equivalent Action object to allow for better code completion.
const snoozeAction = new FSBL.Clients.NotificationClient.Action(); // A single action has been added making this an Actionable Notification
snoozeAction.buttonText = "Snooze";
snoozeAction.type = FSBL.Clients.NotificationClient.ActionTypes.SNOOZE;
snoozeAction.milliseconds = "300000"; // Used to override the snooze length when using the SNOOZE action type
// NOTE: the markAsRead value is ignored for SNOOZE and DISMISS actions.


// Anatomy of QUERY, TRANSMIT and PUBLISH actions
const queryAction = {
buttonText: "My Custom Action",
type: FSBL.Clients.NotificationClient.ActionTypes.QUERY, // |TRANSMIT|PUBLISH,
channel: "my.router.channel", // Channel to transmit payload on QUERY, TRANSMIT and PUBLISH actions
payload: { myData: "value" } // Payload transmitted along channel on QUERY
}


FSBL.Clients.NotificationClient.notify({
source: "Finsemble", // Where the Notification was sent from
title: "Request for Quote",
details: "RIO - 9409 @ $9409",
actions: [spawnAction, queryAction, snoozeAction]
// Actions have been added making this an Actionable Notification
})

Action groups

Action Groups allows combining related actions in the UI.

A groups of actions combined in Action Group (closed)

A group of actions combined into an Action Group (closed)

A groups of actions combined in Action Group (closed)

A group of actions combined into an Action Group (open)

Sending an Action Group is as easy as sending a notification with two or more notifications combined in an array.

const snoozeAction = {
buttonText: "Snooze",
type: FSBL.Clients.NotificationClient.ActionTypes.SNOOZE
}

const snoozeAction2 = {
buttonText: "5 Min",
type: FSBL.Clients.NotificationClient.ActionTypes.SNOOZE,
milliseconds: "300000"
}

const snoozeAction3 = {
buttonText: "10 Min",
type: FSBL.Clients.NotificationClient.ActionTypes.SNOOZE,
milliseconds: "600000"
}

// Combine them in an array
const actionGroup = [snoozeAction, snoozeAction2, snoozeAction3]

// Send
FSBL.Clients.NotificationClient.notify({
source: "Finsemble", // Where the Notification was sent from
title: "Request for Quote",
details: "RIO - 9409 @ $9409",
actions: [actionGroup] // You are able to mix and match Action Groups and Individual Actions
})

Custom actions

Snoozing, spawning a component, and marking a notification as read (DISMISS) are very useful ways of interacting with a notification on their own. These actions have some limitation in that they perform only a single operation. If you're looking to augment your user's workflow with more complex Notification to operation behavior, this is where the QUERY, TRANSMIT and PUBLISH actions shine.

Sending a notification with a QUERY, TRANSMIT or PUBLISH action can be seen below. Take a look at the Router tutorial for information on which communication model is best for your use case.

Sending a notification with a TRANSMIT action:

const queryAction = {
buttonText: "My Custom Action",
type: FSBL.Clients.NotificationClient.ActionTypes.TRANSMIT, // |QUERY|PUBLISH,
channel: "my.custom.action", // Channel to transmit payload on QUERY, TRANSMIT and PUBLISH actions
payload: { myData: "value" }, // Payload transmitted along channel on QUERY
markAsRead: false,
};

FSBL.Clients.NotificationClient.notify({
source: "Finsemble", // Where the Notification was sent from
title: "Request for Quote",
details: "RIO - 9409 @ $9409",
actions: [queryAction],
});

We suggest setting up listeners to perform complex Actions inside a Finsemble Service as to separate concerns where possible but like with any Router api call, it can be done in a component too.

Setting up listener to perform complex queries.

const { RouterClient, NotificationClient } = FSBL.Clients; // Component Syntax
// const { RouterClient, NotificationClient } = Finsemble.Clients; // Service Syntax

RouterClient.addListener(
"my.custom.action", // Matches action.channel
(error, response) => {
if (!error) {
let notification = response.data.notification;
let payload = response.data.actionPayload;

console.log(notification);
console.log(payload);
// performCustomOperation(notification, payload)

// Change the notification state and re-notify it
notification.isRead = true;
NotificationClient.notify(notification);
}
}
);

Notifications from an external service

When sending notifications, you might want to know the last time Finsemble received a notification from a specific source. This will allow you to send any through any previously queued notifications if Finsemble was shut down at the time of the initial sending.

const sendQueuedNotifications = async () => {
const notificationSource = "Finsemble";
const isoFormattedTimeStamp = await FSBL.Clients.NotificationClient.getLastIssuedAt(notificationSource);
console.log(isoFormattedTimeStamp);

// Fetch Notifications from External web service since "isoFormattedTimeStamp"
// Send multiple notifications
// FSBL.Clients.NotificationClient.notify([newNotification1, newNotification2, ...]);
};

sendQueuedNotifications();

Getting notifications inside your components

There are two ways to get notifications. Getting previously sent notifications and subscribing to future notifications.

Getting previously sent notifications

// Get notifications sent between now and one month ago
const getNotificationsHistory = async () => {
let lastMonth = new Date();
lastMonth.setMonth(lastMonth.getMonth() - 1);

const notifications = await FSBL.Clients.NotificationClient.fetchHistory({sentAfter: lastMonth.toISOString()});
console.log(notifications);
};

getNotificationsHistory();

Subscribe to all future notifications

const notificationsSubscribe = async () => {
const subscription = {};
const subData = await FSBL.Clients.NotificationClient.subscribe(subscription, (notification) => {
console.log("Notification Received", notification);
// DO something
});
// use subData to unsubscribe
};

notificationsSubscribe();

Subscribe to a subset of notifications

It may be that you only want to receive some notifications. This is possible by providing a filter when performing the subscription.

const filterSubscribe = async () => {
const subscription = {
filter: {
// The callback below will be executed if notifications with notification.type == 'chat-notification' AND
// notification.source == 'Symphony' are received
include: [
{
type: "chat-notification",
source: "Symphony",
},
// It's also possible to do deep matches
{ "meta.myCustomObject.field": "email-notification" }, // For an OR match add two objects in the include filter
],
// The exclude filter works the same as the include filter but the callback will NOT be executed if there
// is a match. Exclude matches take precedence over include matches.
exclude: [],
},
};
const subData = await FSBL.Clients.NotificationClient.subscribe(subscription, (notification) => {
console.log("Filter Matches. Notification Received", notification);
// DO something
});
// use subData to unsubscribe
};

filterSubscribe();

// The notification below will be Received with the filter above
FSBL.Clients.NotificationClient.notify({
source: "Finsemble", // Where the Notification was sent from
title: "Request for Quote",
details: "RIO - 9409 @ $9409",
type: "chat-notification",
source: "Symphony",
});

Send notifications to the OS

Using the config, you can also send your notifications to the OS. Do this by specifying the proxyToWebApiFilter on the servicesConfig.notifications object in the seed ./configs/application/config.json. This is achieved by specifying a filter for the proxyToWebApiFilter field.

note

Notifications proxied to the OS have the limitation of not being able to perform actions.

For more information on Filters and Filtering see Subscribe to a subset of Notifications and Subscription Filters Explained

Example config:

{
"servicesConfig": {
"notifications": {
"proxyToWebApiFilter": {
"include": [
{
"type": "web"
}
],
"exclude": []
}
}
}
}

Notification types (Templates)

Defining a Notification type allows you to apply a default set of values to notifications. When a notification arrives with the type field set, the template is retrieved from the configuration and the values specified are applied to any empty/unset fields. Hence, a Notification Type is a template, making it possible to populate a Notification by only setting the type (plus any other field you need to populate or override).

Notification Types also inform the preferences UI, allowing granularity on Muting Notifications and giving users a finer level of control, by allowing them to specify Types of Notifications from each source to mute (as opposed to all notifications from that source).

Specifying a Type using the key default, will apply defaults to all notifications sent, provided that they do not match any other notification type specified.

Configure Types by adding to the servicesConfig.notifications.types object in ./configs/application/config.json in your Finsemble project.

Example type definition:

{
"servicesConfig": {
"notifications": {
"types": {
"urgent": {
"defaults": {
"cssClassName": "my-urgent-class-name",
"actions": [
{
"buttonText": "Mark Read",
"type": "DISMISS"
}
]
}
}
}
}
}
}

The Type defined in the config above is urgent, and in the code snippet below, the type is set to urgent. Since the notification sent has no actions and no cssClassName, these fields will be automatically populated with the values specified in the type's config.

FSBL.Clients.NotificationClient.notify([
{
source: "Finsemble", // Where the Notification was sent from
title: `Request for Quote`,
details: "RIO - 9409 @ $9409",
type: "urgent",
},
]);

Customizing the display of notifications

Toast duration

Notification toasts are designed to draw the user's attention to an event. Toasts are displayed for a short period of time that is usually determined by a system default. There are a few way you can change the behavior and length of time a toast is displayed.

The Notification object has a timeout field. This field tells Finsemble how long it should display a specific notification toast. If you don't specify this value on the notification itself, Finsemble uses the system default. The default value is determined by the config setting finsemble.servicesConfig.notifications.informationalNotificationToastPeriodSeconds. This setting changes the toast duration for all notifications that don't have a timeout specified on the notification object itself. A user can override this value by specifying different preferences.

note

Even though the field name is prefixed with "informational", you can apply this setting to both actionable and informational toasts.

By default, both actionable and informational toasts get hidden from view after a specific amount of time. The config value finsemble.servicesConfig.notifications.toastTimeoutOnActionableNotifications changes the behavior so that all toast with an action stay in view until the user manually removes them.

Theming notifications

If you're looking to change the theme of Notifications in general, take a look at Customizing The UI

Styling individual notifications and types

You may need certain notifications or notification types to display differently in order to draw more or less attention to them. This is possible with the use of the cssClassName field on a notification. Setting this field will add the specified class name to the notification HTML making it possible to style the notifications with CSS.

To inspect and target the specific notification HTML, open up a developer console by clicking on the Finsemble icon in the top left corner of the toolbar and select "Central Logger" from the menu. In the bottom left panel, click and open the developer tools by clicking on the Notification Center and/or Notification Toasts components.

Place any targeted css in your theme outlined by the Customizing The UI tutorial.

Additional information: Take a look at Notification Types on how to apply a css class to all notifications belonging to a specific type.

Changing the entire notification

If Theming and Style are not enough to cover your needs, it's possible to create an entirely custom notification card:

Create your custom notification card and/or list row React components. You can create a completely custom notification card or compose a notification card using the Finsemble provided Notification shells components to leverage built-in Finsemble functionality.

// CustomNotificationComponentsExample.tsx
import * as React from "react";
import {
NotificationCardPropType,
NotificationCardHeaderShell,
NotificationCardShell,
NotificationCardHeaderLogo,
NotificationCardBodyShell,
NotificationCardBodyContentLogo,
NotificationListRowShell,
NotificationListRowPropType
} from "@finsemble/finsemble-core";

/**
* A custom Notfication Card
*/
export const CustomNotificationCard: React.FunctionComponent<NotificationCardPropType> = (props) => {
return (
<NotificationCardShell {...props}>
<NotificationCardHeaderShell {...props}>
{/* The Component children should be empty if you want the default Finsemble Notification Header */}
<NotificationCardHeaderLogo headerLogo={props.notification.headerLogo} />
<div className="notification-card__title">Custom</div>
{/* NotificationCardHeaderShell will render the notification context menu */}
</NotificationCardHeaderShell>
<NotificationCardBodyShell {...props}>
{/* The Component children should be empty if you want the default Finsemble Notification Header */}
<NotificationCardBodyContentLogo contentLogo={props.notification.contentLogo} />
<div className="notification-card__body_text">Custom</div>
{/* NotificationCardBodyShell will render the notification Actions */}
</NotificationCardBodyShell>
</NotificationCardShell>
);
};

/**
* A custom Notfication Center List Row
*/
export const CustomNotificationCenterRow: React.FunctionComponent<NotificationListRowPropType> = (props) => {
return (
<NotificationListRowShell {...props}>
<div className="list-row__cell">Custom</div>
<div className="list-row__cell">Row</div>
<div className="list-row__cell">{props.notification.title}</div>
{/* The NotificationListRowShell will render the Notification context menu */}
</NotificationListRowShell>
);
};

Import your custom notification card into the Notification Center and Notification Toasts components:

// finsemble-seed/src/components/notificationsToasts/NotificationsToasts.tsx
import {CustomNotificationCard} from "../notificationsCenter/CustomNotificationComponentsExample";

ReactDOM.render(
<FinsembleProvider>
{ /* Pass in your notification card component*/ }
<NotificationsToasts notificationCard={CustomNotificationCard} />
</FinsembleProvider>,
document.getElementById("notifications-toasts")
);

// finsemble-seed/src/components/notificationsCenter/NotificationsCenter.tsx

import {CustomNotificationCard, CustomNotificationCenterRow} from "./CustomNotificationComponentsExample";

ReactDOM.render(
<FinsembleProvider>
<NotificationsCenter
{ /* Pass in your React components */ }
notificationCard={CustomNotificationCard}
notificationListRow={CustomNotificationCenterRow}
/>
</FinsembleProvider>,
document.getElementById("notifications-center")
);

Multiple notification templates

In some cases you might need to have multiple custom notifications cards. To achieve this, create a notification card selector component.

// finsemble-seed/src/components/notificationsCenter/NotificationCardSelector.tsx
import * as React from "react";
// Import the default Finsemble notification card components
import {
NotificationCardPropType,
NotificationCardShell
} from "@finsemble/finsemble-core";

// Import/Create your custom notification cards components
import {CustomNotificationCard} from "./CustomNotificationComponentsExample";
const RedDiv = () => <div style={{background:"red", height:"130px"}}/>;
const BlueDiv = () => <div style={{background:"blue", height:"130px"}}/>

export const NotificationCardSelector:FunctionComponent<NotificationCardPropType> = (props) => {
const {notification} = props;

// Set the default fallback notification Card
let Card = NotificationCardShell;

// Select your card based on your specific conditions
if(notification.type?.toLowerCase() == "custom") {
Card = CustomNotificationCard;
} else if (notification.type?.toLowerCase() == "red-type") {
Card = RedDiv;
} else if (notification.type?.toLowerCase() == "blue-type") {
Card = BlueDiv;
}

// Render the selected card
return <Card {...props} />
}

Make sure to use the NotificationCardSelector as the new notification card in the notifications center and notifications toasts react components.

// finsemble-seed/src/components/notificationsCenter/NotificationsCenter.tsx

// Import the NotificationCardSelector
import { NotificationCardSelector } from "./NotificationCardSelector";

ReactDOM.render(
<FinsembleProvider>
<NotificationsCenter
{/* Pass it into the NotificationsCenter */}
notificationCard={NotificationCardSelector}
/>
</FinsembleProvider>,
document.getElementById("notifications-center")
);

Based on the conditions in the NotificationCardSelector these notifications all render with different templates.

FSBL.Clients.NotificationClient.notify([
{
source: "Finsemble",
title: "Request for Quote",
details: "RIO - 9409 @ $9409",
type: "red-type", // will match the conditions for the "RedDiv" card
},
{
source: "Finsemble",
title: "Request for Quote",
details: "RIO - 9409 @ $9409",
type: "custom", // will match the conditions for the "CustomNotificationCard" card
},
{
source: "Finsemble",
title: "Request for Quote",
details: "RIO - 9409 @ $9409",
type: "blue-type", // will match the conditions for the "BlueDiv" card
},
{
source: "Finsemble",
title: "Request for Quote",
details: "RIO - 9409 @ $9409",
type: "not-defined", // does not match any of the conditions and will use the default card
}
]
)

Muting notifications

At its core, the purpose of a notification is to draw the user's attention and let them know something has or is about to happen. The most effective way of grabbing attention is with a Notification toast popping up from off-screen. If the information presented in the Toast is not important or relevant, and this unimportant/irrelevant information grabs the user's attention too frequently, it can frequently be distracting and can hinder the user in doing their tasks. There are two ways to control this behaviour. The first, for developers, is via the Notifications Toasts component Filter config, and the second is user-controlled muting.

Notification toasts component config

Toasts are often the main source of distraction/annoyance for a user receiving too many Notifications. It might be the case you wish to send a notification and not have it pop-up and grab the user's attention but still have it available in the Notification Center. In this case, you are able to send a notification with a specific field defined internally within your organisation identifying that this Notification is of low importance and configure the Toasts component not to show this Notification.

See below for an example:

Configure the Notification Toasts component to exclude any notifications that come in with the value meta.userDefinedFieldToSkipToasts set to true. Do this by adding an exclude filter to the NotificationsToasts.window.data.notifications object

// finsemble-seed/configs/application/UIComponents.json

"NotificationsToasts": {
"window": {
...
"data": {
"notifications": {
"applyMuteFilters": true,
"filter": {
"include": [],
"exclude": [{
"meta.userDefinedFieldToSkipToasts": true
}]
},
...
}
},
...
},
...
}

After Restarting Finsemble, sending a notification such as the one below will not toast while still appearing in the Notification Center.

// Run in a Finsemble Developer Console
FSBL.Clients.NotificationClient.notify({
title: "Notify world",
source: "oms",
type: "general",
meta: { userDefinedFieldToSkipToasts: true },
});

User-controlled showing notifications

Users also have some ability to control what notifications are able to grab their attention. You can control which notifications are shown via the Notification context menu.

Muting from the notification context menu

Muting from the notification context menu

After you set the "Don't show" option, any notifications process after that point will have the isMuted value set to true. At this point the Notification UI can then be configured to respect or ignore this isMuted value. This is done changing the component's applyMuteFilters value to true. The default configuration is for the mute filters to only be applied to the toasts component, meaning muted notifications will appear in the Center. Setting this to false would mean disabling user muting.

Set the applyMuteFilters value.

// finsemble-seed/configs/application/UIComponents.json

"NotificationsToasts": {
"window": {
...
"data": {
"notifications": {
"applyMuteFilters": true,
...
}
},
...
},
...
}
note

we do not recommend applying mute filters on the Notification Center

Muting from the Preferences panel

User are also able to mute from the Preferences Panel found in the File Menu > Preferences > Notifications > Notification Sources.

Notification Preferences Panel

If the panel above does not appear in your User Preferences component, add it in by following the step "Add The Notifications Menu to the User Preferences" in Using Notifications with Older Seeds below.

Notification storage and persistence

Persistence

By default, Finsemble stores a maximum of 1000 notifications or 7 days which ever comes first. These directives can be changed on the notifications object on the servicesConfig object in ./configs/application/config.json. See the Config Reference for more information.

note

Notifications are not actively purged. As a new notification comes in, the collection is evaluated for notifications to purge from storage.

Storage

Finsemble notification gets persisted using the finsemble.notifications storage topic. To change the storage adapter Notifications uses, set the appropriate value by adding the finsemble.notifications key to the servicesConfig.storage.topicToDataStoreAdapters object in ./configs/application/config.json in the Finsemble seed:

{
"servicesConfig": {
"storage": {
"topicToDataStoreAdapters": {
"finsemble.notifications": "IndexedDBAdapter"
}
}
}
}

Remote desktop and transparency support

note

Notifications do work over RDP but will need a config update due to the way that transparency appears when running Finsemble on RDP.

In your configs/application/UIComponents.json in the Finsemble Seed, change to the value "[Component Name].window.options.transparent": true to false for the NotificationsToasts and NotificationsCenter components.

Subscription filters

Primitives

A primitive is an object with properties that are matched.

  • {source:"Finsemble"} - primitive that checks that the source field is equal to "Finsemble"

Multiple fields in a primitive are, by default, joined by logical AND. See under Modifiers to change this.

  • {source:"Finsemble",type:"RFQ"} - primitive that checks that the source field is equal to "Finsemble" AND that the type field is equal to RFQ

The name of a field in a primitive is always the name of the field to match in the record. The value can be one of:

  • Basic types: string, number, date - will match directly against the value in the record. Case is ignored in string matches.
  • Array: will match against any one of the values in the array. See below.

Primitives will filter against individual values and against one or more matches in an array. So the filter {field:"value1"} will match against any of the following objects:

  • {field:"value1"}
  • {field:["value1","differentValue"]}
  • {field:["differentValue","value1"]}

Deep filtering

You are not limited to filtering only at the top level. You also can do deep filter on an object of an object using dot-notation. So if you want to match on the object {meta: {customValue: true}} then you can filter:

let filter = { "meta.customValue": true };

The above is a filter primitive that checks that the field "meta" has an object as its value, which in turn has a key "customValue" with a value of true. You can go as deep as you want. The following is a completely valid deep-filter primitive:

let filter = {"meta.customObject.moreData.field":"my value"}

Any modifiers that apply to simple primitives apply to deep fields as well.

Deep filtering arrays

Deep Filtering is not limited to objects embedded in objects. You can have arrays of objects embedded in objects. You even can have arrays of objects embedded in arrays of objects embedded in... (you get the idea!).

Array primitive

If the value of a field in a primitive is an array, then it will accept a match of any one of the array values.

{field:["valueA","valueB"]} // accepts any record where the field matches 'valueA' or 'valueB'

Additionally, if the target record also has an array, it will accept a match if any one of the values in the array of the record matches any one of the values in the array of the filter term.

{field:["valueA","valueB"]}

will match any of these:

{field:"valueA"}
{field:"valueB"}
{field:["valueA","Jim"]}

Using notifications with .Net client

The Finsemble.dll provides API for notification management. The main concept is the same as in JavaScript. WPF and Winforms-based components use NotificationClient to send and receive notifications.

  • Sending an Informational Notification:
    Notification notification = new Notification();
    notification.IssuedAt = DateTime.Now;
    notification.Source = ".Net NotifyComponent";
    notification.HeaderText = "Notification header";
    notification.Title = "Informational Notification";
    notification.Details = "Notification from .Net";
    notification.Type = "email";
    notification.HeaderLogo = "http://localhost:3375/components/finsemble-notifications/components/shared/assets/email.svg";
    notification.ContentLogo = "http://localhost:3375/components/finsemble-notifications/components/shared/assets/graph.png";

    FSBL.NotificationClient.Notify((new[] { notification }), (s, r) => {
    FSBL.Logger.Log(new JToken[] {
    "Notification sent,\nnotification: " + notification.ToString()
    + "\nresponse: " + (r.response != null ? r.response.ToString() : "null")
    + "\nerror: " + (r.error != null ? r.error.ToString() : "null")
    });

    //check response for notification id
    });
  • Sending an Actionable Notification:
    Notification notification = new Notification();
    notification.IssuedAt = DateTime.Now;
    notification.Source = ".Net NotifyComponent";
    notification.HeaderText = "Notification header";
    notification.Title = "Actionable Notification";
    notification.Details = "Notification from .Net";
    notification.Type = "email";
    notification.HeaderLogo = "http://localhost:3375/components/finsemble-notifications/components/shared/assets/email.svg";
    notification.ContentLogo = "http://localhost:3375/components/finsemble-notifications/components/shared/assets/graph.png";

    NAction dismiss = new NAction();
    dismiss.ButtonText = "Dismiss";
    dismiss.Type = ActionTypes.DISMISS;

    NAction snooze = new NAction();
    snooze.ButtonText = "Snooze";
    snooze.Type = ActionTypes.SNOOZE;
    snooze.Milliseconds = 10000;

    NAction welcome = new NAction();
    welcome.ButtonText = "Welcome";
    welcome.Type = ActionTypes.SPAWN;
    welcome.Component = "Welcome Component";

    notification.Actions.Add(snooze);
    notification.Actions.Add(welcome);
    notification.Actions.Add(dismiss);

    FSBL.NotificationClient.Notify((new[] { notification }), (s, r) => {
    FSBL.Logger.Log(new JToken[] {
    "Notification sent,\nnotification: " + notification.ToString()
    + "\nresponse: " + (r.response != null ? r.response.ToString() : "null")
    + "\nerror: " + (r.error != null ? r.error.ToString() : "null")
    });

    //check response for notification id
    });

  • Custom Actions:
    Notification notification = new Notification();
    notification.IssuedAt = DateTime.Now;
    notification.Id = "wpf_notification_456";
    notification.Source = ".Net NotifyComponent";
    notification.Title = "Custom Actions";
    notification.HeaderText = "Notification header";
    notification.Details = "Notification from .Net";
    notification.Type = "chat";
    notification.HeaderLogo = "http://localhost:3375/components/finsemble-notifications/components/shared/assets/chat.svg";
    notification.ContentLogo = "http://localhost:3375/components/finsemble-notifications/components/shared/assets/sheild.png";

    NAction query = new NAction();
    query.ButtonText = "Send Query";
    query.Type = ActionTypes.QUERY;
    query.Channel = "query-channel";
    query.Payload = new JObject();
    query.Payload.Add("hello", new JValue("world"));

    NAction transmit = new NAction();
    transmit.ButtonText = "Send Transmit";
    transmit.Type = ActionTypes.TRANSMIT;
    transmit.Channel = "transmit-channel";
    transmit.Payload = new JObject { };
    transmit.Payload.Add("foo", new JValue("bar"));

    NAction publish = new NAction();
    publish.ButtonText = "Send Publish";
    publish.Type = ActionTypes.PUBLISH;
    publish.Channel = "publish-channel";
    publish.Payload = new JObject { };
    publish.Payload.Add("xyzzy", new JValue("moo"));

    notification.Actions.Add(query);
    notification.Actions.Add(transmit);
    notification.Actions.Add(publish);

    FSBL.NotificationClient.Notify((new[] { notification }), (s, r) => {
    FSBL.Logger.Log(new JToken[] {
    "Notification sent,\nnotification: " + notification.ToString()
    + "\nresponse: " + (r.response != null ? r.response.ToString() : "null")
    + "\nerror: " + (r.error != null ? r.error.ToString() : "null")
    });

    //check response for notification id
    });
  • Subscribe to all notifications:
    Subscription sub = new Subscription();
    sub.Filter = new Filter();
    //sub.filter.include = new Dictionary<String, Object>();
    //sub.filter.include.Add("type", "email");

    EventHandler<FinsembleEventArgs> onSubHandler = (s, r) =>
    {
    FSBL.Logger.Log(new JToken[] {
    "Subscription request sent,\nSubscription: " + sub.ToString()
    + "\nresponse: " + (r.response != null ? r.response.ToString() : "null")
    + "\nerror: " + (r.error != null ? r.error.ToString() : "null")
    });
    if (r.response != null)
    {
    subscription = Subscription.FromJObject((JObject)r.response);
    Application.Current.Dispatcher.Invoke(delegate //main thread
    {
    Unsubscribe.IsEnabled = true;
    Subscribe.IsEnabled = false;
    });
    }
    };

    EventHandler<Notification> onNotifyHandler = (s, r) =>
    {
    FSBL.Logger.Log(new JToken[] {
    "Received Notification,\nnotification: " + r.ToString()
    });

    Application.Current.Dispatcher.Invoke(delegate //main thread
    {
    NotificationData.Text = r.ToString();
    });
    };

    FSBL.NotificationClient.Subscribe(sub, onSubHandler, onNotifyHandler);
  • Unsubscribe:
    EventHandler<FinsembleEventArgs> onUnsubHandler = (s, r) =>
    {
    FSBL.Logger.Log(new JToken[] {
    "Unsubscribe request sent,\nSubscription id: " + subscription.Id
    + "\nresponse: " + (r.response != null ? r.response.ToString() : "null")
    + "\nerror: " + (r.error != null ? r.error.ToString() : "null")
    });

    if (r.error == null) subscription = null;
    Application.Current.Dispatcher.Invoke(delegate //main thread
    {
    Unsubscribe.IsEnabled = false;
    Subscribe.IsEnabled = true;
    });
    };

    FSBL.NotificationClient.Unsubscribe(subscription.Id, onUnsubHandler);