Created
September 13, 2021 20:20
-
-
Save jaidetree/5e5b26b474feace48d94b714625056b2 to your computer and use it in GitHub Desktop.
First draft of feature combining baconjs stream app state library with a lightweight state machine system
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns montage.features.slideshow | |
(:require | |
[framework.fsm :as fsm] | |
[framework.features :refer [register]] | |
[framework.reactor :refer [of-type compose-fx]] | |
[frmaework.stream :as stream])) | |
(defn transition-delay | |
[machine] | |
(/ (get-in machine [:context :transition-delay]) 2)) | |
(def states | |
{:idle | |
{:slideshow/transition | |
(fn [machine action] | |
{:state :prepare | |
:context {:transition-delay (get-in action [:payload :transition-delay]) | |
:from (get-in action [:payload :from]) | |
:to (get-in action [:payload :to])} | |
:effect {:type :slideshow/wait | |
:payload {:delay 0 | |
:action {:type :slideshow/begin | |
:payload nil}}}})} | |
:prepare | |
{:slideshow/begin | |
(fn [machine _action] | |
{:state :transition-out | |
:context (:context machine) | |
:effect {:type :slideshow/wait | |
:payload {:delay (transition-delay machine) | |
:action {:type :slideshow/transition-out-complete | |
:payload nil}}}})} | |
:transition-out | |
{:slideshow/transitioned-out-complete | |
(fn [machine _action] | |
{:state :transition-in | |
:context (:context machine) | |
:effect {:type :slideshow/wait | |
:payload {:delay (transition-delay machine) | |
:action {:type :slideshow/transition-in-complete | |
:payload nil}}}})} | |
:transition-in | |
{:slideshow/transition-in-complete | |
(fn [machine _action] | |
{:state :idle | |
:context (merge (:context machine) | |
{:from nil | |
:to nil}) | |
:effect nil})}}) | |
(def slideshow | |
(fsm/create :create/initial | |
states | |
{:state :idle | |
:context {} | |
:effect nil})) | |
(def reducer (:reducer slideshow)) | |
(defn wait-fx | |
[actions _deps] | |
(-> actions | |
(of-type :slideshow/wait) | |
(.flatMap (fn [action] | |
(stream/later (get-in action [:payload :delay]) | |
(get-in action [:payload :action])))))) | |
(def fx (compose-fx | |
(:fx slideshow) | |
wait-fx)) | |
(register :slideshow {:reducer reducer | |
:fx fx}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Basically we're creating a state machine that acts as a general app state reducer and an fx handler to process the bacon stream of actions for general
{:type :slideshow/wait :payload {:delay number :action {:type action-type :payload action-payload}}}
, wait the specified delay, then emit the payload action. This supports waiting for a CSS transition to conclude.The
states
data maps the slideshow states the can be in, to actions expected to be dispatched to functions to return the new state machine state which includes the current state key, context data, and an effect action that will be immediately emitted downstream to trigger any subscriptions to the actions bacon stream.Feature wise:
It expects a
{:type :slideshow/transition {:payload {:transition-delay 1000 :from 0 :to 1}}}
to kick the transition animation off. The machine will be in the:prepare
state and emits an effect to wait 0ms before emitting a:slideshow/begin
action. This delay is used to render the target slides to the DOM with their initial CSS state.After 0ms
{:type :transition/begin :payload nil}
is emitted, this transitions the machine to the:transition-out
state and is intended to represent the duration of time the currently displayed slide is fading out. It uses the delay obtained from the:slideshow/transition
, stored in the machine context, to schedule another delayed action that emits:slideshow/transition-out-complete
.The
{:type :slideshow/transition-out-complete :payload nil}
action implies that the current slide is no longer visible as the fade-out animation completed. This event transitions the machine to the:transition-in
state which implies the next slide is fading in over time. It will emit a:slideshow/transition-in-complete
action after another delay from the context:transition-delay
value.When
{:type :slideshow/transition-in-complete :payload nil}
is emitted, it means the next image in the slide show has faded in and is now the current slide. The machine then updates back to the:idle
state ready for the next transition. This time no effect is emitted.Another feature is expected to listen for
:slideshow/transition-in-complete
to update the app state to update the currently selected slide index. If the:transition-in-complete
does not have enough data, the other machine can subscribe to the general:fsm/transition
action, test against the name of the state machine, then grab the target index from the previous's state's context.With this architecture, the feature that controls the cycling through slides, and the timing of transitions can be created additively without this feature needing to know about the others as long as a feature emits the
{:type :slideshow/transition {:payload {...} }
event, it should transition.With the current setup of the statemachine defined in
states
it will ignore any incoming transitions while a transition is in progress.