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:
commit
1d9586721e
@ -620,6 +620,8 @@
|
|||||||
"pluginCount_plural": "__count__ plugins",
|
"pluginCount_plural": "__count__ plugins",
|
||||||
"moduleCount": "__count__ module available",
|
"moduleCount": "__count__ module available",
|
||||||
"moduleCount_plural": "__count__ modules available",
|
"moduleCount_plural": "__count__ modules available",
|
||||||
|
"updateCount": "__count__ update available",
|
||||||
|
"updateCount_plural": "__count__ updates available",
|
||||||
"inuse": "in use",
|
"inuse": "in use",
|
||||||
"enableall": "enable all",
|
"enableall": "enable all",
|
||||||
"disableall": "disable all",
|
"disableall": "disable all",
|
||||||
|
@ -426,14 +426,38 @@ RED.palette.editor = (function() {
|
|||||||
var catalogueCount;
|
var catalogueCount;
|
||||||
var catalogueLoadStatus = [];
|
var catalogueLoadStatus = [];
|
||||||
var catalogueLoadStart;
|
var catalogueLoadStart;
|
||||||
var catalogueLoadErrors = false;
|
|
||||||
|
|
||||||
var activeSort = sortModulesRelevance;
|
var activeSort = sortModulesRelevance;
|
||||||
|
|
||||||
function handleCatalogResponse(err,catalog,index,v) {
|
function refreshCatalogues (done) {
|
||||||
const url = catalog.url
|
catalogueLoadStatus = [];
|
||||||
catalogueLoadStatus.push(err||v);
|
catalogueCount = catalogues.length;
|
||||||
if (!err) {
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCatalogResponse(catalog,index,v) {
|
||||||
if (v.modules) {
|
if (v.modules) {
|
||||||
v.modules = v.modules.filter(function(m) {
|
v.modules = v.modules.filter(function(m) {
|
||||||
if (RED.utils.checkModuleAllowed(m.id,m.version,installAllowList,installDenyList)) {
|
if (RED.utils.checkModuleAllowed(m.id,m.version,installAllowList,installDenyList)) {
|
||||||
@ -459,22 +483,6 @@ RED.palette.editor = (function() {
|
|||||||
})
|
})
|
||||||
loadedList = loadedList.concat(v.modules);
|
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 initInstallTab() {
|
function initInstallTab() {
|
||||||
@ -485,35 +493,19 @@ RED.palette.editor = (function() {
|
|||||||
packageList.editableList('empty');
|
packageList.editableList('empty');
|
||||||
|
|
||||||
$(".red-ui-palette-module-shade-status").text(RED._('palette.editor.loading'));
|
$(".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();
|
$("#red-ui-palette-module-install-shade").show();
|
||||||
catalogueLoadStart = Date.now();
|
catalogueLoadStart = Date.now()
|
||||||
var handled = 0;
|
refreshCatalogues(function () {
|
||||||
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();
|
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)
|
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) {
|
if (catalogSelection.length === 0) {
|
||||||
// sidebar not yet loaded (red-catalogue-filter-select is not in dom)
|
// sidebar not yet loaded (red-catalogue-filter-select is not in dom)
|
||||||
if (maxRetry > 0) {
|
if (maxRetry > 0) {
|
||||||
// console.log("updateCatalogFilter: sidebar not yet loaded, retrying in 100ms")
|
|
||||||
// try again in 100ms
|
// try again in 100ms
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
updateCatalogFilter(catalogEntries, maxRetry - 1)
|
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.actions.add("core:manage-palette",function() {
|
||||||
RED.userSettings.show('palette');
|
RED.userSettings.show('palette');
|
||||||
});
|
});
|
||||||
|
|
||||||
RED.events.on('registry:module-updated', function(ns) {
|
RED.events.on('registry:module-updated', function(ns) {
|
||||||
refreshNodeModule(ns.module);
|
refreshNodeModule(ns.module);
|
||||||
|
refreshUpdateStatus();
|
||||||
});
|
});
|
||||||
RED.events.on('registry:node-set-enabled', function(ns) {
|
RED.events.on('registry:node-set-enabled', function(ns) {
|
||||||
refreshNodeModule(ns.module);
|
refreshNodeModule(ns.module);
|
||||||
@ -683,12 +680,14 @@ RED.palette.editor = (function() {
|
|||||||
if (!/^subflow:/.test(nodeType)) {
|
if (!/^subflow:/.test(nodeType)) {
|
||||||
var ns = RED.nodes.registry.getNodeSetForType(nodeType);
|
var ns = RED.nodes.registry.getNodeSetForType(nodeType);
|
||||||
refreshNodeModule(ns.module);
|
refreshNodeModule(ns.module);
|
||||||
|
refreshUpdateStatus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
RED.events.on('registry:node-type-removed', function(nodeType) {
|
RED.events.on('registry:node-type-removed', function(nodeType) {
|
||||||
if (!/^subflow:/.test(nodeType)) {
|
if (!/^subflow:/.test(nodeType)) {
|
||||||
var ns = RED.nodes.registry.getNodeSetForType(nodeType);
|
var ns = RED.nodes.registry.getNodeSetForType(nodeType);
|
||||||
refreshNodeModule(ns.module);
|
refreshNodeModule(ns.module);
|
||||||
|
refreshUpdateStatus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
RED.events.on('registry:node-set-added', function(ns) {
|
RED.events.on('registry:node-set-added', function(ns) {
|
||||||
@ -757,6 +756,7 @@ RED.palette.editor = (function() {
|
|||||||
_refreshNodeModule(module);
|
_refreshNodeModule(module);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshUpdateStatus();
|
||||||
for (var i=0;i<filteredList.length;i++) {
|
for (var i=0;i<filteredList.length;i++) {
|
||||||
if (filteredList[i].info.id === module) {
|
if (filteredList[i].info.id === module) {
|
||||||
var installButton = filteredList[i].elements.installButton;
|
var installButton = filteredList[i].elements.installButton;
|
||||||
@ -1362,6 +1362,7 @@ RED.palette.editor = (function() {
|
|||||||
if (e) {
|
if (e) {
|
||||||
nodeList.editableList('removeItem', e);
|
nodeList.editableList('removeItem', e);
|
||||||
delete nodeEntries[entry.name];
|
delete nodeEntries[entry.name];
|
||||||
|
refreshUpdateStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We assume that a plugin that implements onremove
|
// 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 {
|
return {
|
||||||
init: init,
|
init: init,
|
||||||
install: install
|
install: install
|
||||||
|
@ -33,6 +33,7 @@ RED.statusBar = (function() {
|
|||||||
var el = $('<span class="red-ui-statusbar-widget"></span>');
|
var el = $('<span class="red-ui-statusbar-widget"></span>');
|
||||||
el.prop('id', options.id);
|
el.prop('id', options.id);
|
||||||
options.element.appendTo(el);
|
options.element.appendTo(el);
|
||||||
|
options.elementDiv = el;
|
||||||
if (options.align === 'left') {
|
if (options.align === 'left') {
|
||||||
leftBucket.append(el);
|
leftBucket.append(el);
|
||||||
} else if (options.align === 'right') {
|
} 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 {
|
return {
|
||||||
init: function() {
|
init: function() {
|
||||||
leftBucket = $('<span class="red-ui-statusbar-bucket red-ui-statusbar-bucket-left">').appendTo("#red-ui-workspace-footer");
|
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");
|
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;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.red-ui-update-status {
|
||||||
|
width: auto;
|
||||||
|
padding: 0 3px;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user