mirror of
https://github.com/nasa/openmct.git
synced 2024-12-19 05:07:52 +00:00
Vue status bar (#2188)
* Implemented indicators * WIP * Fixed templates from notifications example * Message bar implemented * Implemented notifications * Fixed bug with destruction of notifications * Renamed MessageBanner to NotificationBanner * Add save success message * Removed NotificationServiceSpec * Removed legacy constants from bundle
This commit is contained in:
parent
88bcb6078e
commit
64b9d4c24a
@ -26,12 +26,16 @@ define([
|
||||
"./src/NotificationLaunchController",
|
||||
"./src/DialogLaunchIndicator",
|
||||
"./src/NotificationLaunchIndicator",
|
||||
"./res/dialog-launch.html",
|
||||
"./res/notification-launch.html",
|
||||
'legacyRegistry'
|
||||
], function (
|
||||
DialogLaunchController,
|
||||
NotificationLaunchController,
|
||||
DialogLaunchIndicator,
|
||||
NotificationLaunchIndicator,
|
||||
DialogLaunch,
|
||||
NotificationLaunch,
|
||||
legacyRegistry
|
||||
) {
|
||||
"use strict";
|
||||
@ -41,11 +45,11 @@ define([
|
||||
"templates": [
|
||||
{
|
||||
"key": "dialogLaunchTemplate",
|
||||
"templateUrl": "dialog-launch.html"
|
||||
"template": DialogLaunch
|
||||
},
|
||||
{
|
||||
"key": "notificationLaunchTemplate",
|
||||
"templateUrl": "notification-launch.html"
|
||||
"template": NotificationLaunch
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
|
@ -51,76 +51,26 @@ define(
|
||||
return actionTexts[Math.floor(Math.random()*3)];
|
||||
}
|
||||
|
||||
function getExampleActions() {
|
||||
var actions = [
|
||||
{
|
||||
label: "Try Again",
|
||||
callback: function () {
|
||||
$log.debug("Try Again pressed");
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Remove",
|
||||
callback: function () {
|
||||
$log.debug("Remove pressed");
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Cancel",
|
||||
callback: function () {
|
||||
$log.debug("Cancel pressed");
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// Randomly remove some actions off the top; leave at least one
|
||||
actions.splice(0,Math.floor(Math.random() * actions.length));
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
function getExampleSeverity() {
|
||||
var severities = [
|
||||
"info",
|
||||
"alert",
|
||||
"error"
|
||||
];
|
||||
return severities[Math.floor(Math.random() * severities.length)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch a new notification with a severity level of 'Error'.
|
||||
*/
|
||||
$scope.newError = function(){
|
||||
|
||||
$scope.newError = function () {
|
||||
notificationService.notify({
|
||||
title: "Example error notification " + messageCounter++,
|
||||
hint: "An error has occurred",
|
||||
severity: "error",
|
||||
primaryOption: {
|
||||
label: 'Retry',
|
||||
callback: function() {
|
||||
$log.info('Retry clicked');
|
||||
}
|
||||
},
|
||||
options: getExampleActions()});
|
||||
severity: "error"
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Launch a new notification with a severity of 'Alert'.
|
||||
*/
|
||||
$scope.newAlert = function(){
|
||||
|
||||
$scope.newAlert = function () {
|
||||
notificationService.notify({
|
||||
title: "Alert notification " + (messageCounter++),
|
||||
hint: "This is an alert message",
|
||||
severity: "alert",
|
||||
primaryOption: {
|
||||
label: 'Retry',
|
||||
callback: function() {
|
||||
$log.info('Retry clicked');
|
||||
}
|
||||
},
|
||||
options: getExampleActions()});
|
||||
autoDismiss: true
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -128,39 +78,38 @@ define(
|
||||
* Launch a new notification with a progress bar that is updated
|
||||
* periodically, tracking an ongoing process.
|
||||
*/
|
||||
$scope.newProgress = function(){
|
||||
|
||||
$scope.newProgress = function () {
|
||||
var notificationModel = {
|
||||
title: "Progress notification example",
|
||||
severity: "info",
|
||||
progress: 0,
|
||||
actionText: getExampleActionText(),
|
||||
unknownProgress: false
|
||||
actionText: getExampleActionText()
|
||||
};
|
||||
|
||||
/**
|
||||
* Simulate an ongoing process and update the progress bar.
|
||||
* @param notification
|
||||
*/
|
||||
function incrementProgress(notificationModel) {
|
||||
function incrementProgress() {
|
||||
notificationModel.progress = Math.min(100, Math.floor(notificationModel.progress + Math.random() * 30));
|
||||
notificationModel.progressText = ["Estimated time" +
|
||||
" remaining:" +
|
||||
" about ", 60 - Math.floor((notificationModel.progress / 100) * 60), " seconds"].join(" ");
|
||||
if (notificationModel.progress < 100) {
|
||||
$timeout(function(){incrementProgress(notificationModel);}, 1000);
|
||||
$timeout(function () {
|
||||
incrementProgress(notificationModel);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
notificationService.notify(notificationModel);
|
||||
incrementProgress(notificationModel);
|
||||
incrementProgress();
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch a new notification with severity level of INFO.
|
||||
*/
|
||||
$scope.newInfo = function(){
|
||||
|
||||
$scope.newInfo = function () {
|
||||
notificationService.info({
|
||||
title: "Example Info notification " + messageCounter++
|
||||
});
|
||||
|
@ -60,7 +60,7 @@ define(
|
||||
};
|
||||
//If the notification is dismissed by the user, close
|
||||
// the dialog.
|
||||
notification.onDismiss(function () {
|
||||
notification.on('dismiss', function () {
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
|
@ -23,33 +23,17 @@
|
||||
define([
|
||||
"./src/NotificationIndicatorController",
|
||||
"./src/NotificationIndicator",
|
||||
"./src/NotificationService",
|
||||
"./res/notification-indicator.html",
|
||||
'legacyRegistry'
|
||||
], function (
|
||||
NotificationIndicatorController,
|
||||
NotificationIndicator,
|
||||
NotificationService,
|
||||
notificationIndicatorTemplate,
|
||||
legacyRegistry
|
||||
) {
|
||||
|
||||
legacyRegistry.register("platform/commonUI/notification", {
|
||||
"extensions": {
|
||||
"constants": [
|
||||
{
|
||||
"key": "DEFAULT_AUTO_DISMISS",
|
||||
"value": 3000
|
||||
},
|
||||
{
|
||||
"key": "FORCE_AUTO_DISMISS",
|
||||
"value": 1000
|
||||
},
|
||||
{
|
||||
"key": "MINIMIZE_TIMEOUT",
|
||||
"value": 300
|
||||
}
|
||||
],
|
||||
"templates": [
|
||||
{
|
||||
"key": "notificationIndicatorTemplate",
|
||||
@ -76,12 +60,11 @@ define([
|
||||
"services": [
|
||||
{
|
||||
"key": "notificationService",
|
||||
"implementation": NotificationService,
|
||||
"implementation": function (openmct) {
|
||||
return openmct.notifications;
|
||||
},
|
||||
"depends": [
|
||||
"$timeout",
|
||||
"topic",
|
||||
"DEFAULT_AUTO_DISMISS",
|
||||
"MINIMIZE_TIMEOUT"
|
||||
"openmct"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@ -1,437 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* This bundle implements the notification service, which can be used to
|
||||
* show banner notifications to the user. Banner notifications
|
||||
* are used to inform users of events in a non-intrusive way. As
|
||||
* much as possible, notifications share a model with blocking
|
||||
* dialogs so that the same information can be provided in a dialog
|
||||
* and then minimized to a banner notification if needed.
|
||||
*
|
||||
* @namespace platform/commonUI/notification
|
||||
*/
|
||||
define(
|
||||
['moment'],
|
||||
function (moment) {
|
||||
|
||||
/**
|
||||
* A representation of a user action. Options are provided to
|
||||
* dialogs and notifications and are shown as buttons.
|
||||
*
|
||||
* @typedef {object} NotificationOption
|
||||
* @property {string} label the label to appear on the button for
|
||||
* this action
|
||||
* @property {function} callback a callback function to be invoked
|
||||
* when the button is clicked
|
||||
*/
|
||||
|
||||
/**
|
||||
* A representation of a banner notification. Banner notifications
|
||||
* are used to inform users of events in a non-intrusive way. As
|
||||
* much as possible, notifications share a model with blocking
|
||||
* dialogs so that the same information can be provided in a dialog
|
||||
* and then minimized to a banner notification if needed, or vice-versa.
|
||||
*
|
||||
* @typedef {object} NotificationModel
|
||||
* @property {string} title The title of the message
|
||||
* @property {string} severity The importance of the message (one of
|
||||
* 'info', 'alert', or 'error' where info < alert <error)
|
||||
* @property {number} [progress] The completion status of a task
|
||||
* represented numerically
|
||||
* @property {boolean} [unknownProgress] a boolean indicating that the
|
||||
* progress of the underlying task is unknown. This will result in a
|
||||
* visually distinct progress bar.
|
||||
* @property {boolean} [autoDismiss] If truthy, dialog will
|
||||
* be automatically minimized or dismissed (depending on severity).
|
||||
* Additionally, if the provided value is a number, it will be used
|
||||
* as the delay period before being dismissed.
|
||||
* @property {boolean} [dismissable=true] If true, notification will
|
||||
* include an option to dismiss it completely.
|
||||
* @property {NotificationOption} [primaryOption] the default user
|
||||
* response to
|
||||
* this message. Will be represented as a button with the provided
|
||||
* label and action. May be used by banner notifications to display
|
||||
* only the most important option to users.
|
||||
* @property {NotificationOption[]} [options] any additional
|
||||
* actions the user can take. Will be represented as additional buttons
|
||||
* that may or may not be available from a banner.
|
||||
* @see DialogModel
|
||||
*/
|
||||
|
||||
/**
|
||||
* A wrapper object that is returned as a handle to a newly created
|
||||
* notification. Wraps the notifications model and decorates with
|
||||
* functions to dismiss or minimize the notification.
|
||||
*
|
||||
* @typedef {object} Notification
|
||||
* @property {function} dismiss This method is added to the object
|
||||
* returned by {@link NotificationService#notify} and can be used to
|
||||
* dismiss this notification. Dismissing a notification will remove
|
||||
* it completely and it will not appear in the notification indicator
|
||||
* @property {function} minimize This method is added to the object
|
||||
* returned by {@link NotificationService#notify} and can be used to
|
||||
* minimize this notification. Minimizing a notification will send
|
||||
* it to the notification indicator
|
||||
* @property {function} dismissOrMinimize This method is added to the
|
||||
* object returned by {@link NotificationService#notify}. It will
|
||||
* hide the notification by either dismissing or minimizing it,
|
||||
* depending on severity. Typically this is the method that should
|
||||
* be used for dismissing a notification. If more control is
|
||||
* required, then the minimize or dismiss functions can be called
|
||||
* individually.
|
||||
* @property {function} onDismiss Allows listening for on dismiss
|
||||
* events. This allows cleanup etc. when the notification is dismissed.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The notification service is responsible for informing the user of
|
||||
* events via the use of banner notifications.
|
||||
* @memberof platform/commonUI/notification
|
||||
* @constructor
|
||||
* @param $timeout the Angular $timeout service
|
||||
* @param defaultAutoDismissTimeout The period of time that an
|
||||
* auto-dismissed message will be displayed for.
|
||||
* @param minimizeAnimationTimeout When notifications are minimized, a brief
|
||||
* animation is shown. This animation requires some time to execute,
|
||||
* so a timeout is required before the notification is hidden
|
||||
*/
|
||||
function NotificationService($timeout, topic, defaultAutoDismissTimeout, minimizeAnimationTimeout) {
|
||||
this.notifications = [];
|
||||
this.$timeout = $timeout;
|
||||
this.highest = { severity: "info" };
|
||||
this.AUTO_DISMISS_TIMEOUT = defaultAutoDismissTimeout;
|
||||
this.MINIMIZE_ANIMATION_TIMEOUT = minimizeAnimationTimeout;
|
||||
this.topic = topic;
|
||||
|
||||
/*
|
||||
* A context in which to hold the active notification and a
|
||||
* handle to its timeout.
|
||||
*/
|
||||
this.active = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimize a notification. The notification will still be available
|
||||
* from the notification list. Typically notifications with a
|
||||
* severity of 'info' should not be minimized, but rather
|
||||
* dismissed. If you're not sure which is appropriate,
|
||||
* use {@link Notification#dismissOrMinimize}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.minimize = function (service, notification) {
|
||||
//Check this is a known notification
|
||||
var index = service.notifications.indexOf(notification);
|
||||
|
||||
if (service.active.timeout) {
|
||||
/*
|
||||
Method can be called manually (clicking dismiss) or
|
||||
automatically from an auto-timeout. this.active.timeout
|
||||
acts as a semaphore to prevent race conditions. Cancel any
|
||||
timeout in progress (for the case where a manual dismiss
|
||||
has shortcut an active auto-dismiss), and clear the
|
||||
semaphore.
|
||||
*/
|
||||
service.$timeout.cancel(service.active.timeout);
|
||||
delete service.active.timeout;
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
notification.model.minimized = true;
|
||||
//Add a brief timeout before showing the next notification
|
||||
// in order to allow the minimize animation to run through.
|
||||
service.$timeout(function () {
|
||||
service.setActiveNotification(service.selectNextNotification());
|
||||
}, service.MINIMIZE_ANIMATION_TIMEOUT);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Completely removes a notification. This will dismiss it from the
|
||||
* message banner and remove it from the list of notifications.
|
||||
* Typically only notifications with a severity of info should be
|
||||
* dismissed. If you're not sure whether to dismiss or minimize a
|
||||
* notification, use {@link Notification#dismissOrMinimize}.
|
||||
* dismiss
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.dismiss = function (service, notification) {
|
||||
//Check this is a known notification
|
||||
var index = service.notifications.indexOf(notification);
|
||||
|
||||
if (service.active.timeout) {
|
||||
/* Method can be called manually (clicking dismiss) or
|
||||
* automatically from an auto-timeout. this.active.timeout
|
||||
* acts as a semaphore to prevent race conditions. Cancel any
|
||||
* timeout in progress (for the case where a manual dismiss
|
||||
* has shortcut an active auto-dismiss), and clear the
|
||||
* semaphore.
|
||||
*/
|
||||
|
||||
service.$timeout.cancel(service.active.timeout);
|
||||
delete service.active.timeout;
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
service.notifications.splice(index, 1);
|
||||
}
|
||||
service.setActiveNotification(service.selectNextNotification());
|
||||
|
||||
this.setHighestSeverity();
|
||||
};
|
||||
|
||||
/**
|
||||
* Depending on the severity of the notification will selectively
|
||||
* dismiss or minimize where appropriate.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.dismissOrMinimize = function (notification) {
|
||||
var model = notification.model;
|
||||
if (model.severity === "info") {
|
||||
if (model.autoDismiss === false) {
|
||||
notification.minimize();
|
||||
} else {
|
||||
notification.dismiss();
|
||||
}
|
||||
} else {
|
||||
notification.minimize();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the notification that is currently visible in the banner area
|
||||
* @returns {Notification}
|
||||
*/
|
||||
NotificationService.prototype.getActiveNotification = function () {
|
||||
return this.active.notification;
|
||||
};
|
||||
|
||||
/**
|
||||
* A convenience method for info notifications. Notifications
|
||||
* created via this method will be auto-dismissed after a default
|
||||
* wait period unless explicitly forbidden by the caller through
|
||||
* the {autoDismiss} property on the {NotificationModel}, in which
|
||||
* case the notification will be minimized after the wait.
|
||||
* @param {NotificationModel | string} message either a string for
|
||||
* the title of the notification message, or a {@link NotificationModel}
|
||||
* defining the options notification to display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to dismiss or minimize
|
||||
*/
|
||||
NotificationService.prototype.info = function (message) {
|
||||
var notificationModel = typeof message === "string" ? {title: message} : message;
|
||||
notificationModel.severity = "info";
|
||||
return this.notify(notificationModel);
|
||||
};
|
||||
|
||||
/**
|
||||
* A convenience method for alert notifications. Notifications
|
||||
* created via this method will will have severity of "alert" enforced
|
||||
* @param {NotificationModel | string} message either a string for
|
||||
* the title of the alert message with default options, or a
|
||||
* {@link NotificationModel} defining the options notification to
|
||||
* display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to dismiss or minimize
|
||||
*/
|
||||
NotificationService.prototype.alert = function (message) {
|
||||
var notificationModel = typeof message === "string" ? {title: message} : message;
|
||||
notificationModel.severity = "alert";
|
||||
return this.notify(notificationModel);
|
||||
};
|
||||
|
||||
/**
|
||||
* A convenience method for error notifications. Notifications
|
||||
* created via this method will will have severity of "error" enforced
|
||||
* @param {NotificationModel | string} message either a string for
|
||||
* the title of the error message with default options, or a
|
||||
* {@link NotificationModel} defining the options notification to
|
||||
* display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to dismiss or minimize
|
||||
*/
|
||||
NotificationService.prototype.error = function (message) {
|
||||
var notificationModel = typeof message === "string" ? {title: message} : message;
|
||||
notificationModel.severity = "error";
|
||||
return this.notify(notificationModel);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.setHighestSeverity = function () {
|
||||
var severity = {
|
||||
"info": 1,
|
||||
"alert": 2,
|
||||
"error": 3
|
||||
};
|
||||
this.highest.severity = this.notifications.reduce(function (previous, notification) {
|
||||
if (severity[notification.model.severity] > severity[previous]) {
|
||||
return notification.model.severity;
|
||||
} else {
|
||||
return previous;
|
||||
}
|
||||
}, "info");
|
||||
};
|
||||
|
||||
/**
|
||||
* Notifies the user of an event. If there is a banner notification
|
||||
* already active, then it will be dismissed or minimized automatically,
|
||||
* and the provided notification displayed in its place.
|
||||
*
|
||||
* @param {NotificationModel} notificationModel The notification to
|
||||
* display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to {@link Notification#dismiss} or {@link Notification#minimize}
|
||||
*/
|
||||
NotificationService.prototype.notify = function (notificationModel) {
|
||||
var self = this,
|
||||
notification,
|
||||
activeNotification = self.active.notification,
|
||||
topic = this.topic();
|
||||
|
||||
notificationModel.severity = notificationModel.severity || "info";
|
||||
notificationModel.timestamp = moment.utc().format('YYYY-MM-DD hh:mm:ss.ms');
|
||||
|
||||
notification = {
|
||||
model: notificationModel,
|
||||
|
||||
minimize: function () {
|
||||
self.minimize(self, notification);
|
||||
},
|
||||
|
||||
dismiss: function () {
|
||||
self.dismiss(self, notification);
|
||||
topic.notify();
|
||||
},
|
||||
|
||||
dismissOrMinimize: function () {
|
||||
self.dismissOrMinimize(notification);
|
||||
},
|
||||
|
||||
onDismiss: function (callback) {
|
||||
topic.listen(callback);
|
||||
}
|
||||
};
|
||||
|
||||
//Notifications support a 'dismissable' attribute. This is a
|
||||
// convenience to support adding a 'dismiss' option to the
|
||||
// notification for the common case of dismissing a
|
||||
// notification. Could also be done manually by specifying an
|
||||
// option on the model
|
||||
if (notificationModel.dismissable !== false) {
|
||||
notificationModel.options = notificationModel.options || [];
|
||||
notificationModel.options.unshift({
|
||||
label: "Dismiss",
|
||||
callback: function () {
|
||||
notification.dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.notifications.push(notification);
|
||||
|
||||
this.setHighestSeverity();
|
||||
|
||||
/*
|
||||
Check if there is already an active (ie. visible) notification
|
||||
*/
|
||||
if (!this.active.notification) {
|
||||
this.setActiveNotification(notification);
|
||||
|
||||
} else if (!this.active.timeout) {
|
||||
/*
|
||||
If there is already an active notification, time it out. If it's
|
||||
already got a timeout in progress (either because it has had
|
||||
timeout forced because of a queue of messages, or it had an
|
||||
autodismiss specified), leave it to run. Otherwise force a
|
||||
timeout.
|
||||
|
||||
This notification has been added to queue and will be
|
||||
serviced as soon as possible.
|
||||
*/
|
||||
this.active.timeout = this.$timeout(function () {
|
||||
activeNotification.dismissOrMinimize();
|
||||
}, this.AUTO_DISMISS_TIMEOUT);
|
||||
}
|
||||
|
||||
return notification;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used internally by the NotificationService
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.setActiveNotification = function (notification) {
|
||||
var shouldAutoDismiss;
|
||||
this.active.notification = notification;
|
||||
|
||||
if (!notification) {
|
||||
delete this.active.timeout;
|
||||
return;
|
||||
}
|
||||
|
||||
if (notification.model.severity === "info") {
|
||||
shouldAutoDismiss = true;
|
||||
} else {
|
||||
shouldAutoDismiss = notification.model.autoDismiss;
|
||||
}
|
||||
|
||||
if (shouldAutoDismiss || this.selectNextNotification()) {
|
||||
this.active.timeout = this.$timeout(function () {
|
||||
notification.dismissOrMinimize();
|
||||
}, this.AUTO_DISMISS_TIMEOUT);
|
||||
} else {
|
||||
delete this.active.timeout;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Used internally by the NotificationService
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.selectNextNotification = function () {
|
||||
var notification,
|
||||
i = 0;
|
||||
|
||||
/*
|
||||
Loop through the notifications queue and find the first one that
|
||||
has not already been minimized (manually or otherwise).
|
||||
*/
|
||||
for (; i < this.notifications.length; i++) {
|
||||
notification = this.notifications[i];
|
||||
|
||||
if (!notification.model.minimized &&
|
||||
notification !== this.active.notification) {
|
||||
|
||||
return notification;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return NotificationService;
|
||||
}
|
||||
);
|
@ -1,275 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/*global describe,it,expect,beforeEach,jasmine*/
|
||||
|
||||
define(
|
||||
['../src/NotificationService'],
|
||||
function (NotificationService) {
|
||||
|
||||
describe("The notification service ", function () {
|
||||
var notificationService,
|
||||
mockTimeout,
|
||||
mockAutoDismiss,
|
||||
mockMinimizeTimeout,
|
||||
mockTopicFunction,
|
||||
mockTopicObject,
|
||||
infoModel,
|
||||
alertModel,
|
||||
errorModel;
|
||||
|
||||
function elapseTimeout() {
|
||||
mockTimeout.calls.mostRecent().args[0]();
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockTimeout = jasmine.createSpy("$timeout");
|
||||
mockTopicFunction = jasmine.createSpy("topic");
|
||||
mockTopicObject = jasmine.createSpyObj("topicObject", ["listen", "notify"]);
|
||||
mockTopicFunction.and.returnValue(mockTopicObject);
|
||||
|
||||
mockAutoDismiss = mockMinimizeTimeout = 1000;
|
||||
notificationService = new NotificationService(mockTimeout, mockTopicFunction, mockAutoDismiss, mockMinimizeTimeout);
|
||||
|
||||
infoModel = {
|
||||
title: "Mock Info Notification",
|
||||
severity: "info"
|
||||
};
|
||||
|
||||
alertModel = {
|
||||
title: "Mock Alert Notification",
|
||||
severity: "alert"
|
||||
};
|
||||
|
||||
errorModel = {
|
||||
title: "Mock Error Notification",
|
||||
severity: "error"
|
||||
};
|
||||
});
|
||||
|
||||
it("notifies listeners on dismissal of notification", function () {
|
||||
var dismissListener = jasmine.createSpy("ondismiss");
|
||||
var notification = notificationService.notify(infoModel);
|
||||
notification.onDismiss(dismissListener);
|
||||
expect(mockTopicObject.listen).toHaveBeenCalled();
|
||||
notification.dismiss();
|
||||
expect(mockTopicObject.notify).toHaveBeenCalled();
|
||||
mockTopicObject.listen.calls.mostRecent().args[0]();
|
||||
expect(dismissListener).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("dismisses a notification when the notification's dismiss method is used", function () {
|
||||
var notification = notificationService.info(infoModel);
|
||||
notification.dismiss();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(0);
|
||||
});
|
||||
|
||||
it("minimizes a notification when the notification's minimize method is used", function () {
|
||||
var notification = notificationService.info(infoModel);
|
||||
notification.minimize();
|
||||
elapseTimeout(); // needed for the minimize animation timeout
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
describe("when receiving info notifications", function () {
|
||||
it("minimizes info notifications if the caller disables auto-dismiss", function () {
|
||||
infoModel.autoDismiss = false;
|
||||
var notification = notificationService.info(infoModel);
|
||||
elapseTimeout();
|
||||
// 2nd elapse for the minimize animation timeout
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("dismisses info notifications if the caller ignores auto-dismiss", function () {
|
||||
notificationService.info(infoModel);
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(0);
|
||||
});
|
||||
|
||||
it("dismisses info notifications if the caller requests auto-dismiss", function () {
|
||||
infoModel.autoDismiss = true;
|
||||
notificationService.info(infoModel);
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when receiving alert notifications", function () {
|
||||
it("minimizes alert notifications if the caller enables auto-dismiss", function () {
|
||||
alertModel.autoDismiss = true;
|
||||
var notification = notificationService.alert(alertModel);
|
||||
elapseTimeout();
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("keeps alert notifications active if the caller disables auto-dismiss", function () {
|
||||
mockTimeout.and.callFake(function (callback, time) {
|
||||
callback();
|
||||
});
|
||||
alertModel.autoDismiss = false;
|
||||
var notification = notificationService.alert(alertModel);
|
||||
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("keeps alert notifications active if the caller ignores auto-dismiss", function () {
|
||||
mockTimeout.and.callFake(function (callback, time) {
|
||||
callback();
|
||||
});
|
||||
var notification = notificationService.alert(alertModel);
|
||||
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when receiving error notifications", function () {
|
||||
it("minimizes error notifications if the caller enables auto-dismiss", function () {
|
||||
errorModel.autoDismiss = true;
|
||||
var notification = notificationService.error(errorModel);
|
||||
elapseTimeout();
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("keeps error notifications active if the caller disables auto-dismiss", function () {
|
||||
mockTimeout.and.callFake(function (callback, time) {
|
||||
callback();
|
||||
});
|
||||
errorModel.autoDismiss = false;
|
||||
var notification = notificationService.error(errorModel);
|
||||
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("keeps error notifications active if the caller ignores auto-dismiss", function () {
|
||||
mockTimeout.and.callFake(function (callback, time) {
|
||||
callback();
|
||||
});
|
||||
var notification = notificationService.error(errorModel);
|
||||
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when called with multiple notifications", function () {
|
||||
it("auto-dismisses the previously active notification, making the new notification active", function () {
|
||||
var activeNotification;
|
||||
infoModel.autoDismiss = false;
|
||||
//First pre-load with a info message
|
||||
notificationService.notify(infoModel);
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
//Initially expect the active notification to be info
|
||||
expect(activeNotification.model).toBe(infoModel);
|
||||
//Then notify of an error
|
||||
notificationService.notify(errorModel);
|
||||
//But it should be auto-dismissed and replaced with the
|
||||
// error notification
|
||||
elapseTimeout();
|
||||
//Two timeouts, one is to force minimization after
|
||||
// displaying the message for a minimum period, the
|
||||
// second is to allow minimization animation to take place.
|
||||
elapseTimeout();
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(errorModel);
|
||||
});
|
||||
|
||||
it("auto-minimizes an active error notification", function () {
|
||||
var activeNotification;
|
||||
//First pre-load with an error message
|
||||
notificationService.notify(errorModel);
|
||||
//Then notify of info
|
||||
notificationService.notify(infoModel);
|
||||
expect(notificationService.notifications.length).toEqual(2);
|
||||
//Mock the auto-minimize
|
||||
elapseTimeout();
|
||||
//Two timeouts, one is to force minimization after
|
||||
// displaying the message for a minimum period, the
|
||||
// second is to allow minimization animation to take place.
|
||||
elapseTimeout();
|
||||
//Previous error message should be minimized, not
|
||||
// dismissed
|
||||
expect(notificationService.notifications.length).toEqual(2);
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(infoModel);
|
||||
expect(errorModel.minimized).toEqual(true);
|
||||
});
|
||||
|
||||
it("auto-minimizes errors when a number of them arrive in short succession", function () {
|
||||
var activeNotification,
|
||||
error2 = {
|
||||
title: "Second Mock Error Notification",
|
||||
severity: "error"
|
||||
},
|
||||
error3 = {
|
||||
title: "Third Mock Error Notification",
|
||||
severity: "error"
|
||||
};
|
||||
|
||||
//First pre-load with a info message
|
||||
notificationService.notify(errorModel);
|
||||
//Then notify of a third error
|
||||
notificationService.notify(error2);
|
||||
notificationService.notify(error3);
|
||||
expect(notificationService.notifications.length).toEqual(3);
|
||||
//Mock the auto-minimize
|
||||
elapseTimeout();
|
||||
//Two timeouts, one is to force minimization after
|
||||
// displaying the message for a minimum period, the
|
||||
// second is to allow minimization animation to take place.
|
||||
elapseTimeout();
|
||||
//Previous error message should be minimized, not
|
||||
// dismissed
|
||||
expect(notificationService.notifications.length).toEqual(3);
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(error2);
|
||||
expect(errorModel.minimized).toEqual(true);
|
||||
|
||||
//Mock the second auto-minimize
|
||||
elapseTimeout();
|
||||
//Two timeouts, one is to force minimization after
|
||||
// displaying the message for a minimum period, the
|
||||
// second is to allow minimization animation to take place.
|
||||
elapseTimeout();
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(error3);
|
||||
expect(error2.minimized).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -223,6 +223,8 @@ define([
|
||||
*/
|
||||
this.indicators = new api.IndicatorAPI(this);
|
||||
|
||||
this.notifications = new api.NotificationAPI();
|
||||
|
||||
this.Dialog = api.Dialog;
|
||||
|
||||
this.editor = new api.EditorAPI.default(this);
|
||||
|
@ -29,6 +29,7 @@ define([
|
||||
'./ui/GestureAPI',
|
||||
'./telemetry/TelemetryAPI',
|
||||
'./indicators/IndicatorAPI',
|
||||
'./notifications/NotificationAPI',
|
||||
'./Editor'
|
||||
|
||||
], function (
|
||||
@ -40,6 +41,7 @@ define([
|
||||
GestureAPI,
|
||||
TelemetryAPI,
|
||||
IndicatorAPI,
|
||||
NotificationAPI,
|
||||
EditorAPI
|
||||
) {
|
||||
return {
|
||||
@ -51,6 +53,7 @@ define([
|
||||
GestureAPI: GestureAPI,
|
||||
TelemetryAPI: TelemetryAPI,
|
||||
IndicatorAPI: IndicatorAPI,
|
||||
NotificationAPI: NotificationAPI.default,
|
||||
EditorAPI: EditorAPI
|
||||
};
|
||||
});
|
||||
|
@ -28,7 +28,7 @@ define([
|
||||
) {
|
||||
function IndicatorAPI(openmct) {
|
||||
this.openmct = openmct;
|
||||
this.indicatorElements = [];
|
||||
this.indicatorObjects = [];
|
||||
}
|
||||
|
||||
IndicatorAPI.prototype.simpleIndicator = function () {
|
||||
@ -55,12 +55,7 @@ define([
|
||||
*
|
||||
*/
|
||||
IndicatorAPI.prototype.add = function (indicator) {
|
||||
// So that we can consistently position indicator elements,
|
||||
// guarantee that they are wrapped in an element we control
|
||||
var wrapperNode = document.createElement('div');
|
||||
wrapperNode.className = 'h-indicator';
|
||||
wrapperNode.appendChild(indicator.element);
|
||||
this.indicatorElements.push(wrapperNode);
|
||||
this.indicatorObjects.push(indicator);
|
||||
};
|
||||
|
||||
return IndicatorAPI;
|
||||
|
48
src/api/notifications/MCTNotification.js
Normal file
48
src/api/notifications/MCTNotification.js
Normal file
@ -0,0 +1,48 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import EventEmitter from 'EventEmitter';
|
||||
export default class MCTNotification extends EventEmitter {
|
||||
|
||||
constructor(notificationModel, notificationAPI) {
|
||||
super();
|
||||
this.notifications = notificationAPI;
|
||||
this.model = notificationModel;
|
||||
this.initializeModel();
|
||||
}
|
||||
|
||||
minimize() {
|
||||
this.notifications.minimize(this);
|
||||
}
|
||||
|
||||
dismiss() {
|
||||
this.notifications.dismiss(this)
|
||||
}
|
||||
|
||||
dismissOrMinimize() {
|
||||
this.notifications.dismissOrMinimize(this);
|
||||
}
|
||||
|
||||
initializeModel() {
|
||||
this.model.minimized = this.model.minimized || false;
|
||||
}
|
||||
}
|
353
src/api/notifications/NotificationApi.js
Normal file
353
src/api/notifications/NotificationApi.js
Normal file
@ -0,0 +1,353 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* This bundle implements the notification service, which can be used to
|
||||
* show banner notifications to the user. Banner notifications
|
||||
* are used to inform users of events in a non-intrusive way. As
|
||||
* much as possible, notifications share a model with blocking
|
||||
* dialogs so that the same information can be provided in a dialog
|
||||
* and then minimized to a banner notification if needed.
|
||||
*
|
||||
* @namespace platform/api/notifications
|
||||
*/
|
||||
import moment from 'moment';
|
||||
import EventEmitter from 'EventEmitter';
|
||||
import MCTNotification from './MCTNotification.js';
|
||||
|
||||
/**
|
||||
* A representation of a banner notification. Banner notifications
|
||||
* are used to inform users of events in a non-intrusive way. As
|
||||
* much as possible, notifications share a model with blocking
|
||||
* dialogs so that the same information can be provided in a dialog
|
||||
* and then minimized to a banner notification if needed, or vice-versa.
|
||||
*
|
||||
* @typedef {object} NotificationModel
|
||||
* @property {string} title The title of the message
|
||||
* @property {string} severity The importance of the message (one of
|
||||
* 'info', 'alert', or 'error' where info < alert <error)
|
||||
* @property {number} [progress] The completion status of a task
|
||||
* represented numerically
|
||||
* @property {boolean} [unknownProgress] a boolean indicating that the
|
||||
* progress of the underlying task is unknown. This will result in a
|
||||
* visually distinct progress bar.
|
||||
* @property {boolean} [autoDismiss] If truthy, dialog will
|
||||
* be automatically minimized or dismissed (depending on severity).
|
||||
* Additionally, if the provided value is a number, it will be used
|
||||
* as the delay period before being dismissed.
|
||||
* @property {boolean} [dismissable=true] If true, notification will
|
||||
* include an option to dismiss it completely.
|
||||
* @see DialogModel
|
||||
*/
|
||||
|
||||
const DEFAULT_AUTO_DISMISS_TIMEOUT = 3000;
|
||||
const MINIMIZE_ANIMATION_TIMEOUT = 300;
|
||||
|
||||
/**
|
||||
* The notification service is responsible for informing the user of
|
||||
* events via the use of banner notifications.
|
||||
* @memberof platform/commonUI/notification
|
||||
* @constructor
|
||||
* @param defaultAutoDismissTimeout The period of time that an
|
||||
* auto-dismissed message will be displayed for.
|
||||
* @param minimizeAnimationTimeout When notifications are minimized, a brief
|
||||
* animation is shown. This animation requires some time to execute,
|
||||
* so a timeout is required before the notification is hidden
|
||||
*/
|
||||
export default class NotificationAPI extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
this.notifications = [];
|
||||
this.highest = { severity: "info" };
|
||||
|
||||
/*
|
||||
* A context in which to hold the active notification and a
|
||||
* handle to its timeout.
|
||||
*/
|
||||
this.activeNotification = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimize a notification. The notification will still be available
|
||||
* from the notification list. Typically notifications with a
|
||||
* severity of 'info' should not be minimized, but rather
|
||||
* dismissed. If you're not sure which is appropriate,
|
||||
* use {@link Notification#dismissOrMinimize}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
minimize(notification) {
|
||||
//Check this is a known notification
|
||||
let index = this.notifications.indexOf(notification);
|
||||
|
||||
if (this.activeTimeout) {
|
||||
/*
|
||||
Method can be called manually (clicking dismiss) or
|
||||
automatically from an auto-timeout. this.activeTimeout
|
||||
acts as a semaphore to prevent race conditions. Cancel any
|
||||
timeout in progress (for the case where a manual dismiss
|
||||
has shortcut an active auto-dismiss), and clear the
|
||||
semaphore.
|
||||
*/
|
||||
clearTimeout(this.activeTimeout);
|
||||
delete this.activeTimeout;
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
notification.model.minimized = true;
|
||||
//Add a brief timeout before showing the next notification
|
||||
// in order to allow the minimize animation to run through.
|
||||
setTimeout(() => {
|
||||
notification.emit('destroy');
|
||||
this.setActiveNotification(this.selectNextNotification());
|
||||
}, MINIMIZE_ANIMATION_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely removes a notification. This will dismiss it from the
|
||||
* message banner and remove it from the list of notifications.
|
||||
* Typically only notifications with a severity of info should be
|
||||
* dismissed. If you're not sure whether to dismiss or minimize a
|
||||
* notification, use {@link Notification#dismissOrMinimize}.
|
||||
* dismiss
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
dismiss(notification) {
|
||||
//Check this is a known notification
|
||||
let index = this.notifications.indexOf(notification);
|
||||
|
||||
if (this.activeTimeout) {
|
||||
/* Method can be called manually (clicking dismiss) or
|
||||
* automatically from an auto-timeout. this.activeTimeout
|
||||
* acts as a semaphore to prevent race conditions. Cancel any
|
||||
* timeout in progress (for the case where a manual dismiss
|
||||
* has shortcut an active auto-dismiss), and clear the
|
||||
* semaphore.
|
||||
*/
|
||||
|
||||
clearTimeout(this.activeTimeout);
|
||||
delete this.activeTimeout;
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
this.notifications.splice(index, 1);
|
||||
}
|
||||
this.setActiveNotification(this.selectNextNotification());
|
||||
this.setHighestSeverity();
|
||||
notification.emit('destroy');
|
||||
}
|
||||
|
||||
/**
|
||||
* Depending on the severity of the notification will selectively
|
||||
* dismiss or minimize where appropriate.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
dismissOrMinimize(notification) {
|
||||
let model = notification.model;
|
||||
if (model.severity === "info") {
|
||||
if (model.autoDismiss === false) {
|
||||
this.minimize(notification);
|
||||
} else {
|
||||
this.dismiss(notification);
|
||||
}
|
||||
} else {
|
||||
this.minimize(notification);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the notification that is currently visible in the banner area
|
||||
* @returns {Notification}
|
||||
*/
|
||||
getActiveNotification() {
|
||||
return this.activeNotification;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method for info notifications. Notifications
|
||||
* created via this method will be auto-destroy after a default
|
||||
* wait period unless explicitly forbidden by the caller through
|
||||
* the {autoDismiss} property on the {NotificationModel}, in which
|
||||
* case the notification will be minimized after the wait.
|
||||
* @param {NotificationModel | string} message either a string for
|
||||
* the title of the notification message, or a {@link NotificationModel}
|
||||
* defining the options notification to display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to dismiss or minimize
|
||||
*/
|
||||
info(message) {
|
||||
let notificationModel = typeof message === "string" ? {title: message} : message;
|
||||
notificationModel.severity = "info";
|
||||
return this.notify(notificationModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method for alert notifications. Notifications
|
||||
* created via this method will will have severity of "alert" enforced
|
||||
* @param {NotificationModel | string} message either a string for
|
||||
* the title of the alert message with default options, or a
|
||||
* {@link NotificationModel} defining the options notification to
|
||||
* display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to dismiss or minimize
|
||||
*/
|
||||
alert(message) {
|
||||
let notificationModel = typeof message === "string" ? {title: message} : message;
|
||||
notificationModel.severity = "alert";
|
||||
return this.notify(notificationModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method for error notifications. Notifications
|
||||
* created via this method will will have severity of "error" enforced
|
||||
* @param {NotificationModel | string} message either a string for
|
||||
* the title of the error message with default options, or a
|
||||
* {@link NotificationModel} defining the options of the notification to
|
||||
* display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to dismiss or minimize
|
||||
*/
|
||||
error(message) {
|
||||
let notificationModel = typeof message === "string" ? {title: message} : message;
|
||||
notificationModel.severity = "error";
|
||||
return this.notify(notificationModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setHighestSeverity() {
|
||||
let severity = {
|
||||
"info": 1,
|
||||
"alert": 2,
|
||||
"error": 3
|
||||
};
|
||||
this.highest.severity = this.notifications.reduce((previous, notification) => {
|
||||
if (severity[notification.model.severity] > severity[previous]) {
|
||||
return notification.model.severity;
|
||||
} else {
|
||||
return previous;
|
||||
}
|
||||
}, "info");
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the user of an event. If there is a banner notification
|
||||
* already active, then it will be dismissed or minimized automatically,
|
||||
* and the provided notification displayed in its place.
|
||||
*
|
||||
* @param {NotificationModel} notificationModel The notification to
|
||||
* display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to {@link Notification#dismiss} or {@link Notification#minimize}
|
||||
*/
|
||||
notify(notificationModel) {
|
||||
let notification;
|
||||
let activeNotification = this.activeNotification;
|
||||
|
||||
notificationModel.severity = notificationModel.severity || "info";
|
||||
notificationModel.timestamp = moment.utc().format('YYYY-MM-DD hh:mm:ss.ms');
|
||||
|
||||
notification = new MCTNotification(notificationModel, this);
|
||||
|
||||
this.notifications.push(notification);
|
||||
this.setHighestSeverity();
|
||||
|
||||
/*
|
||||
Check if there is already an active (ie. visible) notification
|
||||
*/
|
||||
if (!this.activeNotification) {
|
||||
this.setActiveNotification(notification);
|
||||
} else if (!this.activeTimeout) {
|
||||
/*
|
||||
If there is already an active notification, time it out. If it's
|
||||
already got a timeout in progress (either because it has had
|
||||
timeout forced because of a queue of messages, or it had an
|
||||
autodismiss specified), leave it to run. Otherwise force a
|
||||
timeout.
|
||||
|
||||
This notification has been added to queue and will be
|
||||
serviced as soon as possible.
|
||||
*/
|
||||
this.activeTimeout = setTimeout(() => {
|
||||
this.dismissOrMinimize(activeNotification);
|
||||
}, DEFAULT_AUTO_DISMISS_TIMEOUT);
|
||||
}
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used internally by the NotificationService
|
||||
* @private
|
||||
*/
|
||||
setActiveNotification(notification) {
|
||||
let shouldAutoDismiss;
|
||||
this.activeNotification = notification;
|
||||
|
||||
if (!notification) {
|
||||
delete this.activeTimeout;
|
||||
return;
|
||||
}
|
||||
this.emit('notification', notification);
|
||||
|
||||
if (notification.model.severity === "info") {
|
||||
shouldAutoDismiss = true;
|
||||
} else {
|
||||
shouldAutoDismiss = notification.model.autoDismiss;
|
||||
}
|
||||
|
||||
if (shouldAutoDismiss || this.selectNextNotification()) {
|
||||
this.activeTimeout = setTimeout(() => {
|
||||
this.dismissOrMinimize(notification);
|
||||
}, DEFAULT_AUTO_DISMISS_TIMEOUT);
|
||||
} else {
|
||||
delete this.activeTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used internally by the NotificationService
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
selectNextNotification() {
|
||||
let notification;
|
||||
let i = 0;
|
||||
|
||||
/*
|
||||
Loop through the notifications queue and find the first one that
|
||||
has not already been minimized (manually or otherwise).
|
||||
*/
|
||||
for (; i < this.notifications.length; i++) {
|
||||
notification = this.notifications[i];
|
||||
|
||||
if (!notification.model.minimized &&
|
||||
notification !== this.activeNotification) {
|
||||
return notification;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -75,7 +75,12 @@
|
||||
this.openmct.editor.cancel();
|
||||
},
|
||||
saveAndFinishEditing() {
|
||||
this.openmct.editor.save();
|
||||
this.openmct.editor.save().then(()=> {
|
||||
this.openmct.notifications.info('Save successful');
|
||||
}).catch((error) => {
|
||||
this.openmct.notifications.error('Error saving objects');
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
|
@ -47,7 +47,7 @@
|
||||
</pane>
|
||||
</multipane>
|
||||
<div class="l-shell__status">
|
||||
<MctStatus></MctStatus>
|
||||
<StatusBar></StatusBar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -236,7 +236,6 @@
|
||||
|
||||
<script>
|
||||
import Inspector from '../inspector/Inspector.vue';
|
||||
import MctStatus from './MctStatus.vue';
|
||||
import MctTree from './mct-tree.vue';
|
||||
import ObjectView from './ObjectView.vue';
|
||||
import MctTemplate from '../legacy/mct-template.vue';
|
||||
@ -246,6 +245,7 @@
|
||||
import multipane from '../controls/multipane.vue';
|
||||
import pane from '../controls/pane.vue';
|
||||
import BrowseBar from './BrowseBar.vue';
|
||||
import StatusBar from './status-bar/StatusBar.vue';
|
||||
import Toolbar from './Toolbar.vue';
|
||||
|
||||
var enterFullScreen = () => {
|
||||
@ -280,7 +280,6 @@
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
Inspector,
|
||||
MctStatus,
|
||||
MctTree,
|
||||
ObjectView,
|
||||
'mct-template': MctTemplate,
|
||||
@ -290,6 +289,7 @@
|
||||
multipane,
|
||||
pane,
|
||||
BrowseBar,
|
||||
StatusBar,
|
||||
Toolbar
|
||||
},
|
||||
mounted() {
|
||||
|
@ -1,14 +0,0 @@
|
||||
<template>
|
||||
<span class="c-status">
|
||||
[ Status ]
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
}
|
||||
</script>
|
41
src/ui/components/layout/status-bar/Indicators.vue
Normal file
41
src/ui/components/layout/status-bar/Indicators.vue
Normal file
@ -0,0 +1,41 @@
|
||||
<!--
|
||||
Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
as represented by the Administrator of the National Aeronautics and Space
|
||||
Administration. All rights reserved.
|
||||
Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0.
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
Open MCT includes source code licensed under additional open source
|
||||
licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<template>
|
||||
<span id='status' class='status-holder'></span>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
|
||||
mounted() {
|
||||
this.openmct.indicators.indicatorObjects.forEach((indicator) => {
|
||||
// So that we can consistently position indicator elements,
|
||||
// guarantee that they are wrapped in an element we control
|
||||
var wrapperNode = document.createElement('span');
|
||||
wrapperNode.className = 'l-indicator';
|
||||
wrapperNode.appendChild(indicator.element);
|
||||
this.$el.appendChild(wrapperNode);
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
98
src/ui/components/layout/status-bar/NotificationBanner.vue
Normal file
98
src/ui/components/layout/status-bar/NotificationBanner.vue
Normal file
@ -0,0 +1,98 @@
|
||||
<!--
|
||||
Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
as represented by the Administrator of the National Aeronautics and Space
|
||||
Administration. All rights reserved.
|
||||
Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0.
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
Open MCT includes source code licensed under additional open source
|
||||
licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<template>
|
||||
<div class="l-message-banner s-message-banner"
|
||||
:class="[
|
||||
activeModel.severity,
|
||||
{
|
||||
'minimized': activeModel.minimized,
|
||||
'new': !activeModel.minimized
|
||||
}]"
|
||||
v-if="activeModel">
|
||||
<span @click="maximize()" class="banner-elem label">{{activeModel.title}}</span>
|
||||
<span @click="maximize()" v-if="activeModel.progress !== undefined || activeModel.unknownProgress">
|
||||
<div class="banner-elem"><!-- was mct-include -->
|
||||
<span class="l-progress-bar s-progress-bar"
|
||||
:class="{'indeterminate': activeModel.unknownProgress }">
|
||||
<span class="progress-amt-holder">
|
||||
<span class="progress-amt" :style="progressWidth"></span>
|
||||
</span>
|
||||
</span>
|
||||
<div class="progress-info hint" v-if="activeModel.progressText !== undefined">
|
||||
<span class="progress-amt-text" v-if="activeModel.progress > 0">{{activeModel.progress}}% complete. </span>
|
||||
{{activeModel.progressText}}
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
<a class="close icon-x" @click="dismiss()"></a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.l-message-banner {
|
||||
display: inline;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
}
|
||||
.banner-elem {
|
||||
display: inline;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
let activeNotification = undefined;
|
||||
let dialogService = undefined;
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
data() {
|
||||
return {
|
||||
activeModel: undefined
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showNotification(notification) {
|
||||
activeNotification = notification;
|
||||
this.activeModel = notification.model;
|
||||
activeNotification.once('destroy', () => {
|
||||
if (this.activeModel === notification.model){
|
||||
this.activeModel = undefined;
|
||||
activeNotification = undefined;
|
||||
}
|
||||
});
|
||||
},
|
||||
dismiss() {
|
||||
activeNotification.dismissOrMinimize();
|
||||
},
|
||||
maximize() {
|
||||
//Not implemented yet.
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
progressWidth() {
|
||||
return {
|
||||
width: this.activeModel.progress + '%'
|
||||
};
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
openmct.notifications.on('notification', this.showNotification);
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
42
src/ui/components/layout/status-bar/StatusBar.vue
Normal file
42
src/ui/components/layout/status-bar/StatusBar.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<!--
|
||||
Open MCT, Copyright (c) 2014-2018, United States Government
|
||||
as represented by the Administrator of the National Aeronautics and Space
|
||||
Administration. All rights reserved.
|
||||
Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0.
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
Open MCT includes source code licensed under additional open source
|
||||
licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<template>
|
||||
<span class="c-status">
|
||||
<indicators></indicators>
|
||||
<notification-banner></notification-banner>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.c-status {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import Indicators from './Indicators.vue';
|
||||
import NotificationBanner from './NotificationBanner.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Indicators,
|
||||
NotificationBanner
|
||||
}
|
||||
}
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user