diff --git a/.gitmodules b/.gitmodules index beb37f01f7..51e1374b1a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,3 +28,6 @@ [submodule "public/vendor/multiple-select"] path = public/vendor/multiple-select url = https://github.com/ornicar/multiple-select +[submodule "public/vendor/hopscotch"] + path = public/vendor/hopscotch + url = https://github.com/linkedin/hopscotch diff --git a/app/templating/AssetHelper.scala b/app/templating/AssetHelper.scala index 42df3eb7f7..10fa418703 100644 --- a/app/templating/AssetHelper.scala +++ b/app/templating/AssetHelper.scala @@ -68,6 +68,9 @@ trait AssetHelper { self: I18nHelper => jsAt(s"vendor/moment/locale/$l.js", static = true) } + def hopscotchJsTag = jsAt("""vendor/hopscotch/dist/js/hopscotch.min.js""") + def hopscotchCssTag = cssVendorTag("""hopscotch/dist/css/hopscotch.min.css""") + val tagmanagerTag = cdnOrLocal( cdn = "http://cdnjs.cloudflare.com/ajax/libs/tagmanager/3.0.0/tagmanager.js", test = "$.tagsManager", diff --git a/app/views/insight/index.scala.html b/app/views/insight/index.scala.html index fe8534c46f..624a8cb50a 100644 --- a/app/views/insight/index.scala.html +++ b/app/views/insight/index.scala.html @@ -2,12 +2,15 @@ @moreJs = { @highchartsLatestTag +@hopscotchJsTag @jsAt("vendor/multiple-select/multiple-select.js") @jsAt(s"compiled/lichess.insight${isProd??(".min")}.js") @jsTag("insight-refresh.js") +@jsTag("insight-tour.js") @embedJs { $(function() { -LichessInsight(document.getElementById('insight'), { +lichess = lichess || {}; +lichess.insight = LichessInsight(document.getElementById('insight'), { ui: @Html(toJson(ui)), initialQuestion: @Html(toJson(question)), i18n: @jsI18n(), @@ -27,6 +30,7 @@ postUrl: "@routes.Insight.json(u.username)" } @moreCss = { +@hopscotchCssTag @cssTag("insight.css") @cssVendorTag("multiple-select/multiple-select.css") @ctx.currentBg match { diff --git a/public/javascripts/insight-tour.js b/public/javascripts/insight-tour.js new file mode 100644 index 0000000000..e28520bcaf --- /dev/null +++ b/public/javascripts/insight-tour.js @@ -0,0 +1,80 @@ +$(function() { + + setTimeout(function() { + var tour = { + id: "insights", + showPrevButton: true, + steps: [{ + title: "Welcome to chess insights!", + content: "Know your strengths and weaknesses!
" + + "Insights let you analyse your playing style,
" + + "using pertinent metrics and dimensions.

" + + "It's a powerful tool, let's take some time to see how it works.", + target: "#insight header h2", + placement: "bottom" + }, { + title: "Insights answer questions", + content: "Here are a few examples of the questions you can ask. Try clicking them!", + target: "#insight .panel-tabs", + placement: "right", + onShow: function() { + lichess.insight.setPanel('preset'); + } + }, { + title: "Answers are graphs", + content: "Colorful bars represent the answer to the question posed.
" + + "Gray bars represent the size of each data sample, like the number of moves.", + target: "#insight .chart", + placement: "left" + }, { + title: "The same data, in a table", + content: "This table provides an alternative way to read the answer.
" + + "Farther down the page are a few of the games used to answer the question.", + target: "#insight table.slist", + placement: "top" + }, { + title: "Ask a question: metric", + content: "To start asking your own questions, start by selecting a metric.
" + + "For instance, let's ask a question about move times.", + target: "#insight div.ms.metric", + placement: "left", + onShow: function() { + lichess.insight.clearFilters(); + lichess.insight.setPanel('filter'); + } + }, { + title: "Ask a question: dimension", + content: "Now select a dimension to compare move times with.
" + + "For instance, try seeing your move times per variant, or per piece moved.", + target: "#insight div.ms.dimension", + placement: "left" + }, { + title: "Ask a question: filters", + content: "Make your question more precise by filtering the results.
" + + "For instance, select games where you play black and castle kingside.", + target: "#insight .panel-tabs", + placement: "top", + onShow: function() { + lichess.insight.clearFilters(); + lichess.insight.setPanel('filter'); + } + }, { + title: "Thank you for your time!", + content: "Now be inventive and find the right questions to ask!
" + + "You can copy the URL at any time to share the results you're seeing.

" + + "Oh and one last thing...", + target: "#insight header h2", + placement: "bottom" + }, { + title: "Share your insights data", + content: "By default, your data is visible to your lichess friends only.
" + + "You can make it public or private from your privacy settings.

" + + "Have fun :)", + target: "#insight .share", + placement: "right" + }] + }; + var t = hopscotch.startTour(tour); + console.log(t); + }, 1000); +}); diff --git a/public/vendor/hopscotch b/public/vendor/hopscotch new file mode 160000 index 0000000000..5a09436783 --- /dev/null +++ b/public/vendor/hopscotch @@ -0,0 +1 @@ +Subproject commit 5a09436783db849e1a2d1a9551c2ce3acf045fa9 diff --git a/ui/analyse/src/forecast/forecastCtrl.js b/ui/analyse/src/forecast/forecastCtrl.js index 37d721cd8f..6131acceac 100644 --- a/ui/analyse/src/forecast/forecastCtrl.js +++ b/ui/analyse/src/forecast/forecastCtrl.js @@ -65,6 +65,7 @@ module.exports = function(cfg, saveUrl) { var reloadToLastPly = function() { loading(true); + m.redraw(); if (window.history.replaceState) window.history.replaceState(null, null, '#last'); location.reload(); }; diff --git a/ui/insight/src/ctrl.js b/ui/insight/src/ctrl.js index 751df581a7..dd858419b4 100644 --- a/ui/insight/src/ctrl.js +++ b/ui/insight/src/ctrl.js @@ -34,6 +34,12 @@ module.exports = function(env, domElement) { panel: !!Object.keys(env.initialQuestion.filters).length ? 'filter' : 'preset' }; + this.setPanel = function(p) { + this.vm.panel = p; + m.redraw(); + }.bind(this); + + var reset = function() { this.vm.metric = this.metrics[0]; this.vm.dimension = this.dimensions[0]; diff --git a/ui/insight/src/main.js b/ui/insight/src/main.js index 509f1d2c38..1a3fe60e85 100644 --- a/ui/insight/src/main.js +++ b/ui/insight/src/main.js @@ -13,4 +13,6 @@ module.exports = function(element, opts) { }, view: view }); + + return controller; }; diff --git a/ui/insight/src/view.js b/ui/insight/src/view.js index 884b56c97f..48c40619af 100644 --- a/ui/insight/src/view.js +++ b/ui/insight/src/view.js @@ -24,13 +24,13 @@ module.exports = function(ctrl) { m('a[data-panel=preset]', { class: 'tab' + (ctrl.vm.panel === 'preset' ? ' active' : ''), onclick: function() { - ctrl.vm.panel = 'preset'; + ctrl.setPanel('preset'); } }, 'Presets'), m('a[data-panel=filter]', { class: 'tab' + (ctrl.vm.panel === 'filter' ? ' active' : ''), onclick: function() { - ctrl.vm.panel = 'filter'; + ctrl.setPanel('filter'); } }, 'Filters'), !!Object.keys(ctrl.vm.filters).length ? m('a.clear.hint--top', { 'data-hint': 'Clear all filters',