[SEBA-747] Upgraded loader to show real-time progress istead of using a time-out

Change-Id: Iafea32e3b15461dc8b6859f0e4b1813150e362d5
diff --git a/src/app/core/loader/loader.spec.ts b/src/app/core/loader/loader.spec.ts
index 0744c11..1a2d553 100644
--- a/src/app/core/loader/loader.spec.ts
+++ b/src/app/core/loader/loader.spec.ts
@@ -29,6 +29,7 @@
 
 const MockDiscover = {
   areModelsLoaded: () => loaded,
+  getStatusMessage: () => 'Test Message',
   discover: null
 };
 
diff --git a/src/app/core/loader/loader.ts b/src/app/core/loader/loader.ts
index 3606ca8..c8def42 100644
--- a/src/app/core/loader/loader.ts
+++ b/src/app/core/loader/loader.ts
@@ -27,6 +27,7 @@
     '$rootScope',
     '$location',
     '$timeout',
+    '$interval',
     '$state',
     'AuthService',
     'XosConfig',
@@ -35,20 +36,24 @@
   ];
 
   public loader: boolean = true;
+  public message: string = 'Loading data...';
   public error: string;
 
+  private getMessageInterval: ng.IPromise<any>;
+
   constructor (
     private $log: ng.ILogService,
     private $rootScope: ng.IScope,
     private $location: ng.ILocationService,
     private $timeout: ng.ITimeoutService,
+    private $interval: ng.IIntervalService,
     private $state: ng.ui.IStateService,
     private XosAuthService: IXosAuthService,
     private XosConfig: any,
     private XosModelDiscoverer: IXosModelDiscovererService,
     private XosOnboarder: IXosOnboarder
   ) {
-
+    this.getMessage();
     this.run();
   }
 
@@ -119,11 +124,34 @@
         break;
     }
   }
+
+  /**
+   * This method query the model-discoverer service to have the status of the loading
+   */
+  public getMessage() {
+    this.getMessageInterval = this.$interval(() => {
+      this.message = this.XosModelDiscoverer.getStatusMessage();
+    }, 1000);
+    this.message = this.XosModelDiscoverer.getStatusMessage();
+  }
+
+  $onDestroy() {
+    this.$interval.cancel(this.getMessageInterval);
+  }
 }
 
 export const xosLoader: angular.IComponentOptions = {
   template: `
-    <div ng-show="vm.loader" class="loader"></div>
+    <div class="loader-container">
+      <div ng-show="vm.loader" class="loader"></div>
+    </div>
+    <div class="row">
+      <div class="col-sm-6 col-sm-offset-3">
+        <div class="alert alert-accent">
+          {{ vm.message }}
+        </div>
+      </div>
+    </div>
     <div class="row" ng-show="vm.error == 'chameleon'">
       <div class="col-sm-6 col-sm-offset-3">
         <div class="alert alert-danger">
diff --git a/src/app/datasources/helpers/model-discoverer.service.ts b/src/app/datasources/helpers/model-discoverer.service.ts
index 3ee5a15..2e5597e 100644
--- a/src/app/datasources/helpers/model-discoverer.service.ts
+++ b/src/app/datasources/helpers/model-discoverer.service.ts
@@ -47,12 +47,14 @@
   discover(): ng.IPromise<string>;
   getApiUrlFromModel(model: IXosModel): string;
   areModelsLoaded(): boolean;
+  getStatusMessage(): string;
 }
 
 export class XosModelDiscovererService implements IXosModelDiscovererService {
   static $inject = [
     '$log',
     '$q',
+    '$interval',
     'XosModelDefs',
     'ConfigHelpers',
     'XosRuntimeStates',
@@ -66,10 +68,12 @@
   private xosServices: string[] = []; // list of loaded services
   private progressBar;
   private modelsLoaded: boolean = false;
+  private statusMessage: string = 'Loading models definition';
 
   constructor (
     private $log: ng.ILogService,
     private $q: ng.IQService,
+    private $interval: ng.IIntervalService,
     private XosModelDefs: IXosModeldefsService,
     private ConfigHelpers: IXosConfigHelpersService,
     private XosRuntimeStates: IXosRuntimeStatesService,
@@ -97,13 +101,35 @@
     }
   }
 
+  public getStatusMessage(): string {
+    return this.statusMessage;
+  }
+
   public discover() {
     const d = this.$q.defer();
-    this.progressBar.start();
+    // loading stats
+    let loadingSince = 0;
+    const modelDefInerval = this.$interval(() => {
+      loadingSince += 1;
+      this.setModelDefTimeMsg(loadingSince);
+    }, 1000);
+    this.progressBar.set(1);
+
+    // start loading data
     this.XosModelDefs.get()
       .then((modelsDef: IXosModeldef[]) => {
-        // TODO store modeldefs and add a method to retrieve the model definition from the name
         const pArray = [];
+
+        // Setting up counters for the status message
+        this.$interval.cancel(modelDefInerval);
+        const modelsTotal = modelsDef.length;
+        let modelsLoaded = 0;
+        this.setModelsCountMsg(modelsLoaded, modelsTotal);
+
+        // Setting up counters for the loading bar
+        this.progressBar.set(10);
+        const progressBarStep = 90 / modelsTotal;
+
         _.forEach(modelsDef, (model: IXosModeldef) => {
           this.$log.debug(`[XosModelDiscovererService] Loading: ${model.name}`);
           let p = this.cacheModelEntries(model)
@@ -123,6 +149,13 @@
               return this.storeModel(model);
             })
             .then(model => {
+              // Updating the status message
+              modelsLoaded = modelsLoaded + 1;
+              this.setModelsCountMsg(modelsLoaded, modelsTotal);
+
+              // Updating the progress bar
+              this.progressBar.set(10 + (modelsLoaded * progressBarStep));
+
               this.$log.debug(`[XosModelDiscovererService] Model ${model.name} stored`);
               return this.$q.resolve('true');
             })
@@ -167,6 +200,15 @@
     return d.promise;
   }
 
+  private setModelDefTimeMsg(seconds: number) {
+    this.statusMessage = `Loading models definition for ${seconds} seconds...`;
+  }
+
+  private setModelsCountMsg(loaded: number, modelsTotal: number) {
+    const percent = Math.round((100 * loaded) / modelsTotal);
+    this.statusMessage = `Loading data.... ${percent}% completed (${loaded} of ${modelsTotal} models)`;
+  }
+
   private stateNameFromModel(model: IXosModel): string {
     return `xos.${this.XosModeldefsCache.serviceNameFromAppName(model.app)}.${model.name.toLowerCase()}`;
   }
diff --git a/src/app/datasources/helpers/model.discoverer.service.spec.ts b/src/app/datasources/helpers/model.discoverer.service.spec.ts
index 5deb444..bcdec0d 100644
--- a/src/app/datasources/helpers/model.discoverer.service.spec.ts
+++ b/src/app/datasources/helpers/model.discoverer.service.spec.ts
@@ -85,7 +85,7 @@
 };
 const MockProgressBar = {
   setColor: jasmine.createSpy('progressBar.setColor'),
-  start: jasmine.createSpy('progressBar.start'),
+  set: jasmine.createSpy('progressBar.set'),
   complete: jasmine.createSpy('progressBar.complete')
 };
 const MockngProgressFactory = {
@@ -279,7 +279,7 @@
     it('should call all the function chain', (done) => {
       service.discover()
         .then((res) => {
-          expect(MockProgressBar.start).toHaveBeenCalled();
+          expect(MockProgressBar.set).toHaveBeenCalled();
           expect(MockXosModelDefs.get).toHaveBeenCalled();
           expect(service['cacheModelEntries'].calls.count()).toBe(2);
           expect(service['addState'].calls.count()).toBe(2);
diff --git a/src/app/datasources/rest/modeldefs.rest.ts b/src/app/datasources/rest/modeldefs.rest.ts
index 078eede..c354afe 100644
--- a/src/app/datasources/rest/modeldefs.rest.ts
+++ b/src/app/datasources/rest/modeldefs.rest.ts
@@ -54,7 +54,7 @@
 
   public get(): IPromise<IXosModeldef[]> {
     const d = this.$q.defer();
-    this.$http.get(`${this.AppConfig.apiEndpoint}/modeldefs`, {timeout: 10 * 1000})
+    this.$http.get(`${this.AppConfig.apiEndpoint}/modeldefs`)
       .then((res: any) => {
         d.resolve(res.data.items);
       })
diff --git a/src/app/style/imports/loader.scss b/src/app/style/imports/loader.scss
index a04b17f..703b910 100644
--- a/src/app/style/imports/loader.scss
+++ b/src/app/style/imports/loader.scss
@@ -18,6 +18,10 @@
 
 @import '../vars';
 
+.loader-container {
+  height: 9em !important;
+}
+
 .loader,
 .loader:before,
 .loader:after {
diff --git a/src/index.html b/src/index.html
index 0eba668..ea0c97a 100644
--- a/src/index.html
+++ b/src/index.html
@@ -31,12 +31,12 @@
   </head>
 
   <body class="{{class}}">
-    <ui-view></ui-view>
     <div ng-show="::false">
       <div class="loader">
         Loading
       </div>
     </div>
+    <ui-view></ui-view>
   </body>
   <script type="text/javascript" src="./app.config.js"></script>
   <script type="text/javascript" src="./style.config.js"></script>
diff --git a/src/index.scss b/src/index.scss
index 561a741..fd2ecf7 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -74,6 +74,10 @@
   @include alert-variant($background-dark-color, $alert-danger-text, $alert-danger-border);
 }
 
+.alert-accent {
+  @include alert-variant($background-dark-color, $color-accent, #fff);
+}
+
 .modal-backdrop.in {
   filter: alpha(opacity=50);
   opacity: .5;