浏览代码

[core] Improved assist loading experience

This commit generally improves the feedback given to the user when the assist panel is busy. Some spinners are fixed and some new ones are introduced.
Johan Ahlen 10 年之前
父节点
当前提交
d058c2f
共有 1 个文件被更改,包括 114 次插入96 次删除
  1. 114 96
      desktop/core/src/desktop/templates/ko_components.mako

+ 114 - 96
desktop/core/src/desktop/templates/ko_components.mako

@@ -135,7 +135,7 @@ from desktop.views import _ko
   </script>
   </script>
 
 
   <script type="text/html" id="assist-no-entries">
   <script type="text/html" id="assist-no-entries">
-    <ul>
+    <ul class="assist-tables">
       <li data-bind="visible: definition.isDatabase">
       <li data-bind="visible: definition.isDatabase">
         <span>${_('The selected database has no tables.')}</span>
         <span>${_('The selected database has no tables.')}</span>
       </li>
       </li>
@@ -153,7 +153,7 @@ from desktop.views import _ko
   </script>
   </script>
 
 
   <script type="text/html" id="assist-entries">
   <script type="text/html" id="assist-entries">
-    <div class="nav-header center" data-bind="visible: loading">
+    <div class="center" data-bind="visible: loading">
       <!--[if !IE]><!--><i class="fa fa-spinner fa-spin" style="font-size: 20px; color: #BBB"></i><!--<![endif]-->
       <!--[if !IE]><!--><i class="fa fa-spinner fa-spin" style="font-size: 20px; color: #BBB"></i><!--<![endif]-->
       <!--[if IE]><img src="${ static('desktop/art/spinner.gif') }"/><![endif]-->
       <!--[if IE]><img src="${ static('desktop/art/spinner.gif') }"/><![endif]-->
     </div>
     </div>
@@ -164,47 +164,51 @@ from desktop.views import _ko
         <a class="assist-column-link" data-bind="click: toggleOpen, attr: {'title': definition.title }, css: { 'assist-field-link': ! definition.isTable, 'assist-table-link': definition.isTable }" href="javascript:void(0)">
         <a class="assist-column-link" data-bind="click: toggleOpen, attr: {'title': definition.title }, css: { 'assist-field-link': ! definition.isTable, 'assist-table-link': definition.isTable }" href="javascript:void(0)">
           <span data-bind="text: definition.displayName, event: { 'dblclick': dblClick }"></span>
           <span data-bind="text: definition.displayName, event: { 'dblclick': dblClick }"></span>
         </a>
         </a>
-        <!-- ko template: { if: open() && ! hasEntries() && ! loading(), name: 'assist-no-entries' } --><!-- /ko -->
-        <!-- ko template: { if: open() && hasEntries() && ! loading(), name: 'assist-entries'  } --><!-- /ko -->
+        <!-- ko template: { if: open(), name: 'assist-entries'  } --><!-- /ko -->
       </li>
       </li>
     </ul>
     </ul>
+    <!-- ko template: { if: ! hasEntries() && ! loading(), name: 'assist-no-entries' } --><!-- /ko -->
   </script>
   </script>
 
 
   <script type="text/html" id="assist-panel-template">
   <script type="text/html" id="assist-panel-template">
     <div class="reveals-actions" style="position: relative; width:100%">
     <div class="reveals-actions" style="position: relative; width:100%">
       <ul class="nav nav-list" style="position:relative; border: none; padding: 0; background-color: #FFF; margin-bottom: 1px; width:100%;">
       <ul class="nav nav-list" style="position:relative; border: none; padding: 0; background-color: #FFF; margin-bottom: 1px; width:100%;">
-        <li class="nav-header">${_('database')}
+        <li class="nav-header">
+          ${_('database')}
           <div class="pull-right" data-bind="css: { 'hover-actions' : ! reloading() }">
           <div class="pull-right" data-bind="css: { 'hover-actions' : ! reloading() }">
             <a href="javascript:void(0)" data-bind="click: reloadAssist"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin' : reloading }" title="${_('Manually refresh the table list')}"></i></a>
             <a href="javascript:void(0)" data-bind="click: reloadAssist"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin' : reloading }" title="${_('Manually refresh the table list')}"></i></a>
           </div>
           </div>
         </li>
         </li>
-        <li data-bind="visible: ! hasErrors()" >
+
+        <li data-bind="visible: ! hasErrors() && ! loadingDatabases()" >
           <select data-bind="options: availableDatabaseNames, select2: { width: '100%', placeholder: '${ _ko("Choose a database...") }', update: assistHelper.activeDatabase }" class="input-medium" data-placeholder="${_('Choose a database...')}"></select>
           <select data-bind="options: availableDatabaseNames, select2: { width: '100%', placeholder: '${ _ko("Choose a database...") }', update: assistHelper.activeDatabase }" class="input-medium" data-placeholder="${_('Choose a database...')}"></select>
         </li>
         </li>
-        <li data-bind="visible: hasErrors">
-          <span>${ _('The database list cannot be loaded.') }</span>
-        </li>
 
 
-        <li class="nav-header center" data-bind="visible: loadingTables">
+        <li class="center" data-bind="visible: loadingDatabases" >
           <!--[if !IE]><!--><i class="fa fa-spinner fa-spin" style="font-size: 20px; color: #BBB"></i><!--<![endif]-->
           <!--[if !IE]><!--><i class="fa fa-spinner fa-spin" style="font-size: 20px; color: #BBB"></i><!--<![endif]-->
           <!--[if IE]><img src="${ static('desktop/art/spinner.gif') }"/><![endif]-->
           <!--[if IE]><img src="${ static('desktop/art/spinner.gif') }"/><![endif]-->
         </li>
         </li>
 
 
-        <!-- ko if: selectedDatabase() != null && ! loadingTables() -->
-        <li class="nav-header" style="margin-top:10px;">${_('tables')}
+        <li data-bind="visible: hasErrors">
+          <span>${ _('The database list cannot be loaded.') }</span>
+        </li>
+
+        <li class="nav-header" style="margin-top:10px;" data-bind="visible: ! loadingDatabases()">
+          ${_('tables')}
           <div class="pull-right" data-bind="visible: selectedDatabase() != null && selectedDatabase().hasEntries(), css: { 'hover-actions': ! filter(), 'blue': filter }">
           <div class="pull-right" data-bind="visible: selectedDatabase() != null && selectedDatabase().hasEntries(), css: { 'hover-actions': ! filter(), 'blue': filter }">
             <a href="javascript:void(0)" data-bind="click: toggleSearch"><i class="pointer fa fa-search" title="${_('Search')}"></i></a>
             <a href="javascript:void(0)" data-bind="click: toggleSearch"><i class="pointer fa fa-search" title="${_('Search')}"></i></a>
           </div>
           </div>
         </li>
         </li>
 
 
-        <li data-bind="slideVisible: selectedDatabase() != null && selectedDatabase().hasEntries() && options.isSearchVisible()">
-          <div><input type="text" placeholder="${ _('Table name...') }" style="width:90%;" data-bind="value: filter, valueUpdate: 'afterkeydown'"/></div>
-        </li>
-        <!-- /ko -->
+        <!-- ko if: selectedDatabase() != null -->
+          <li data-bind="slideVisible: selectedDatabase() != null && selectedDatabase().hasEntries() && options.isSearchVisible()">
+            <div><input type="text" placeholder="${ _('Table name...') }" style="width:90%;" data-bind="value: filter, valueUpdate: 'afterkeydown'"/></div>
+          </li>
 
 
         <div class="table-container">
         <div class="table-container">
-        <!-- ko template: { if: selectedDatabase() != null && ! loadingTables(), name: 'assist-entries', data: selectedDatabase } --><!-- /ko -->
-        </div>
+          <!-- ko template: { if: selectedDatabase() != null, name: 'assist-entries', data: selectedDatabase } --><!-- /ko -->
+          </div>
+        <!-- /ko -->
       </ul>
       </ul>
     </div>
     </div>
 
 
@@ -259,6 +263,7 @@ from desktop.views import _ko
         self.parent = parent;
         self.parent = parent;
         self.filter = filter;
         self.filter = filter;
 
 
+        self.loaded = false;
         self.loading = ko.observable(false);
         self.loading = ko.observable(false);
         self.open = ko.observable(false);
         self.open = ko.observable(false);
         self.entries = ko.observableArray([]);
         self.entries = ko.observableArray([]);
@@ -294,82 +299,86 @@ from desktop.views import _ko
           return;
           return;
         }
         }
         self.loading(true);
         self.loading(true);
+        self.entries([]);
 
 
-        self.assistPanel.assistHelper.fetchPanelData(self.getHierarchy(), function(data) {
-          if (typeof data.tables !== "undefined") {
-            self.entries($.map(data.tables, function(tableName) {
-              return self.createEntry({
-                name: tableName,
-                displayName: tableName,
-                title: tableName,
-                isTable: true
-              });
-            }));
-          } else if (typeof data.extended_columns !== "undefined" && data.extended_columns !== null) {
-            self.entries($.map(data.extended_columns, function (columnDef) {
-              var displayName = columnDef.name;
-              if (typeof columnDef.type !== "undefined" && columnDef.type !== null) {
-                displayName += ' (' + columnDef.type + ')'
-              }
-              var title = displayName;
-              if (typeof columnDef.comment !== "undefined" && columnDef.comment !== null) {
-                title += ' ' + columnDef.comment;
-              }
-              return self.createEntry({
-                name: columnDef.name,
-                displayName: displayName,
-                title: title,
-                isColumn: true
-              });
-            }));
-          } else if (typeof data.columns !== "undefined" && data.columns !== null) {
-            self.entries($.map(data.columns, function(columnName) {
-              return self.createEntry({
-                name: columnName,
-                displayName: columnName,
-                title: columnName,
-                isColumn: true
-              });
-            }));
-          } else if (typeof data.type !== "undefined" && data.type !== null) {
-            if (data.type === "map") {
-              self.entries([
-                self.createEntry({
-                  name: "key",
-                  displayName: "key (" + data.key.type + ")",
-                  title: "key (" + data.key.type + ")"
-                }),
-                self.createEntry({
-                  name: "value",
-                  displayName: "value (" + data.value.type + ")",
-                  title: "value (" + data.value.type + ")",
-                  isMapValue: true
-                })
-              ]);
-            } else if (data.type == "struct") {
-              self.entries($.map(data.fields, function(field) {
+        // Defer this part to allow ko to react on empty entries and loading
+        window.setTimeout(function() {
+          self.assistPanel.assistHelper.fetchPanelData(self.getHierarchy(), function(data) {
+            if (typeof data.tables !== "undefined") {
+              self.entries($.map(data.tables, function(tableName) {
                 return self.createEntry({
                 return self.createEntry({
-                  name: field.name,
-                  displayName: field.name + " (" + field.type + ")",
-                  title: field.name + " (" + field.type + ")"
+                  name: tableName,
+                  displayName: tableName,
+                  title: tableName,
+                  isTable: true
                 });
                 });
               }));
               }));
-            } else if (data.type == "array") {
-              self.entries([
-                self.createEntry({
-                  name: "item",
-                  displayName: "item (" + data.item.type + ")",
-                  title: "item (" + data.item.type + ")",
-                  isArray: true
-                })
-              ]);
+            } else if (typeof data.extended_columns !== "undefined" && data.extended_columns !== null) {
+              self.entries($.map(data.extended_columns, function (columnDef) {
+                var displayName = columnDef.name;
+                if (typeof columnDef.type !== "undefined" && columnDef.type !== null) {
+                  displayName += ' (' + columnDef.type + ')'
+                }
+                var title = displayName;
+                if (typeof columnDef.comment !== "undefined" && columnDef.comment !== null) {
+                  title += ' ' + columnDef.comment;
+                }
+                return self.createEntry({
+                  name: columnDef.name,
+                  displayName: displayName,
+                  title: title,
+                  isColumn: true
+                });
+              }));
+            } else if (typeof data.columns !== "undefined" && data.columns !== null) {
+              self.entries($.map(data.columns, function(columnName) {
+                return self.createEntry({
+                  name: columnName,
+                  displayName: columnName,
+                  title: columnName,
+                  isColumn: true
+                });
+              }));
+            } else if (typeof data.type !== "undefined" && data.type !== null) {
+              if (data.type === "map") {
+                self.entries([
+                  self.createEntry({
+                    name: "key",
+                    displayName: "key (" + data.key.type + ")",
+                    title: "key (" + data.key.type + ")"
+                  }),
+                  self.createEntry({
+                    name: "value",
+                    displayName: "value (" + data.value.type + ")",
+                    title: "value (" + data.value.type + ")",
+                    isMapValue: true
+                  })
+                ]);
+              } else if (data.type == "struct") {
+                self.entries($.map(data.fields, function(field) {
+                  return self.createEntry({
+                    name: field.name,
+                    displayName: field.name + " (" + field.type + ")",
+                    title: field.name + " (" + field.type + ")"
+                  });
+                }));
+              } else if (data.type == "array") {
+                self.entries([
+                  self.createEntry({
+                    name: "item",
+                    displayName: "item (" + data.item.type + ")",
+                    title: "item (" + data.item.type + ")",
+                    isArray: true
+                  })
+                ]);
+              }
             }
             }
-          }
-          self.loading(false);
-        }, function() {
-          self.assistPanel.hasErrors(true);
-          self.loading(false);
-        })
+            self.loading(false);
+          }, function() {
+            self.assistPanel.hasErrors(true);
+            self.loading(false);
+          });
+        }, 10);
       };
       };
 
 
       AssistEntry.prototype.createEntry = function(definition) {
       AssistEntry.prototype.createEntry = function(definition) {
@@ -586,7 +595,6 @@ from desktop.views import _ko
         var self = this;
         var self = this;
 
 
         self.hasErrors = ko.observable(false);
         self.hasErrors = ko.observable(false);
-        self.reloading = ko.observable(false);
         self.simpleStyles = ko.observable(false);
         self.simpleStyles = ko.observable(false);
 
 
         self.filter = ko.observable("").extend({ rateLimit: 150 });
         self.filter = ko.observable("").extend({ rateLimit: 150 });
@@ -614,13 +622,16 @@ from desktop.views import _ko
         self.databases = ko.observableArray();
         self.databases = ko.observableArray();
         self.selectedDatabase = ko.observable();
         self.selectedDatabase = ko.observable();
 
 
+        self.reloading = ko.observable(false);
+        self.loadingDatabases = ko.observable(true);
+
         self.loadingTables = ko.computed(function() {
         self.loadingTables = ko.computed(function() {
-          return  ! (self.selectedDatabase() && !self.selectedDatabase().loading());
+          return typeof self.selectedDatabase() != "undefined" && self.selectedDatabase() !== null && self.selectedDatabase().loading();
         });
         });
 
 
         self.selectedDatabase.subscribe(function(newValue) {
         self.selectedDatabase.subscribe(function(newValue) {
-          if (newValue != null) {
-            newValue.loadEntries();
+          if (newValue != null && !newValue.hasEntries() && !newValue.loading()) {
+              newValue.loadEntries()
           }
           }
         });
         });
 
 
@@ -628,10 +639,14 @@ from desktop.views import _ko
         self.availableDatabaseNames = ko.observableArray();
         self.availableDatabaseNames = ko.observableArray();
 
 
         self.fetchDatabases(function() {
         self.fetchDatabases(function() {
-          self.assistHelper.activeDatabase.subscribe(setDatabase);
+          self.assistHelper.activeDatabase.subscribe(function(newValue) {
+            window.setTimeout(function() {
+              self.setDatabase(newValue);
+            }, 10);
+          });
 
 
           if ($.inArray(self.assistHelper.activeDatabase(), self.availableDatabaseNames()) > -1) {
           if ($.inArray(self.assistHelper.activeDatabase(), self.availableDatabaseNames()) > -1) {
-            setDatabase(self.assistHelper.activeDatabase());
+            self.setDatabase(self.assistHelper.activeDatabase());
             return;
             return;
           }
           }
           if ($.inArray(self.options.lastSelectedDb(), self.availableDatabaseNames()) > -1) {
           if ($.inArray(self.options.lastSelectedDb(), self.availableDatabaseNames()) > -1) {
@@ -658,7 +673,7 @@ from desktop.views import _ko
             var newTop = targetElement.offset().top - $(window).scrollTop();
             var newTop = targetElement.offset().top - $(window).scrollTop();
             if (targetElement != null && (lastOffset.left != targetElement.offset().left || lastOffset.top != newTop)) {
             if (targetElement != null && (lastOffset.left != targetElement.offset().left || lastOffset.top != newTop)) {
               lastOffset.left = targetElement.offset().left;
               lastOffset.left = targetElement.offset().left;
-              lastOffset.top = newTop
+              lastOffset.top = newTop;
               var newCssTop = lastOffset.top - $tableAnalysis.outerHeight() / 2 + targetElement.outerHeight() / 2;
               var newCssTop = lastOffset.top - $tableAnalysis.outerHeight() / 2 + targetElement.outerHeight() / 2;
               $tableAnalysis.css("top", newCssTop).css("left", lastOffset.left + targetElement.outerWidth());
               $tableAnalysis.css("top", newCssTop).css("left", lastOffset.left + targetElement.outerWidth());
               if ((newCssTop + $tableAnalysis.outerHeight() / 2) < 70) {
               if ((newCssTop + $tableAnalysis.outerHeight() / 2) < 70) {
@@ -701,6 +716,7 @@ from desktop.views import _ko
       AssistPanel.prototype.reloadAssist = function() {
       AssistPanel.prototype.reloadAssist = function() {
         var self = this;
         var self = this;
         self.reloading(true);
         self.reloading(true);
+        self.loadingDatabases(true);
         self.selectedDatabase(null);
         self.selectedDatabase(null);
         self.assistHelper.clearCache();
         self.assistHelper.clearCache();
         self.fetchDatabases(function() {
         self.fetchDatabases(function() {
@@ -723,10 +739,12 @@ from desktop.views import _ko
           self.availableDatabaseNames(data.databases);
           self.availableDatabaseNames(data.databases);
 
 
           self.reloading(false);
           self.reloading(false);
+          self.loadingDatabases(false);
           if (callback) {
           if (callback) {
             callback();
             callback();
           }
           }
         }, function() {
         }, function() {
+          self.loadingDatabases(false);
           self.reloading(false);
           self.reloading(false);
           self.hasErrors(true);
           self.hasErrors(true);
         });
         });