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 hidden or 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}) |
Author
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
statesdata 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:preparestate and emits an effect to wait 0ms before emitting a:slideshow/beginaction. 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-outstate 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-instate which implies the next slide is fading in over time. It will emit a:slideshow/transition-in-completeaction after another delay from the context:transition-delayvalue.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:idlestate ready for the next transition. This time no effect is emitted.Another feature is expected to listen for
:slideshow/transition-in-completeto update the app state to update the currently selected slide index. If the:transition-in-completedoes not have enough data, the other machine can subscribe to the general:fsm/transitionaction, 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
statesit will ignore any incoming transitions while a transition is in progress.