Commit 48da780b by Samuel Padgett

Combine connection error messages

When multiple connection errors happen close together, show them in one
toast notification to avoid spamming the user.
parent 176e824b
......@@ -2,6 +2,10 @@
color: @gray-light;
}
.toast-notification-details .truncated-content {
white-space: pre-line;
}
.toast-notification-message {
font-weight: 700;
margin-right: 5px;
......
......@@ -1211,6 +1211,46 @@ angular.module('openshiftCommonServices')
}
}
// If several connection errors happen close together, display them as one
// notification. This prevents us spamming the user with many failed requests
// at once.
var queuedErrors = [];
var addQueuedNotifications = _.debounce(function() {
if (!queuedErrors.length) {
return;
}
// Show all queued messages together. If the details is extremely long, it
// will be truncated with a see more link.
var notification = {
type: 'error',
message: 'An error occurred connecting to the server.',
details: queuedErrors.join('\n'),
links: [{
label: 'Refresh',
onClick: function() {
window.location.reload();
}
}]
};
// Use `$rootScope.$emit` instead of NotificationsService directly
// so that DataService doesn't add a dependency on `openshiftCommonUI`
$rootScope.$emit('NotificationsService.addNotification', notification);
// Clear the queue.
queuedErrors = [];
}, 300, { maxWait: 1000 });
var showRequestError = function(message, status) {
if (status) {
message += " (status " + status + ")";
}
// Queue the message and call debounced `addQueuedNotifications`.
queuedErrors.push(message);
addQueuedNotifications();
};
function DataService() {
this._listDeferredMap = {};
this._watchCallbacksMap = {};
......@@ -1547,16 +1587,7 @@ angular.module('openshiftCommonServices')
})
.error(function(data, status, headers, config) {
if (opts.errorNotification !== false) {
var msg = "Failed to get " + resource + "/" + name;
if (status !== 0) {
msg += " (" + status + ")";
}
// Use `$rootScope.$emit` instead of NotificationsService directly
// so that DataService doesn't add a dependency on `openshiftCommonUI`
$rootScope.$emit('NotificationsService.addNotification', {
type: 'error',
message: msg
});
showRequestError("Failed to get " + resource + "/" + name, status);
}
deferred.reject({
data: data,
......@@ -2092,16 +2123,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
return;
}
var msg = "Failed to list " + resource;
if (status !== 0) {
msg += " (" + status + ")";
}
// Use `$rootScope.$emit` instead of NotificationsService directly
// so that DataService doesn't add a dependency on `openshiftCommonUI`
$rootScope.$emit('NotificationsService.addNotification', {
type: 'error',
message: msg
});
showRequestError("Failed to list " + resource, status);
});
});
}
......@@ -2123,16 +2145,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
return;
}
var msg = "Failed to list " + resource;
if (status !== 0) {
msg += " (" + status + ")";
}
// Use `$rootScope.$emit` instead of NotificationsService directly
// so that DataService doesn't add a dependency on `openshiftCommonUI`
$rootScope.$emit('NotificationsService.addNotification', {
type: 'error',
message: msg
});
showRequestError("Failed to list " + resource, status);
});
}
};
......
......@@ -405,7 +405,7 @@ hawtioPluginLoader.addModule('openshiftCommonUI');
" <span class=\"{{notification.type | alertIcon}}\" aria-hidden=\"true\"></span>\n" +
" <span class=\"sr-only\">{{notification.type}}</span>\n" +
" <span class=\"toast-notification-message\" ng-if=\"notification.message\">{{notification.message}}</span>\n" +
" <span ng-if=\"notification.details\">\n" +
" <div ng-if=\"notification.details\" class=\"toast-notification-details\">\n" +
" <truncate-long-text\n" +
" limit=\"200\"\n" +
" content=\"notification.details\"\n" +
......@@ -413,7 +413,7 @@ hawtioPluginLoader.addModule('openshiftCommonUI');
" expandable=\"true\"\n" +
" hide-collapse=\"true\">\n" +
" </truncate-long-text>\n" +
" </span>\n" +
" </div>\n" +
" <span ng-repeat=\"link in notification.links\">\n" +
" <a ng-if=\"!link.href\" href=\"\" ng-click=\"onClick(notification, link)\" role=\"button\">{{link.label}}</a>\n" +
" <a ng-if=\"link.href\" ng-href=\"{{link.href}}\" ng-attr-target=\"{{link.target}}\">{{link.label}}</a>\n" +
......@@ -1081,16 +1081,18 @@ angular.module('openshiftCommonUI')
// Listen for updates from NotificationsService to show a notification.
var deregisterNotificationListener = $rootScope.$on('NotificationsService.onNotificationAdded', function(event, notification) {
$scope.notifications.push(notification);
if (NotificationsService.isAutoDismiss(notification)) {
$timeout(function () {
notification.hidden = true;
}, NotificationsService.dismissDelay);
}
$scope.$evalAsync(function() {
$scope.notifications.push(notification);
if (NotificationsService.isAutoDismiss(notification)) {
$timeout(function () {
notification.hidden = true;
}, NotificationsService.dismissDelay);
}
// Whenever we add a new notification, also remove any hidden toasts
// so that the array doesn't grow indefinitely.
pruneRemovedNotifications();
// Whenever we add a new notification, also remove any hidden toasts
// so that the array doesn't grow indefinitely.
pruneRemovedNotifications();
});
});
$scope.$on('$destroy', function() {
......
......@@ -267,6 +267,9 @@ div.hopscotch-bubble .hopscotch-nav-button.prev {
.toast-action-divider {
color: #9c9c9c;
}
.toast-notification-details .truncated-content {
white-space: pre-line;
}
.toast-notification-message {
font-weight: 700;
margin-right: 5px;
......
......@@ -576,7 +576,7 @@ hawtioPluginLoader.addModule('openshiftCommonUI');
" <span class=\"{{notification.type | alertIcon}}\" aria-hidden=\"true\"></span>\n" +
" <span class=\"sr-only\">{{notification.type}}</span>\n" +
" <span class=\"toast-notification-message\" ng-if=\"notification.message\">{{notification.message}}</span>\n" +
" <span ng-if=\"notification.details\">\n" +
" <div ng-if=\"notification.details\" class=\"toast-notification-details\">\n" +
" <truncate-long-text\n" +
" limit=\"200\"\n" +
" content=\"notification.details\"\n" +
......@@ -584,7 +584,7 @@ hawtioPluginLoader.addModule('openshiftCommonUI');
" expandable=\"true\"\n" +
" hide-collapse=\"true\">\n" +
" </truncate-long-text>\n" +
" </span>\n" +
" </div>\n" +
" <span ng-repeat=\"link in notification.links\">\n" +
" <a ng-if=\"!link.href\" href=\"\" ng-click=\"onClick(notification, link)\" role=\"button\">{{link.label}}</a>\n" +
" <a ng-if=\"link.href\" ng-href=\"{{link.href}}\" ng-attr-target=\"{{link.target}}\">{{link.label}}</a>\n" +
......@@ -1252,16 +1252,18 @@ angular.module('openshiftCommonUI')
// Listen for updates from NotificationsService to show a notification.
var deregisterNotificationListener = $rootScope.$on('NotificationsService.onNotificationAdded', function(event, notification) {
$scope.notifications.push(notification);
if (NotificationsService.isAutoDismiss(notification)) {
$timeout(function () {
notification.hidden = true;
}, NotificationsService.dismissDelay);
}
$scope.$evalAsync(function() {
$scope.notifications.push(notification);
if (NotificationsService.isAutoDismiss(notification)) {
$timeout(function () {
notification.hidden = true;
}, NotificationsService.dismissDelay);
}
// Whenever we add a new notification, also remove any hidden toasts
// so that the array doesn't grow indefinitely.
pruneRemovedNotifications();
// Whenever we add a new notification, also remove any hidden toasts
// so that the array doesn't grow indefinitely.
pruneRemovedNotifications();
});
});
$scope.$on('$destroy', function() {
......@@ -2975,6 +2977,46 @@ angular.module('openshiftCommonServices')
}
}
// If several connection errors happen close together, display them as one
// notification. This prevents us spamming the user with many failed requests
// at once.
var queuedErrors = [];
var addQueuedNotifications = _.debounce(function() {
if (!queuedErrors.length) {
return;
}
// Show all queued messages together. If the details is extremely long, it
// will be truncated with a see more link.
var notification = {
type: 'error',
message: 'An error occurred connecting to the server.',
details: queuedErrors.join('\n'),
links: [{
label: 'Refresh',
onClick: function() {
window.location.reload();
}
}]
};
// Use `$rootScope.$emit` instead of NotificationsService directly
// so that DataService doesn't add a dependency on `openshiftCommonUI`
$rootScope.$emit('NotificationsService.addNotification', notification);
// Clear the queue.
queuedErrors = [];
}, 300, { maxWait: 1000 });
var showRequestError = function(message, status) {
if (status) {
message += " (status " + status + ")";
}
// Queue the message and call debounced `addQueuedNotifications`.
queuedErrors.push(message);
addQueuedNotifications();
};
function DataService() {
this._listDeferredMap = {};
this._watchCallbacksMap = {};
......@@ -3311,16 +3353,7 @@ angular.module('openshiftCommonServices')
})
.error(function(data, status, headers, config) {
if (opts.errorNotification !== false) {
var msg = "Failed to get " + resource + "/" + name;
if (status !== 0) {
msg += " (" + status + ")";
}
// Use `$rootScope.$emit` instead of NotificationsService directly
// so that DataService doesn't add a dependency on `openshiftCommonUI`
$rootScope.$emit('NotificationsService.addNotification', {
type: 'error',
message: msg
});
showRequestError("Failed to get " + resource + "/" + name, status);
}
deferred.reject({
data: data,
......@@ -3856,16 +3889,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
return;
}
var msg = "Failed to list " + resource;
if (status !== 0) {
msg += " (" + status + ")";
}
// Use `$rootScope.$emit` instead of NotificationsService directly
// so that DataService doesn't add a dependency on `openshiftCommonUI`
$rootScope.$emit('NotificationsService.addNotification', {
type: 'error',
message: msg
});
showRequestError("Failed to list " + resource, status);
});
});
}
......@@ -3887,16 +3911,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
return;
}
var msg = "Failed to list " + resource;
if (status !== 0) {
msg += " (" + status + ")";
}
// Use `$rootScope.$emit` instead of NotificationsService directly
// so that DataService doesn't add a dependency on `openshiftCommonUI`
$rootScope.$emit('NotificationsService.addNotification', {
type: 'error',
message: msg
});
showRequestError("Failed to list " + resource, status);
});
}
};
......
......@@ -376,7 +376,7 @@ angular.module('openshiftCommonUI').run(['$templateCache', function($templateCac
" <span class=\"{{notification.type | alertIcon}}\" aria-hidden=\"true\"></span>\n" +
" <span class=\"sr-only\">{{notification.type}}</span>\n" +
" <span class=\"toast-notification-message\" ng-if=\"notification.message\">{{notification.message}}</span>\n" +
" <span ng-if=\"notification.details\">\n" +
" <div ng-if=\"notification.details\" class=\"toast-notification-details\">\n" +
" <truncate-long-text\n" +
" limit=\"200\"\n" +
" content=\"notification.details\"\n" +
......@@ -384,7 +384,7 @@ angular.module('openshiftCommonUI').run(['$templateCache', function($templateCac
" expandable=\"true\"\n" +
" hide-collapse=\"true\">\n" +
" </truncate-long-text>\n" +
" </span>\n" +
" </div>\n" +
" <span ng-repeat=\"link in notification.links\">\n" +
" <a ng-if=\"!link.href\" href=\"\" ng-click=\"onClick(notification, link)\" role=\"button\">{{link.label}}</a>\n" +
" <a ng-if=\"link.href\" ng-href=\"{{link.href}}\" ng-attr-target=\"{{link.target}}\">{{link.label}}</a>\n" +
......
......@@ -9,7 +9,7 @@
<span class="{{notification.type | alertIcon}}" aria-hidden="true"></span>
<span class="sr-only">{{notification.type}}</span>
<span class="toast-notification-message" ng-if="notification.message">{{notification.message}}</span>
<span ng-if="notification.details">
<div ng-if="notification.details" class="toast-notification-details">
<truncate-long-text
limit="200"
content="notification.details"
......@@ -17,7 +17,7 @@
expandable="true"
hide-collapse="true">
</truncate-long-text>
</span>
</div>
<span ng-repeat="link in notification.links">
<a ng-if="!link.href" href="" ng-click="onClick(notification, link)" role="button">{{link.label}}</a>
<a ng-if="link.href" ng-href="{{link.href}}" ng-attr-target="{{link.target}}">{{link.label}}</a>
......
......@@ -56,16 +56,18 @@ angular.module('openshiftCommonUI')
// Listen for updates from NotificationsService to show a notification.
var deregisterNotificationListener = $rootScope.$on('NotificationsService.onNotificationAdded', function(event, notification) {
$scope.notifications.push(notification);
if (NotificationsService.isAutoDismiss(notification)) {
$timeout(function () {
notification.hidden = true;
}, NotificationsService.dismissDelay);
}
$scope.$evalAsync(function() {
$scope.notifications.push(notification);
if (NotificationsService.isAutoDismiss(notification)) {
$timeout(function () {
notification.hidden = true;
}, NotificationsService.dismissDelay);
}
// Whenever we add a new notification, also remove any hidden toasts
// so that the array doesn't grow indefinitely.
pruneRemovedNotifications();
// Whenever we add a new notification, also remove any hidden toasts
// so that the array doesn't grow indefinitely.
pruneRemovedNotifications();
});
});
$scope.$on('$destroy', function() {
......
......@@ -76,6 +76,46 @@ angular.module('openshiftCommonServices')
}
}
// If several connection errors happen close together, display them as one
// notification. This prevents us spamming the user with many failed requests
// at once.
var queuedErrors = [];
var addQueuedNotifications = _.debounce(function() {
if (!queuedErrors.length) {
return;
}
// Show all queued messages together. If the details is extremely long, it
// will be truncated with a see more link.
var notification = {
type: 'error',
message: 'An error occurred connecting to the server.',
details: queuedErrors.join('\n'),
links: [{
label: 'Refresh',
onClick: function() {
window.location.reload();
}
}]
};
// Use `$rootScope.$emit` instead of NotificationsService directly
// so that DataService doesn't add a dependency on `openshiftCommonUI`
$rootScope.$emit('NotificationsService.addNotification', notification);
// Clear the queue.
queuedErrors = [];
}, 300, { maxWait: 1000 });
var showRequestError = function(message, status) {
if (status) {
message += " (status " + status + ")";
}
// Queue the message and call debounced `addQueuedNotifications`.
queuedErrors.push(message);
addQueuedNotifications();
};
function DataService() {
this._listDeferredMap = {};
this._watchCallbacksMap = {};
......@@ -412,16 +452,7 @@ angular.module('openshiftCommonServices')
})
.error(function(data, status, headers, config) {
if (opts.errorNotification !== false) {
var msg = "Failed to get " + resource + "/" + name;
if (status !== 0) {
msg += " (" + status + ")";
}
// Use `$rootScope.$emit` instead of NotificationsService directly
// so that DataService doesn't add a dependency on `openshiftCommonUI`
$rootScope.$emit('NotificationsService.addNotification', {
type: 'error',
message: msg
});
showRequestError("Failed to get " + resource + "/" + name, status);
}
deferred.reject({
data: data,
......@@ -957,16 +988,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
return;
}
var msg = "Failed to list " + resource;
if (status !== 0) {
msg += " (" + status + ")";
}
// Use `$rootScope.$emit` instead of NotificationsService directly
// so that DataService doesn't add a dependency on `openshiftCommonUI`
$rootScope.$emit('NotificationsService.addNotification', {
type: 'error',
message: msg
});
showRequestError("Failed to list " + resource, status);
});
});
}
......@@ -988,16 +1010,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
return;
}
var msg = "Failed to list " + resource;
if (status !== 0) {
msg += " (" + status + ")";
}
// Use `$rootScope.$emit` instead of NotificationsService directly
// so that DataService doesn't add a dependency on `openshiftCommonUI`
$rootScope.$emit('NotificationsService.addNotification', {
type: 'error',
message: msg
});
showRequestError("Failed to list " + resource, status);
});
}
};
......
......@@ -2,6 +2,10 @@
color: @gray-light;
}
.toast-notification-details .truncated-content {
white-space: pre-line;
}
.toast-notification-message {
font-weight: 700;
margin-right: 5px;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment