Skip to content

Instantly share code, notes, and snippets.

@loganlinn
Created October 30, 2014 18:16
Show Gist options
  • Save loganlinn/0c9108079ce87ec23c13 to your computer and use it in GitHub Desktop.
Save loganlinn/0c9108079ce87ec23c13 to your computer and use it in GitHub Desktop.
minimal time-travel
(ns qcon.core
(:require
[qcon.state :as state]
[qcon.time-travel :as time-travel]
[om.core :as om :include-macros true]
[om-tools.dom :as dom :include-macros true]
[om-tools.core :refer-macros [defcomponent]]))
(defcomponent compose-view
[pending-item owner {:keys [on-submit]}]
(render [_]
(dom/form
{:on-submit (comp (constantly false) on-submit)}
(dom/input
{:type :text
:value (:text pending-item "")
:placeholder "Add an item"
:on-change #(om/update! pending-item :text (.. % -target -value))})
(dom/input {:type "submit" :value "Add"}))))
(defcomponent items-view [items owner]
(render [_]
(dom/ol
(for [item items]
(dom/li (:text item))))))
(defcomponent app-view [app owner]
(render [_]
(dom/div
(om/build compose-view (:pending-item app)
{:opts
{:on-submit
#(om/transact! app nil state/submit-pending time-travel/undoable-txn)}})
(dom/button {:on-click time-travel/undo!
:disabled (not (time-travel/can-undo?))}
"Undo")
(dom/button {:on-click time-travel/redo!
:disabled (not (time-travel/can-redo?))}
"Redo")
(om/build items-view (:items app)))))
(defn main []
(om/root app-view state/app-state
{:target (. js/document (getElementById "app"))
:tx-listen time-travel/handle-transaction}))
(ns qcon.state)
(defn init-state []
{:items []
:pending-item {}})
(defonce app-state (atom (init-state)))
(defn submit-pending
"Returns state after moving pending item to items"
[state]
(let [pending-item (:pending-item state)]
(-> state
(update-in [:items] conj pending-item)
(update-in [:pending-item] empty))))
(ns qcon.time-travel
(:require
[qcon.state :as state]))
(defonce app-history (atom [@state/app-state]))
(defonce app-future (atom []))
(def undoable-txn ::undoable)
(defn can-undo? [] (> (count @app-history) 1))
(defn can-redo? [] (> (count @app-future) 0))
(defn push-history! [new-state]
(when (not= (last @app-history) new-state)
(swap! app-history conj new-state)))
(defn undo! []
(when (can-undo?)
(swap! app-future conj (last @app-history))
(swap! app-history pop)
(reset! state/app-state (last @app-history))))
(defn redo! []
(when (can-redo?)
(reset! state/app-state (last @app-future))
(push-history! (last @app-future))
(swap! app-future pop)))
(defn handle-transaction
[{:keys [tag new-state]}]
(when (= tag undoable-txn)
(reset! app-future [])
(push-history! new-state)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment