Skip to content

Commit

Permalink
Snackbar to indicate that a tutorial is available, fixes #17
Browse files Browse the repository at this point in the history
  • Loading branch information
oliyh committed Mar 6, 2020
1 parent 9d89e96 commit da4c128
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 24 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,13 @@ Combine lessons into tutorials and attach them to views:
```clojure
(def checkout
(re-learn/with-tutorial
{:id :checkout-tutorial
:name "The checkout"
:description "Review your basket, check the price and confirm your purchase"
:precedence 1 ;; optional, allows some tutorials to take precedence over others
{:id :checkout-tutorial
:name "The checkout"
:description "Review your basket, check the price and confirm your purchase"
:precedence 1 ;; optional, allows some tutorials to take precedence over others
:auto-accept? false ;; optional, defaults to false
;; when true will start the tutorial immediately when this component is rendered
;; when false will display a snackbar indicating that a tutorial is available
:lessons [{:id :welcome-lesson ;; this is an inline lesson, not attached to anything
:description "Welcome to the re-learn example"}
basket
Expand Down
31 changes: 31 additions & 0 deletions dev-resources/public/css/re-learn.css
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,37 @@
border-bottom-color: rgba(0, 0, 0, 0.8);
}

/* toast */

.toast {
position: fixed;
bottom: 0;
left: 50%;
transform: translateX(-50%);
background-color: rgba(0, 0, 0, 0.8);
color: white;
padding: 1em;
z-index: 10000;
border-top-right-radius: 4px;
border-top-left-radius: 4px;
}

.toast button {
margin-left: 4px;
padding: 4px;
cursor: pointer;
border: none;
color: white;
}

.toast button.accept {
background-color: rgb(255,64,129);
}

.toast button.dismiss {
background-color: rgba(0, 0, 0, 0.4);
}

/* tutorial context */

.context-container {
Expand Down
2 changes: 1 addition & 1 deletion example/checkout/checkout/app.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@
app-root (js/document.getElementById "app")]

(reagent/render [checkout app-db] app-root)
(reagent/render [re-learn-views/tutorial {:context? true}] tutorial-root)))
(reagent/render [re-learn-views/tutorial {:context? true :auto-accept? true}] tutorial-root)))

(defn- on-figwheel-reload []
(mount-all))
Expand Down
26 changes: 21 additions & 5 deletions src/re_learn/model.cljs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
(ns re-learn.model
(:require [re-frame.core :as re-frame]
[re-frame.std-interceptors :refer [trim-v]]
[re-learn.local-storage :as local-storage]
[re-learn.schema :refer [ReLearnModel]]
[re-learn.dom :refer [->absolute-bounds in-viewport? viewport-height viewport-width]]
Expand Down Expand Up @@ -31,7 +30,7 @@
(re-frame/reg-event-fx ::hard-reset
interceptors
(fn [{:keys [db]}]
{:db (assoc db :lessons-learned {})
{:db (assoc db :lessons-learned {} :accepted-tutorials #{})
::local-storage/save [:re-learn/lessons-learned {}]}))

(re-frame/reg-fx ::on-dom-event
Expand Down Expand Up @@ -93,8 +92,11 @@

(re-frame/reg-event-fx ::lesson-learned
interceptors
(fn [{:keys [db]} lesson-ids]
(let [lessons-learned (reduce (fn [learned lesson-id]
(fn [{:keys [db]} [lesson-id-or-ids]]
(let [lesson-ids (if (coll? lesson-id-or-ids)
lesson-id-or-ids
[lesson-id-or-ids])
lessons-learned (reduce (fn [learned lesson-id]
(assoc learned lesson-id
(get-in db [:lessons lesson-id :version])))
(:lessons-learned db)
Expand All @@ -109,6 +111,18 @@
{:db (assoc db :lessons-learned lessons-learned)
::local-storage/save [:re-learn/lessons-learned lessons-learned]})))

(re-frame/reg-event-fx ::skip-tutorial
interceptors
(fn [{:keys [db]} [tutorial-id]]
(let [tutorial (get-in db [:tutorials tutorial-id])]
(js/console.log "tutorial" (pr-str tutorial-id) (pr-str tutorial))
{:dispatch [::lesson-learned (:lessons tutorial)]})))

(re-frame/reg-event-db ::accept-tutorial
interceptors
(fn [db [tutorial-id]]
(update db :accepted-tutorials (fnil conj #{}) tutorial-id)))

(defn- ->lesson-id [lesson]
(cond
(keyword? lesson)
Expand Down Expand Up @@ -179,10 +193,12 @@
(let [state (state db)]
(first (for [tutorial (->> (:tutorials state) vals (sort-by :precedence))
:let [lessons (keep (:lessons state) (:lessons tutorial))
[learned to-learn] (split-with #(already-learned? (:lessons-learned state) %) lessons)]
[learned to-learn] (split-with #(already-learned? (:lessons-learned state) %) lessons)
accepted? (contains? (:accepted-tutorials state) (:id tutorial))]
lesson to-learn
:let [total (count lessons)]]
{:tutorial tutorial
:accepted? accepted?
:learned learned
:to-learn to-learn
:completion {:ratio (/ (+ (count learned) 0.5) total)
Expand Down
11 changes: 6 additions & 5 deletions src/re_learn/schema.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@
:precedence s/Int})

(def ReLearnModel
{:help-mode? s/Bool
:highlighted-lesson-id (s/maybe LessonId)
(s/optional-key :lessons-learned) {LessonId s/Int}
(s/optional-key :lessons) {LessonId Lesson}
(s/optional-key :tutorials) {TutorialId Tutorial}})
{:help-mode? s/Bool
:highlighted-lesson-id (s/maybe LessonId)
(s/optional-key :lessons-learned) {LessonId s/Int}
(s/optional-key :lessons) {LessonId Lesson}
(s/optional-key :tutorials) {TutorialId Tutorial}
(s/optional-key :accepted-tutorials) #{TutorialId}})
32 changes: 23 additions & 9 deletions src/re_learn/views.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
(gstring/unescapeEntities "❱")])])

[:div.context-controls
[:a {:on-click #(re-frame/dispatch (into [::model/lesson-learned] (map :id (:to-learn @context))))}
[:a {:on-click #(re-frame/dispatch [::model/skip-tutorial (get-in @context [:tutorial :id])])}
"SKIP " (gstring/unescapeEntities "&#10219")]]

(when (pos? (get-in @context [:completion :total]))
Expand Down Expand Up @@ -168,14 +168,28 @@
(defn tutorial
"Root view for displaying unlearned tutorials on the page.
The :context? key allows you to turn on the context view which shows progress through the tutorial
at the bottom of the screen."
[{:keys [context?]}]
at the bottom of the screen.
The :auto-accept? key when false shows a notification that lessons are available allowing the user to choose to start one,
rather than starting the tutorial straight away when true (legacy behaviour)"
[{:keys [context? auto-accept?]
:or {context? false
auto-accept? false}}]
(let [tutorial (re-frame/subscribe [::model/current-tutorial])
help-mode? (re-frame/subscribe [::model/help-mode?])]
(fn []
(if @help-mode?
[help-mode]
[:div
[lesson-view (:current-lesson @tutorial)]
(when context?
[lesson-context tutorial])]))))
(cond @help-mode?
[help-mode]

(and (false? auto-accept?) (false? (:accepted? @tutorial)))
[:div.toast
"There is a tutorial available"
[:button.accept {:on-click #(re-frame/dispatch [::model/accept-tutorial (get-in @tutorial [:tutorial :id])])}
"Start"]
[:button.dismiss {:on-click #(re-frame/dispatch [::model/skip-tutorial (get-in @tutorial [:tutorial :id])])}
"Dismiss"]]

(or auto-accept? (:accepted? @tutorial))
[:div
[lesson-view (:current-lesson @tutorial)]
(when context?
[lesson-context tutorial])]))))

0 comments on commit da4c128

Please sign in to comment.