mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Merge pull request #4948 from node-red/pr_4935
Add a new `update available` widget to statusBar
This commit is contained in:
		| @@ -620,6 +620,8 @@ | ||||
|             "pluginCount_plural": "__count__ plugins", | ||||
|             "moduleCount": "__count__ module available", | ||||
|             "moduleCount_plural": "__count__ modules available", | ||||
|             "updateCount": "__count__ update available", | ||||
|             "updateCount_plural": "__count__ updates available", | ||||
|             "inuse": "in use", | ||||
|             "enableall": "enable all", | ||||
|             "disableall": "disable all", | ||||
|   | ||||
| @@ -426,54 +426,62 @@ RED.palette.editor = (function() { | ||||
|     var catalogueCount; | ||||
|     var catalogueLoadStatus = []; | ||||
|     var catalogueLoadStart; | ||||
|     var catalogueLoadErrors = false; | ||||
|  | ||||
|     var activeSort = sortModulesRelevance; | ||||
|  | ||||
|     function handleCatalogResponse(err,catalog,index,v) { | ||||
|         const url = catalog.url | ||||
|         catalogueLoadStatus.push(err||v); | ||||
|         if (!err) { | ||||
|             if (v.modules) { | ||||
|                 v.modules = v.modules.filter(function(m) { | ||||
|                     if (RED.utils.checkModuleAllowed(m.id,m.version,installAllowList,installDenyList)) { | ||||
|                         loadedIndex[m.id] = m; | ||||
|                         m.index = [m.id]; | ||||
|                         if (m.keywords) { | ||||
|                             m.index = m.index.concat(m.keywords); | ||||
|                         } | ||||
|                         if (m.types) { | ||||
|                             m.index = m.index.concat(m.types); | ||||
|                         } | ||||
|                         if (m.updated_at) { | ||||
|                             m.timestamp = new Date(m.updated_at).getTime(); | ||||
|                         } else { | ||||
|                             m.timestamp = 0; | ||||
|                         } | ||||
|                         m.index = m.index.join(",").toLowerCase(); | ||||
|                         m.catalog = catalog; | ||||
|                         m.catalogIndex = index; | ||||
|                         return true; | ||||
|     function refreshCatalogues (done) { | ||||
|         catalogueLoadStatus = []; | ||||
|         catalogueCount = catalogues.length; | ||||
|         loadedList = [] | ||||
|         loadedIndex = {} | ||||
|         loadedCatalogs.length = 0 | ||||
|         let handled = 0 | ||||
|         for (let index = 0; index < catalogues.length; index++) { | ||||
|             const url = catalogues[index]; | ||||
|             $.getJSON(url, {_: new Date().getTime()},function(v) { | ||||
|                 loadedCatalogs.push({ index: index, url: url, name: v.name, updated_at: v.updated_at, modules_count: (v.modules || []).length }) | ||||
|                 handleCatalogResponse({ url: url, name: v.name},index,v); | ||||
|             }).fail(function(jqxhr, textStatus, error) { | ||||
|                 console.warn("Error loading catalog",url,":",error); | ||||
|             }).always(function() { | ||||
|                 handled++; | ||||
|                 if (handled === catalogueCount) { | ||||
|                     //sort loadedCatalogs by e.index ascending | ||||
|                     loadedCatalogs.sort((a, b) => a.index - b.index) | ||||
|                     refreshUpdateStatus(); | ||||
|                     if (done) { | ||||
|                         done() | ||||
|                     } | ||||
|                     return false; | ||||
|                 }) | ||||
|                 loadedList = loadedList.concat(v.modules); | ||||
|             } | ||||
|         } else { | ||||
|             catalogueLoadErrors = true; | ||||
|                 } | ||||
|             }) | ||||
|         } | ||||
|         if (catalogueCount > 1) { | ||||
|             $(".red-ui-palette-module-shade-status").html(RED._('palette.editor.loading')+"<br>"+catalogueLoadStatus.length+"/"+catalogueCount); | ||||
|         } | ||||
|         if (catalogueLoadStatus.length === catalogueCount) { | ||||
|             if (catalogueLoadErrors) { | ||||
|                 RED.notify(RED._('palette.editor.errors.catalogLoadFailed',{url: url}),"error",false,8000); | ||||
|             } | ||||
|             var delta = 250-(Date.now() - catalogueLoadStart); | ||||
|             setTimeout(function() { | ||||
|                 $("#red-ui-palette-module-install-shade").hide(); | ||||
|             },Math.max(delta,0)); | ||||
|     } | ||||
|  | ||||
|     function handleCatalogResponse(catalog,index,v) { | ||||
|         if (v.modules) { | ||||
|             v.modules = v.modules.filter(function(m) { | ||||
|                 if (RED.utils.checkModuleAllowed(m.id,m.version,installAllowList,installDenyList)) { | ||||
|                     loadedIndex[m.id] = m; | ||||
|                     m.index = [m.id]; | ||||
|                     if (m.keywords) { | ||||
|                         m.index = m.index.concat(m.keywords); | ||||
|                     } | ||||
|                     if (m.types) { | ||||
|                         m.index = m.index.concat(m.types); | ||||
|                     } | ||||
|                     if (m.updated_at) { | ||||
|                         m.timestamp = new Date(m.updated_at).getTime(); | ||||
|                     } else { | ||||
|                         m.timestamp = 0; | ||||
|                     } | ||||
|                     m.index = m.index.join(",").toLowerCase(); | ||||
|                     m.catalog = catalog; | ||||
|                     m.catalogIndex = index; | ||||
|                     return true; | ||||
|                 } | ||||
|                 return false; | ||||
|             }) | ||||
|             loadedList = loadedList.concat(v.modules); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -485,35 +493,19 @@ RED.palette.editor = (function() { | ||||
|             packageList.editableList('empty'); | ||||
|  | ||||
|             $(".red-ui-palette-module-shade-status").text(RED._('palette.editor.loading')); | ||||
|  | ||||
|             catalogueLoadStatus = []; | ||||
|             catalogueLoadErrors = false; | ||||
|             catalogueCount = catalogues.length; | ||||
|             if (catalogues.length > 1) { | ||||
|                 $(".red-ui-palette-module-shade-status").html(RED._('palette.editor.loading')+"<br>0/"+catalogues.length); | ||||
|             } | ||||
|             $("#red-ui-palette-module-install-shade").show(); | ||||
|             catalogueLoadStart = Date.now(); | ||||
|             var handled = 0; | ||||
|             loadedCatalogs.length = 0; // clear the loadedCatalogs array | ||||
|             for (let index = 0; index < catalogues.length; index++) { | ||||
|                 const url = catalogues[index]; | ||||
|                 $.getJSON(url, {_: new Date().getTime()},function(v) { | ||||
|                     loadedCatalogs.push({ index: index, url: url, name: v.name, updated_at: v.updated_at, modules_count: (v.modules || []).length }) | ||||
|                     handleCatalogResponse(null,{ url: url, name: v.name},index,v); | ||||
|                     refreshNodeModuleList(); | ||||
|                 }).fail(function(jqxhr, textStatus, error) { | ||||
|                     console.warn("Error loading catalog",url,":",error); | ||||
|                     handleCatalogResponse(jqxhr,url,index); | ||||
|                 }).always(function() { | ||||
|                     handled++; | ||||
|                     if (handled === catalogueCount) { | ||||
|                         //sort loadedCatalogs by e.index ascending | ||||
|                         loadedCatalogs.sort((a, b) => a.index - b.index) | ||||
|                         updateCatalogFilter(loadedCatalogs) | ||||
|                     } | ||||
|                 }) | ||||
|             } | ||||
|             catalogueLoadStart = Date.now() | ||||
|             refreshCatalogues(function () { | ||||
|                 refreshNodeModuleList(); | ||||
|                 updateCatalogFilter(loadedCatalogs) | ||||
|                 const delta = 250-(Date.now() - catalogueLoadStart); | ||||
|                 setTimeout(function() { | ||||
|                     $("#red-ui-palette-module-install-shade").hide(); | ||||
|                 },Math.max(delta,0)); | ||||
|             }) | ||||
|         } else { | ||||
|             refreshNodeModuleList(); | ||||
|             updateCatalogFilter(loadedCatalogs) | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -527,7 +519,6 @@ RED.palette.editor = (function() { | ||||
|         if (catalogSelection.length === 0) { | ||||
|             // sidebar not yet loaded (red-catalogue-filter-select is not in dom) | ||||
|             if (maxRetry > 0) { | ||||
|                // console.log("updateCatalogFilter: sidebar not yet loaded, retrying in 100ms") | ||||
|                 // try again in 100ms | ||||
|                 setTimeout(() => { | ||||
|                     updateCatalogFilter(catalogEntries, maxRetry - 1) | ||||
| @@ -666,12 +657,18 @@ RED.palette.editor = (function() { | ||||
|             } | ||||
|         }) | ||||
|  | ||||
|         // Add the update status to the status bar | ||||
|         addUpdateInfoToStatusBar(); | ||||
|  | ||||
|         refreshCatalogues() | ||||
|  | ||||
|         RED.actions.add("core:manage-palette",function() { | ||||
|                 RED.userSettings.show('palette'); | ||||
|             }); | ||||
|  | ||||
|         RED.events.on('registry:module-updated', function(ns) { | ||||
|             refreshNodeModule(ns.module); | ||||
|             refreshUpdateStatus(); | ||||
|         }); | ||||
|         RED.events.on('registry:node-set-enabled', function(ns) { | ||||
|             refreshNodeModule(ns.module); | ||||
| @@ -683,12 +680,14 @@ RED.palette.editor = (function() { | ||||
|             if (!/^subflow:/.test(nodeType)) { | ||||
|                 var ns = RED.nodes.registry.getNodeSetForType(nodeType); | ||||
|                 refreshNodeModule(ns.module); | ||||
|                 refreshUpdateStatus(); | ||||
|             } | ||||
|         }); | ||||
|         RED.events.on('registry:node-type-removed', function(nodeType) { | ||||
|             if (!/^subflow:/.test(nodeType)) { | ||||
|                 var ns = RED.nodes.registry.getNodeSetForType(nodeType); | ||||
|                 refreshNodeModule(ns.module); | ||||
|                 refreshUpdateStatus(); | ||||
|             } | ||||
|         }); | ||||
|         RED.events.on('registry:node-set-added', function(ns) { | ||||
| @@ -757,6 +756,7 @@ RED.palette.editor = (function() { | ||||
|                 _refreshNodeModule(module); | ||||
|             } | ||||
|  | ||||
|             refreshUpdateStatus(); | ||||
|             for (var i=0;i<filteredList.length;i++) { | ||||
|                 if (filteredList[i].info.id === module) { | ||||
|                     var installButton = filteredList[i].elements.installButton; | ||||
| @@ -1362,6 +1362,7 @@ RED.palette.editor = (function() { | ||||
|                                     if (e) { | ||||
|                                         nodeList.editableList('removeItem', e); | ||||
|                                         delete nodeEntries[entry.name]; | ||||
|                                         refreshUpdateStatus(); | ||||
|                                     } | ||||
|  | ||||
|                                     // We assume that a plugin that implements onremove | ||||
| @@ -1500,6 +1501,66 @@ RED.palette.editor = (function() { | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     const updateStatusWidget = $('<button type="button" class="red-ui-footer-button red-ui-update-status"></button>'); | ||||
|     let updateAvailable = []; | ||||
|  | ||||
|     function addUpdateInfoToStatusBar() { | ||||
|         updateStatusWidget.on("click", function (evt) { | ||||
|             RED.actions.invoke("core:manage-palette", { | ||||
|                 view: "nodes", | ||||
|                 filter: '"' + updateAvailable.join('", "') + '"' | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         RED.popover.tooltip(updateStatusWidget, function () { | ||||
|             const count = updateAvailable.length || 0; | ||||
|             return RED._("palette.editor.updateCount", { count: count }); | ||||
|         }); | ||||
|  | ||||
|         RED.statusBar.add({ | ||||
|             id: "update", | ||||
|             align: "right", | ||||
|             element: updateStatusWidget | ||||
|         }); | ||||
|  | ||||
|         updateStatus({ count: 0 }); | ||||
|     } | ||||
|  | ||||
|     let pendingRefreshTimeout | ||||
|     function refreshUpdateStatus() { | ||||
|         clearTimeout(pendingRefreshTimeout) | ||||
|         pendingRefreshTimeout = setTimeout(() => { | ||||
|             updateAvailable = []; | ||||
|             for (const module of Object.keys(nodeEntries)) { | ||||
|                 if (loadedIndex.hasOwnProperty(module)) { | ||||
|                     const moduleInfo = nodeEntries[module].info; | ||||
|                     if (moduleInfo.pending_version) { | ||||
|                         // Module updated | ||||
|                         continue; | ||||
|                     } | ||||
|                     if (updateAllowed && | ||||
|                         semVerCompare(loadedIndex[module].version, moduleInfo.version) > 0 && | ||||
|                         RED.utils.checkModuleAllowed(module, null, updateAllowList, updateDenyList) | ||||
|                     ) { | ||||
|                         updateAvailable.push(module); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             updateStatus({ count: updateAvailable.length }); | ||||
|         }, 200) | ||||
|     } | ||||
|  | ||||
|     function updateStatus(opts) { | ||||
|         if (opts.count) { | ||||
|             RED.statusBar.show("update"); | ||||
|             updateStatusWidget.empty(); | ||||
|             $('<span><i class="fa fa-cube"></i> ' + opts.count + '</span>').appendTo(updateStatusWidget); | ||||
|         } else { | ||||
|             RED.statusBar.hide("update"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: init, | ||||
|         install: install | ||||
|   | ||||
| @@ -33,6 +33,7 @@ RED.statusBar = (function() { | ||||
|         var el = $('<span class="red-ui-statusbar-widget"></span>'); | ||||
|         el.prop('id', options.id); | ||||
|         options.element.appendTo(el); | ||||
|         options.elementDiv = el; | ||||
|         if (options.align === 'left') { | ||||
|             leftBucket.append(el); | ||||
|         } else if (options.align === 'right') { | ||||
| @@ -40,12 +41,30 @@ RED.statusBar = (function() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function hideWidget(id) { | ||||
|         const widget = widgets[id]; | ||||
|  | ||||
|         if (widget && widget.elementDiv) { | ||||
|             widget.elementDiv.hide(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function showWidget(id) { | ||||
|         const widget = widgets[id]; | ||||
|  | ||||
|         if (widget && widget.elementDiv) { | ||||
|             widget.elementDiv.show(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: function() { | ||||
|             leftBucket = $('<span class="red-ui-statusbar-bucket red-ui-statusbar-bucket-left">').appendTo("#red-ui-workspace-footer"); | ||||
|             rightBucket = $('<span class="red-ui-statusbar-bucket red-ui-statusbar-bucket-right">').appendTo("#red-ui-workspace-footer"); | ||||
|         }, | ||||
|         add: addWidget | ||||
|         add: addWidget, | ||||
|         hide: hideWidget, | ||||
|         show: showWidget | ||||
|     } | ||||
|  | ||||
| })(); | ||||
|   | ||||
| @@ -305,3 +305,8 @@ button.red-ui-palette-editor-upload-button { | ||||
|         margin-left: 10px; | ||||
|     } | ||||
| } | ||||
|  | ||||
| button.red-ui-update-status { | ||||
|     width: auto; | ||||
|     padding: 0 3px; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user