Skip to content

Instantly share code, notes, and snippets.

Last active Oct 23, 2020
What would you like to do?
(ns app.statecharts
(:require [app.utils :as utils]
[statecharts.core :as fsm]
[interop.anomalies :as anom]
[re-frame.core :as rf]))
; Guards
(defn offline? [state event] (not (get-in state [:db :app/online?])))
(defn has-errors? [state event] (seq (utils/login-form-errors (get-in state [:db :app/login-form]))))
(defn incorrect? [state event] (anom/incorrect? (:data event)))
(defn anomaly? [state event] (anom/anomaly? (:data event)))
; Actions
(defn api-token-auth
[state event]
(let [{:login-form/keys [username password]} (get-in state [:db :app/login-form])
data {:username username
:password password
:resolve [::transition :auth/api-token-auth.resolved]
:reject [::transition :auth/api-token-auth.rejected]}]
(update state :fx conj [:app.fx/api-token-auth data])))
(defn save-token [state event]
(update-in state [:db :auth/token] (get-in event [:data "token"])))
(defn store-token [state event]
(update state :fx conj [:app.fx/set-token {:token (get-in event [:data "token"])
:resolve [::transaction :auth/set-token.resolved]
:reject [::transaction :auth/set-token.rejected]}]))
(defn oopsie [msg]
(fn [s] (update s :fx conj [:app.fx/alert-modal {:title "Oopsie" :message msg}])))
(def report-offline (oopsie "We're offline"))
(def report-errors (oopsie "Form has errors"))
(def report-failed (oopsie "Unable to authenticate with details provided"))
(def report-anomaly (oopsie "Unexpected error authenticating"))
; Machine
(def auth-machine
{:id :auth
:initial :auth/idle
:states {:auth/idle {:on {:submit [{:target :auth/idle :guard offline? :actions report-offline}
{:target :auth/idle :guard has-errors? :actions report-errors}
{:target :auth/post :actions api-token-auth}]}}
:auth/post {:entry api-token-auth
:on {:api-token-auth.resolved
[{:target :auth/idle :guard incorrect? :actions report-failed}
{:target :auth/idle :guard anomaly? :actions report-anomaly}
{:target :auth/idle :actions [save-token store-token]}]
[{:target :auth/idle :actions report-anomaly}]}}}})
; Reframe
(def machine-path [::machine])
(defn get-state-with-context
[{:keys [db]}]
(let [s2 (get-in db machine-path)]
(assoc s2 :db db)))
(defn do-execute
[{:keys [db _actions] :as s}]
(reduce (fn [s' f]
(f s'))
{:fx [] :db (assoc-in db machine-path (dissoc s :db :_actions))}
(defn do-initialise
[{:keys [db]}]
{:db (assoc-in db machine-path (fsm/initialize (fsm/machine machine)))})
(defn do-transition
[ctx [_ type data]]
(let [s1 (get-state-with-context ctx)
s2 (fsm/transition machine s1 {:type type :data data} {:exec false})]
(do-execute s2)))
(rf/reg-event-fx ::init do-initialise)
(rf/reg-event-fx ::transition do-transition)
(rf/dispatch [::init])
(rf/dispatch [::transition :submit]))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment