// RECEIVED notification format
//
// message: "",
// title: "" (optional)
// SENT notification format
//
// id: UUID,
// message: "",
// title: "", (optional)
// level: warn|success|information|error
export type Subscriber = {
id: string,
pushHandle: (notification: Notification) => any | undefined
}
type NotificationLevels = "warn" | "success" | "information" | "error"
export type Notification = {
id?: string,
message: string,
title?: string,
level?: NotificationLevels
}
/**
* The NotificationManager is a centralised means to send / receive notifications within an SPA.
*
* The export instantiates a NotificationManager to provide a singleton instance of the manager, so that all subscribers
* and all senders are using the same manager.
*
* The instantiated notification manager can be imported from the standard webapp library, i.e.
*
* ```
* import { notifications } from '@jcu/spark'`
* ```
*
* The `notifications` object can then be used to attach subscribers to receive notifications:
*
* ```
* notifications.subscribe('myNotifID', (notification) => {//Do Something})
* ```
*
* It can also be used to send notifications to any existing subscribers:
*
* ```
* notifications.notify({
* title: 'Email not saved',
* message: 'Your profile settings changed; check your profile to see the updated information.'
* })
* ```
*
* By default, the JCU web app framework comes with a popup notification layer automatically subscribed. Therefore all
* SPAs have capacity for popup notifications to be sent to the user.
*
* @category Services
*/
class NotificationManager {
/**
* List of objects defining the currently subscribed consumers.
*/
subscribers: Subscriber[] = []
/**
* Provides the ability for notification consumers to subscribe to the notification engine. Consumers provide a
* function handle that takes in a Notification object, allowing the consumer to make decisions about how to handle
* the Notification.
*
* @param {string} id Identity of notification consumer to subscribe to notifications
* @param {function} pushHandle Handle to the consumer function that takes in a Notification
*/
subscribe(id: string, pushHandle: (notification: Notification) => any | undefined) {
this.subscribers.push({id, pushHandle})
}
/**
* Unsubscribe a consumer from receiving notifications based on the provided ID.
*
* @param {string} id Identity of notification consumer to unsubscribe
*/
unsubscribe(id: string) {
this.subscribers = this.subscribers.filter((subscriber) => subscriber.id !== id)
}
/**
* Publish a Notification to all current subscribers. Generates UID for each notification for unique identification.
*
* @param {Notification} notification Notification to send out to subscribers
* @param {Object} options Extra notification options, mostly used by subscribers
*/
publishNotification(notification: Notification, options = {}) {
//TODO: Proper UUID generation
notification.id = Math.random().toString(36).substring(2, 8) + Math.random().toString(36).substring(2, 8);
for (let subscriber in this.subscribers) {
// the 'as' notation means it is type checked for DEFINED keys, but won't error on undefined extra keys (e.g. ...options)
this.subscribers[subscriber].pushHandle({...notification, ...options} as Notification)
}
}
/**
* Wrapper function for sending a notification with a level of "warn".
*
* Can take a Notification object or a string. If a string is provided, it is converted into the message portion of a
* standard Notification.
*
* @param {Notification | string} notification Notification / message to send out to subscribers
* @param {Object} options Extra notification options, mostly used by subscribers
*/
warn(notification: Notification | string, options = {}) {
let message, title
if (typeof notification === "string") {
message = notification
} else {
message = notification.message
title = notification.title
}
const newNotification: Notification = {
level: "warn",
message,
title
}
this.publishNotification(newNotification, options)
}
/**
* Wrapper function for sending a notification with a level of "error".
*
* Can take a Notification object or a string. If a string is provided, it is converted into the message portion of a
* standard Notification.
*
* @param {Notification | string} notification Notification / message to send out to subscribers
* @param {Object} options Extra notification options, mostly used by subscribers
*/
error(notification: Notification | string, options = {}) {
let message, title
if (typeof notification === "string") {
message = notification
} else {
message = notification.message
title = notification.title
}
const newNotification: Notification = {
level: "error",
message,
title
}
this.publishNotification(newNotification, options)
}
/**
* Wrapper function for sending a notification with a level of "information".
*
* Can take a Notification object or a string. If a string is provided, it is converted into the message portion of a
* standard Notification.
*
* @param {Notification | string} notification Notification / message to send out to subscribers
* @param {Object} options Extra notification options, mostly used by subscribers
*/
notify(notification: Notification | string, options = {}) {
let message, title
if (typeof notification === "string") {
message = notification
} else {
message = notification.message
title = notification.title
}
const newNotification: Notification = {
level: "information",
message,
title
}
this.publishNotification(newNotification, options)
}
/**
* Wrapper function for sending a notification with a level of "success".
*
* Can take a Notification object or a string. If a string is provided, it is converted into the message portion of a
* standard Notification.
*
* @param {Notification | string} notification Notification / message to send out to subscribers
* @param {Object} options Extra notification options, mostly used by subscribers
*/
success(notification: Notification | string, options = {}) {
let message, title
if (typeof notification === "string") {
message = notification
} else {
message = notification.message
title = notification.title
}
const newNotification: Notification = {
level: "success",
message,
title
}
this.publishNotification(newNotification, options)
}
}
// Export singleton instantiation of NotificationManager so that all importers get the same manager.
export let notifications = new NotificationManager()
Source