terriajs
Version:
Geospatial data visualization platform.
107 lines (93 loc) • 3.21 kB
text/typescript
import { action, computed, observable, makeObservable } from "mobx";
import { ReactNode } from "react";
import ViewState from "./ViewState";
export interface Notification {
title: string | (() => string);
message: string | ((viewState: ViewState) => ReactNode);
confirmText?: string;
denyText?: string;
confirmAction?: () => void;
denyAction?: () => void;
hideUi?: boolean;
type?: string;
width?: number | string;
height?: number | string;
key?: string;
/** Show notification as a toast instead of as a blocking message */
showAsToast?: boolean;
/**
* Duration in seconds after which the toast is dismissed. If undefined, the
* toast must be explicitly dismissed by the user.
*/
toastVisibleDuration?: number;
/**
* True if notification should not be shown to the user. You can also pass a
* reactive function which will dismiss the message even if it is currently
* being shown to the user. The reactive behaviour is useful for dismissing
* notifications that becomes invalid because some state has changed.
*/
ignore?: boolean | (() => boolean);
/**
* Called when notification is dismissed, this will also be triggered for
* confirm/deny actions
*/
onDismiss?: () => void;
}
/**
* Tracks pending notifications, and provides and interface for adding and removing them.
* Notification queue is first-in, first-out.
* Notifications with the same key will only be added once.
*/
export default class NotificationState {
protected notifications: Notification[] = [];
protected alreadyNotifiedKeys: Set<string> = new Set();
constructor() {
makeObservable(this);
}
addNotificationToQueue(notification: Notification): void {
const alreadyQueued =
this.notifications.filter(
(item) =>
item.title === notification.title &&
item.message === notification.message
).length !== 0;
const keyNotSeenBefore =
notification.key === undefined ||
!this.alreadyNotifiedKeys.has(notification.key);
if (!alreadyQueued && keyNotSeenBefore) {
const ignore =
typeof notification.ignore === "function"
? notification.ignore()
: (notification.ignore ?? false);
if (!ignore) this.notifications.push(notification);
}
if (notification.key !== undefined) {
this.alreadyNotifiedKeys.add(notification.key);
}
}
dismissCurrentNotification(): Notification | undefined {
const removed = this.notifications.shift();
if (removed?.onDismiss) {
removed.onDismiss();
}
// Remove all ignored notifications
// This is needed here as the action of dismissing the current notification may change "ignore" status of notifications in stack
this.notifications = this.notifications.filter(
(n) =>
!(typeof n.ignore === "function" ? n.ignore() : (n.ignore ?? false))
);
return removed;
}
get currentNotification(): Notification | undefined {
return this.notifications.length > 0 ? this.notifications[0] : undefined;
}
/*
* @private - used in specs
*/
getAllNotifications() {
return this.notifications;
}
}