Skip to content
This repository has been archived by the owner on Jan 24, 2023. It is now read-only.

Commit

Permalink
Improve the loading indicators on initial console load (#1286)
Browse files Browse the repository at this point in the history
* Improve the loading indicators on initial console load

* A better way of having a global busy state and indicator

* Fix unit tests

* Fix lint issue
  • Loading branch information
nwmac authored and richard-cox committed Oct 6, 2017
1 parent d61d957 commit b7e658e
Show file tree
Hide file tree
Showing 13 changed files with 174 additions and 28 deletions.
2 changes: 2 additions & 0 deletions components/app-core/frontend/i18n/en_US/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"terms": "Terms of Use",
"endpoint": "Endpoint",

"preparing.console": "Preparing Console ...",

"buttons": {
"ok": "OK",
"cancel": "Cancel",
Expand Down
72 changes: 72 additions & 0 deletions components/app-core/frontend/src/utils/busy.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
(function () {
'use strict';
angular
.module('app.utils')
.factory('appBusyService', appBusyServiceFactory);

/**
* @namespace appBusyService
* @memberof app.utils
* @name appBusyService
* @description The application busy service
* @param {object} appEventService - the event service
* @returns {object} the busy service
*/
function appBusyServiceFactory() {

var nextBusyId = 0;

// Maintain a list of outstanding busy messages - only show the most recent
var busyStack = [];

var busyStates = {};

return {

busyState: {},

_update: function () {
if (busyStack.length === 0) {
this.busyState.active = false;
} else {
// Get the last item - that is the most recent
var newestId = busyStack[busyStack.length - 1];
var busyInfo = busyStates[newestId];
this.busyState.label = busyInfo.label;
this.busyState.local = busyInfo.local || false;
this.busyState.active = true;
}
},

set: function (label, nonModal) {
var id = nextBusyId;
nextBusyId++;
busyStates[id] = {
id: id,
label: label,
local: nonModal || false
};
busyStack.push(id);
this._update();
return id;
},

clear: function (id) {
if (busyStack.length === 0) {
return;
}
var newestId = busyStack[busyStack.length - 1];
delete busyStates[id];
_.remove(busyStack, function (v) {
return v === id;
});

// If we removed what was the newest, then we need to show the next newest, or hide the busy message if none left
if (id === newestId) {
this. _update();
}
}
};
}

})();
15 changes: 12 additions & 3 deletions components/app-core/frontend/src/view/application.directive.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
* @param {$rootScope} $rootScope - Angular $rootScope service
* @param {$scope} $scope - Angular $scope service
* @param {appLoggedInService} appLoggedInService - Logged In Service
* @param {appBusyService} appBusyService - Application Busy Service
* @property {app.utils.appEventService} appEventService - the event bus service
* @property {app.model.modelManager} modelManager - the application model manager
* @property {app.view.appUpgradeCheck} appUpgradeCheck - the upgrade check service
Expand All @@ -61,7 +62,8 @@
$window,
$rootScope,
$scope,
appLoggedInService
appLoggedInService,
appBusyService
) {

var vm = this;
Expand All @@ -82,6 +84,9 @@
vm.logout = logout;
vm.reload = reload;

// Global spinner state
vm.spinner = appBusyService.busyState;

if (loginManager.isEnabled()) {
$timeout(function () {
verifySessionOrCheckUpgrade();
Expand Down Expand Up @@ -187,9 +192,12 @@
vm.failedLogin = false;
vm.serverErrorOnLogin = false;
vm.serverFailedToRespond = false;
vm.showGlobalSpinner = true;
//vm.showGlobalSpinner = true;
//vm.globalSpinnerLabel = 'preparing.console';
vm.redirectState = false;

vm.appBusyId = appBusyService.set('preparing.console');

// If we have a setup error, then we don't want to continue login to some other page
// We will redirect to our error page instead
var continueLogin = true;
Expand Down Expand Up @@ -239,7 +247,8 @@
}
})
.finally(function () {
vm.showGlobalSpinner = false;
vm.appBusyId = appBusyService.clear(vm.appBusyId);

if (continueLogin) {
// When we notify listeners that login has completed, in some cases we don't want them
// to redirect to their page - we might want to control that they go to the endpoints dashboard (for example).
Expand Down
4 changes: 3 additions & 1 deletion components/app-core/frontend/src/view/application.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
</console-view>
<global-spinner
class="application-global-spinner"
spinner-active="applicationCtrl.showGlobalSpinner"
spinner-active="applicationCtrl.spinner.active"
spinner-label="applicationCtrl.spinner.label"
spinner-local="applicationCtrl.spinner.local"
spinner-type="bounce-spinner">
</global-spinner>
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,23 @@
bindToController: {
classes: '@?',
spinnerActive: '=',
spinnerType: '@?'
spinnerType: '@?',
spinnerLabel: '=?',
spinnerLocal: '=?'
},
controller: GlobalSpinnerController,
controllerAs: 'globalSpinnerCtrl',
link: function (scope) {
scope.$watch('spinnerActive', function (spinnerActive) {
if (spinnerActive) {
$document.find('body').addClass('global-spinner-active');
} else {
$document.find('body').removeClass('global-spinner-active');
}
});
link: function (scope, element, attrs, ctrl) {
// Check to see if other spinners are already active
if (!ctrl.spinnerLocal) {
scope.$watch('spinnerActive', function (spinnerActive) {
if (spinnerActive) {
$document.find('body').addClass('global-spinner-active');
} else {
$document.find('body').removeClass('global-spinner-active');
}
});
}
},
scope: {},
templateUrl: 'framework/widgets/global-spinner/global-spinner.html',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
<div class="global-spinner" ng-if="globalSpinnerCtrl.spinnerActive">
<ng-transclude></ng-transclude>
<spinner ng-if="globalSpinnerCtrl.spinnerType === 'spinner'"></spinner>
<bounce-spinner ng-if="globalSpinnerCtrl.spinnerType === 'bounce-spinner'"
ng-class="'{{ globalSpinnerCtrl.classes }}'">
</bounce-spinner>
<div ng-class="{'global-spinner': !globalSpinnerCtrl.spinnerLocal, 'global-spinner-local': globalSpinnerCtrl.spinnerLocal}" ng-if="globalSpinnerCtrl.spinnerActive">
<div class="global-spinner-content" ng-class="{'global-spinnter-with-label': globalSpinnerCtrl.spinnerLabel}">
<ng-transclude></ng-transclude>
<div ng-if="globalSpinnerCtrl.spinnerLabel" translate>{{ globalSpinnerCtrl.spinnerLabel}}</div>
<spinner ng-if="globalSpinnerCtrl.spinnerType === 'spinner'"></spinner>
<bounce-spinner ng-if="globalSpinnerCtrl.spinnerType === 'bounce-spinner'"
ng-class="'{{ globalSpinnerCtrl.classes }}'">
</bounce-spinner>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.global-spinner {
background-color: $global-spinner-bg-color;

.global-spinner, .global-spinner-local {
position: fixed;
top: 0px;
left: 0px;
Expand All @@ -11,14 +11,41 @@
align-items: center;
height: 100%;
width: 100%;
z-index: $global-spinner-z-index;

spinner {
height: $global-spinner-height;
width: $global-spinner-width;
}

.global-spinner-content.global-spinnter-with-label {
background-color: $gray-darker;
color: $white;
padding: $console-half-space $console-unit-space * 2;
border-radius: 4px;
font-size: 16px;
text-align: center;

> div {
margin-bottom: $console-half-space;
}

bounce-spinner .bounce-spinner > div {
background-color: $white;
width: 16px;
height: 16px;
}
}
}

.global-spinner-active {
overflow-y: hidden;
}

.global-spinner {
background-color: $global-spinner-bg-color;
z-index: $global-spinner-z-index;
}

.global-spinner-local {
pointer-events: none;
}
1 change: 1 addition & 0 deletions components/cloud-foundry/frontend/i18n/en_US/app-wall.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"app-wall": "@:applications",
"search-placeholder": "Search by name",
"add-application": "Add Application",
"retrieving": "Retrieving Applications",
"reset": "Reset",
"options": {
"show": "Show Options",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"cloud-foundry": "Cloud Foundry",
"cf.busy": "Collecting Cloud Foundry Metadata",
"org": "Org",
"space": "Space",
"menu": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,26 @@
* @param {app.model.modelManager} modelManager - the model management service
* @param {app.utils.appEventService} appEventService - the event bus service
* @param {object} appLoggedInService - Logged In Service
* @param {object} appBusyService - the appBusyService service
* @constructor
*/
function ApplicationsController($scope, $q, $state, appUtilsService, modelManager, appEventService, appLoggedInService) {
function ApplicationsController($scope, $q, $state,appUtilsService, modelManager, appEventService, appLoggedInService, appBusyService) {

var authService = modelManager.retrieve('cloud-foundry.model.auth');
var initialized = $q.defer();
var vm = this;

if (appLoggedInService.isLoggedIn()) {
initialized.resolve();
}

function init() {
vm.appBusyId = appBusyService.set('cf.busy');
return initialized.promise
.then(function () {
return authService.initialize();
return authService.initialize().finally(function () {
appBusyService.clear(vm.appBusyId);
});
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@
* @param {object} cfAppWallActions - service providing collection of actions that can be taken on the app wall (add,
* deploy, etc)
* @param {appLocalStorage} appLocalStorage - service provides access to the local storage facility of the web browser
* @param {appBusyService} appBusyService - the application busy service
*
*/
function ApplicationsListController($scope, $translate, $state, $timeout, $q, $window, modelManager, appErrorService,
appUtilsService, cfOrganizationModel, cfAppWallActions, appLocalStorage) {
appUtilsService, cfOrganizationModel, cfAppWallActions, appLocalStorage, appBusyService) {

var vm = this;

Expand All @@ -46,6 +48,12 @@

vm.model = modelManager.retrieve('cloud-foundry.model.application');
vm.loading = true;
if (!vm.model.hasApps) {
vm.appBusyId = appBusyService.set('app-wall.retrieving', true);
} else {
vm.appBusyId = undefined;
}

vm.isSpaceDeveloper = false;
vm.clusters = [{label: 'app-wall.select-endpoint-all', value: 'all', translateLabel: true}];
vm.organizations = [{label: 'app-wall.select-org-all', value: 'all', translateLabel: true}];
Expand Down Expand Up @@ -110,6 +118,11 @@
appErrorService.clearAppError();
// Ensure that remove the resize handler on the window
angular.element($window).off('resize', onResize);

// Clear the busy indicator if it is shown
if (vm.appBusyId) {
appBusyService.clear(vm.appBusyId);
}
});

$scope.$watch(function () {
Expand Down Expand Up @@ -154,6 +167,10 @@
.finally(function () {
// Ensure ready is always set after initial load. Ready will show filters, no services/app message, etc
vm.ready = true;
if (vm.appBusyId) {
appBusyService.clear(vm.appBusyId);
vm.appBusyId = undefined;
}
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
var modelManager = $injector.get('modelManager');
appEventService = $injector.get('appEventService');
var appLoggedInService = $injector.get('appLoggedInService');
var appBusyService = $injector.get('appBusyService');

authService = modelManager.retrieve('cloud-foundry.model.auth');
spyOn(authService, 'initialize');
spyOn(authService, 'initialize').and.callThrough();

$scope = $injector.get('$rootScope').$new();

var ApplicationsController = $state.get('cf.applications').controller;
$controller = new ApplicationsController($scope, $q, $state, appUtilsService, modelManager, appEventService, appLoggedInService);
$controller = new ApplicationsController($scope, $q, $state, appUtilsService, modelManager, appEventService, appLoggedInService, appBusyService);

expect($controller).toBeDefined();
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
var cfAppWallActions = $injector.get('cfAppWallActions');
var $window = $injector.get('$window');
var appLocalStorage = $injector.get('appLocalStorage');
var appBusyService = $injector.get('appBusyService');

var userCnsiModel = modelManager.retrieve('app.model.serviceInstance.user');
if (Object.keys(userCnsiModel.serviceInstances).length === 0) {
Expand All @@ -53,7 +54,7 @@

var ApplicationsListController = $state.get('cf.applications.list').controller;
$controller = new ApplicationsListController($scope, $translate, $state, $timeout, $q, $window, modelManager,
errorService, appUtilsService, cfOrganizationModel, cfAppWallActions, appLocalStorage);
errorService, appUtilsService, cfOrganizationModel, cfAppWallActions, appLocalStorage, appBusyService);
expect($controller).toBeDefined();

var listAllOrgs = mock.cloudFoundryAPI.Organizations.ListAllOrganizations('default');
Expand Down

0 comments on commit b7e658e

Please sign in to comment.