1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Add core:show-welcome-tour action and logic to display on first-run

This commit is contained in:
Nick O'Leary 2021-09-27 16:42:51 +01:00
parent e20cfb3dae
commit e9e03c945b
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
8 changed files with 151 additions and 43 deletions

View File

@ -112,6 +112,7 @@
"editPalette":"Manage palette", "editPalette":"Manage palette",
"other": "Other", "other": "Other",
"showTips": "Show tips", "showTips": "Show tips",
"showWelcomeTours": "Show guided tours for new versions",
"help": "Node-RED website", "help": "Node-RED website",
"projects": "Projects", "projects": "Projects",
"projects-new": "New", "projects-new": "New",

View File

@ -535,19 +535,18 @@ var RED = (function() {
setTimeout(function() { setTimeout(function() {
loader.end(); loader.end();
checkFirstRun();
},100); },100);
} }
function showAbout() { function checkFirstRun() {
$.get('red/about', function(data) { if (RED.settings.theme("tours") === false) {
// data will be strictly markdown. Any HTML should be escaped. return;
data = RED.utils.sanitize(data); }
var aboutHeader = '<div style="text-align:center;">'+ if (!RED.settings.get("editor.view.view-show-welcome-tours", true)) {
'<img width="50px" src="red/images/node-red-icon.svg" />'+ return;
'</div>'; }
RED.actions.invoke("core:show-welcome-tour", RED.settings.get("editor.tours.welcome"));
RED.sidebar.help.set(aboutHeader+RED.utils.renderMarkdown(data));
});
} }
function buildMainMenu() { function buildMainMenu() {
@ -696,9 +695,6 @@ var RED = (function() {
$("#red-ui-main-container").show(); $("#red-ui-main-container").show();
RED.actions.add("core:show-about", showAbout);
loadPluginList(); loadPluginList();
} }

View File

@ -19,7 +19,6 @@ RED.settings = (function () {
var loadedSettings = {}; var loadedSettings = {};
var userSettings = {}; var userSettings = {};
var settingsDirty = false;
var pendingSave; var pendingSave;
var hasLocalStorage = function () { var hasLocalStorage = function () {

View File

@ -25,7 +25,6 @@ RED.sidebar.help = (function() {
var tocPanel; var tocPanel;
var helpIndex = {}; var helpIndex = {};
function resizeStack() { function resizeStack() {
var h = $(content).parent().height() - toolbar.outerHeight(); var h = $(content).parent().height() - toolbar.outerHeight();
panels.resize(h) panels.resize(h)
@ -93,9 +92,28 @@ RED.sidebar.help = (function() {
$('<span class="red-ui-help-info-none">'+RED._("sidebar.help.noHelp")+'</span>').appendTo(helpSection); $('<span class="red-ui-help-info-none">'+RED._("sidebar.help.noHelp")+'</span>').appendTo(helpSection);
treeList = $("<div>").css({width: "100%"}).appendTo(tocPanel).treeList({data: []}) treeList = $("<div>").css({width: "100%"}).appendTo(tocPanel).treeList({data: []})
var pendingContentLoad;
treeList.on('treelistselect', function(e,item) { treeList.on('treelistselect', function(e,item) {
pendingContentLoad = item;
if (item.nodeType) { if (item.nodeType) {
showHelp(item.nodeType); showNodeTypeHelp(item.nodeType);
} else if (item.content) {
helpSection.empty();
if (typeof item.content === "string") {
setInfoText(item.label, item.content);
} else if (typeof item.content === "function") {
if (item.content.length === 0) {
setInfoText(item.label, item.content());
} else {
setInfoText(item.label, '<div class="red-ui-component-spinner red-ui-component-spinner-contain"><img src="red/images/spin.svg" /></div>',helpSection)
item.content(function(content) {
if (pendingContentLoad === item) {
helpSection.empty();
setInfoText(item.label, content);
}
})
}
}
} }
}) })
@ -174,21 +192,28 @@ RED.sidebar.help = (function() {
var moduleNames = Object.keys(modules); var moduleNames = Object.keys(modules);
moduleNames.sort(); moduleNames.sort();
var helpData = [{ var nodeHelp = {
label: RED._("sidebar.help.nodeHelp"), label: RED._("sidebar.help.nodeHelp"),
children: [], children: [],
expanded: true expanded: true
}] }
var helpData = [
{
id: 'changelog',
label: "Node-RED v"+RED.settings.version,
content: getChangelog
},
nodeHelp
]
var subflows = RED.nodes.registry.getNodeTypes().filter(function(t) {return /subflow/.test(t)}); var subflows = RED.nodes.registry.getNodeTypes().filter(function(t) {return /subflow/.test(t)});
if (subflows.length > 0) { if (subflows.length > 0) {
helpData[0].children.push({ nodeHelp.children.push({
label: RED._("menu.label.subflows"), label: RED._("menu.label.subflows"),
children: [] children: []
}) })
subflows.forEach(function(nodeType) { subflows.forEach(function(nodeType) {
var sf = RED.nodes.getType(nodeType); var sf = RED.nodes.getType(nodeType);
helpData[0].children[0].children.push({ nodeHelp.children[0].children.push({
id:"node-type:"+nodeType, id:"node-type:"+nodeType,
nodeType: nodeType, nodeType: nodeType,
subflowLabel: sf.label().toLowerCase(), subflowLabel: sf.label().toLowerCase(),
@ -218,7 +243,7 @@ RED.sidebar.help = (function() {
nodeTypes.sort(function(A,B) { nodeTypes.sort(function(A,B) {
return A.nodeType.localeCompare(B.nodeType) return A.nodeType.localeCompare(B.nodeType)
}) })
helpData[0].children.push({ nodeHelp.children.push({
id: moduleName, id: moduleName,
icon: "fa fa-cube", icon: "fa fa-cube",
label: moduleName, label: moduleName,
@ -244,7 +269,7 @@ RED.sidebar.help = (function() {
return div; return div;
} }
function showHelp(nodeType) { function showNodeTypeHelp(nodeType) {
helpSection.empty(); helpSection.empty();
var helpText; var helpText;
var title; var title;
@ -265,7 +290,7 @@ RED.sidebar.help = (function() {
} }
} }
} }
setInfoText(title, helpText, helpSection); setInfoText(title, helpText);
var ratio = panels.ratio(); var ratio = panels.ratio();
if (ratio > 0.7) { if (ratio > 0.7) {
@ -282,7 +307,7 @@ RED.sidebar.help = (function() {
} }
if (type) { if (type) {
// hideTOC(); // hideTOC();
showHelp(type); showNodeTypeHelp(type);
} }
resizeStack(); resizeStack();
} }
@ -298,11 +323,12 @@ RED.sidebar.help = (function() {
return el; return el;
} }
function setInfoText(title, infoText,target) { function setInfoText(title, infoText) {
helpSection.empty();
if (title) { if (title) {
$("<h1>",{class:"red-ui-help-title"}).text(title).appendTo(target); $("<h1>",{class:"red-ui-help-title"}).text(title).appendTo(helpSection);
} }
var info = addTargetToExternalLinks($('<div class="red-ui-help"><span class="red-ui-text-bidi-aware" dir=\"'+RED.text.bidi.resolveBaseTextDir(infoText)+'">'+infoText+'</span></div>')).appendTo(target); var info = addTargetToExternalLinks($('<div class="red-ui-help"><span class="red-ui-text-bidi-aware" dir=\"'+RED.text.bidi.resolveBaseTextDir(infoText)+'">'+infoText+'</span></div>')).appendTo(helpSection);
info.find(".red-ui-text-bidi-aware").contents().filter(function() { return this.nodeType === 3 && this.textContent.trim() !== "" }).wrap( "<span></span>" ); info.find(".red-ui-text-bidi-aware").contents().filter(function() { return this.nodeType === 3 && this.textContent.trim() !== "" }).wrap( "<span></span>" );
var foldingHeader = "H3"; var foldingHeader = "H3";
info.find(foldingHeader).wrapInner('<a class="red-ui-help-info-header expanded" href="#"></a>') info.find(foldingHeader).wrapInner('<a class="red-ui-help-info-header expanded" href="#"></a>')
@ -316,12 +342,12 @@ RED.sidebar.help = (function() {
} }
$(this).toggleClass('expanded',!isExpanded); $(this).toggleClass('expanded',!isExpanded);
}) })
target.parent().scrollTop(0); helpSection.parent().scrollTop(0);
} }
function set(html,title) { function set(html,title) {
$(helpSection).empty(); $(helpSection).empty();
setInfoText(title,html,helpSection); setInfoText(title,html);
hideTOC(); hideTOC();
show(); show();
} }
@ -336,13 +362,83 @@ RED.sidebar.help = (function() {
if (node.type === "subflow" && node.direction) { if (node.type === "subflow" && node.direction) {
// ignore subflow virtual ports // ignore subflow virtual ports
} else if (node.type !== 'group'){ } else if (node.type !== 'group'){
showHelp(node.type); showNodeTypeHelp(node.type);
} }
} }
} }
} }
RED.events.on("view:selection-changed",refreshSelection); RED.events.on("view:selection-changed",refreshSelection);
function getChangelog(done) {
$.get('red/about', function(data) {
// data will be strictly markdown. Any HTML should be escaped.
data = RED.utils.sanitize(data);
RED.tourGuide.load("./tours/welcome.js", function(err, tour) {
var tourHeader = '<div><img width="50px" src="red/images/node-red-icon.svg" /></div>';
if (tour) {
var currentVersionParts = RED.settings.version.split(".");
var tourVersionParts = tour.version.split(".");
if (tourVersionParts[0] === currentVersionParts[0] && tourVersionParts[1] === currentVersionParts[1]) {
tourHeader = '<div><button type="button" onclick="RED.actions.invoke(\'core:show-welcome-tour\')" class="red-ui-button">Take a tour</button></div>'
}
}
var aboutHeader = '<div style="text-align:center;">'+tourHeader+'</div>'
done(aboutHeader+RED.utils.renderMarkdown(data))
});
});
}
function showAbout() {
treeList.treeList("show","changelog")
treeList.treeList("select","changelog");
show();
}
function showWelcomeTour(lastSeenVersion) {
RED.tourGuide.load("./tours/welcome.js", function(err, tour) {
if (err) {
console.warn("Failed to load welcome tour",err);
return;
}
var currentVersionParts = RED.settings.version.split(".");
var tourVersionParts = tour.version.split(".");
// Only display the tour if its MAJ.MIN versions the current version
// This means if we update MAJ/MIN without updating the tour, the old tour won't get shown
if (tourVersionParts[0] !== currentVersionParts[0] || tourVersionParts[1] !== currentVersionParts[1]) {
return;
}
if (lastSeenVersion) {
// Previously displayed a welcome tour.
if (lastSeenVersion === RED.settings.version) {
// Exact match - don't show the tour
return;
}
var lastSeenParts = lastSeenVersion.split(".");
if (currentVersionParts[0] < lastSeenParts[0] || (currentVersionParts[0] === lastSeenParts[0] && currentVersionParts[1] < lastSeenParts[1])) {
// Running an *older* version than last displayed tour.
return;
}
if (currentVersionParts[0] === lastSeenParts[0] && currentVersionParts[1] === lastSeenParts[1]) {
if (lastSeenParts.length === 3 && currentVersionParts.length === 3) {
// Matching non-beta MAJ.MIN - don't repeat tour
return;
}
if (currentVersionParts.length === 4 && (lastSeenParts.length === 3 || currentVersionParts[3] < lastSeenParts[3])) {
// Running an *older* beta than last displayed tour.
return
}
}
}
RED.tourGuide.run("./tours/welcome.js", function(err) {
RED.settings.set("editor.tours.welcome", RED.settings.version)
})
})
}
RED.actions.add("core:show-about", showAbout);
RED.actions.add("core:show-welcome-tour", showWelcomeTour);
return { return {
init: init, init: init,
show: show, show: show,

View File

@ -15,17 +15,27 @@ RED.tourGuide = (function() {
console.error(err); console.error(err);
} }
}; };
loadTour(tourPath, function(err, tour) {
if (err) {
console.warn("Error loading tour:",err);
return;
}
runTour(tour, done);
})
}
function loadTour(tourPath, done) {
if (tourCache[tourPath]) { if (tourCache[tourPath]) {
runTour(tourCache[tourPath],done); done(null, tourCache[tourPath]);
} else { } else {
/* jshint ignore:start */ /* jshint ignore:start */
// jshint<2.13 doesn't support dynamic imports. Once grunt-contrib-jshint // jshint<2.13 doesn't support dynamic imports. Once grunt-contrib-jshint
// has been updated with the new jshint, we can stop ignoring this block // has been updated with the new jshint, we can stop ignoring this block
import(tourPath).then(function(module) { import(tourPath).then(function(module) {
tourCache[tourPath] = module.default; tourCache[tourPath] = module.default;
runTour(tourCache[tourPath],done); done(null, tourCache[tourPath]);
}).catch(function(err) { }).catch(function(err) {
console.warn("Error loading tour:",err);
done(err); done(err);
}) })
/* jshint ignore:end */ /* jshint ignore:end */
@ -357,7 +367,11 @@ RED.tourGuide = (function() {
} }
return { return {
run: run load: loadTour,
run: run,
reset: function() {
RED.settings.set("editor.tours.welcome",'');
}
} }

View File

@ -139,7 +139,8 @@ RED.userSettings = (function() {
{ {
title: "menu.label.other", title: "menu.label.other",
options: [ options: [
{setting:"view-show-tips",oldSettings:"menu-menu-item-show-tips",label:"menu.label.showTips",toggle:true,default:true,onchange:"core:toggle-show-tips"} {setting:"view-show-tips",oldSettings:"menu-menu-item-show-tips",label:"menu.label.showTips",toggle:true,default:true,onchange:"core:toggle-show-tips"},
{setting:"view-show-welcome-tours",label:"menu.label.showWelcomeTours",toggle:true,default:true}
] ]
} }
]; ];

View File

@ -295,7 +295,7 @@ $group-default-stroke: #999;
$group-default-stroke-opacity: 1; $group-default-stroke-opacity: 1;
$group-default-label-color: #a4a4a4; $group-default-label-color: #a4a4a4;
$tourGuide-shade: rgba(100, 70, 70, 0.6); $tourGuide-shade: $shade-color;
$tourGuide-border: #a22222; $tourGuide-border: #a22222;
$tourGuide-heading-color: #a22222; $tourGuide-heading-color: #a22222;

View File

@ -1,4 +1,5 @@
export default { export default {
version: "2.1.0",
steps: [ steps: [
{ {
titleIcon: "fa fa-map-o", titleIcon: "fa fa-map-o",
@ -11,11 +12,11 @@ export default {
"<p>You can choose not to see this tour in the future by disabling it under the View tab of User Settings.</p>", "<p>You can choose not to see this tour in the future by disabling it under the View tab of User Settings.</p>",
}, },
{ {
prepare:function() { prepare() {
$("#red-ui-header-button-sidemenu").trigger("click"); $("#red-ui-header-button-sidemenu").trigger("click");
$("#menu-item-edit-menu").parent().addClass("open") $("#menu-item-edit-menu").parent().addClass("open")
}, },
complete: function() { complete() {
$("#menu-item-edit-menu").parent().removeClass("open") $("#menu-item-edit-menu").parent().removeClass("open")
}, },
element: "#menu-item-edit-menu-submenu", element: "#menu-item-edit-menu-submenu",
@ -27,11 +28,11 @@ export default {
}, },
{ {
prepare: function() { prepare() {
$("#red-ui-header-button-sidemenu").trigger("click"); $("#red-ui-header-button-sidemenu").trigger("click");
$("#menu-item-arrange-menu").parent().addClass("open") $("#menu-item-arrange-menu").parent().addClass("open")
}, },
complete: function() { complete() {
$("#menu-item-arrange-menu").parent().removeClass("open") $("#menu-item-arrange-menu").parent().removeClass("open")
}, },
element: "#menu-item-arrange-menu-submenu", element: "#menu-item-arrange-menu-submenu",
@ -46,7 +47,7 @@ export default {
description: "<p>Flows and Groups can now have their own environment variables that can be referenced by nodes inside them.</p>", description: "<p>Flows and Groups can now have their own environment variables that can be referenced by nodes inside them.</p>",
}, },
{ {
prepare: function(done) { prepare(done) {
RED.editor.editFlow(RED.nodes.workspace(RED.workspaces.active()),"editor-tab-envProperties"); RED.editor.editFlow(RED.nodes.workspace(RED.workspaces.active()),"editor-tab-envProperties");
setTimeout(done,800); setTimeout(done,800);
}, },
@ -57,7 +58,7 @@ export default {
element: ".node-input-env-container-row .red-ui-editableList-addButton", element: ".node-input-env-container-row .red-ui-editableList-addButton",
direction: "top", direction: "top",
description: '<p>The environment variables are listed in this table and new ones can be added by clicking the <i class="fa fa-plus"></i> button.</p>', description: '<p>The environment variables are listed in this table and new ones can be added by clicking the <i class="fa fa-plus"></i> button.</p>',
complete: function() { complete() {
$("#node-dialog-cancel").trigger("click"); $("#node-dialog-cancel").trigger("click");
} }
}, },